Raspberry Pi escape character - shell

I am trying to create a shell script in which I automatically run a video fullscreen.
It has no way to quit unless I shut down the Raspberry Pi.
What is a small script I can use to bind something like "!" to quit the application?

I searched Google for 'omxplayer exit fullscreen' and found this answer, originally posted by dom on the RaspberryPi forum:
Changing TV modes does lose any content on them (e.g. the console
framebuffer).
You can provoke the console framebuffer to be recreated with: fbset
-depth 8 && fbset -depth 16
Add that to the end of a script that launches omxplayer.
(for extra points read the depth before launching omxplayer and set it
back to original value afterwards)
You might also want to check this issue report on omxplayer's GitHub.

I am not sure if this will work but potentially you could use a tkinter window that you make non visable.
#import the tkinter module for the GUI and input control
try:
# for Python2
import Tkinter as tk
from Tkinter import *
except ImportError:
# for Python3
import tkinter as tk
from tkinter import *
def key(event):
#create a function to control closing the window in this case
if event.keysym == 'Escape':
#this currently closes the window however you could add to root.destroy() with
#the relevant command for closing the video.
root.destroy()
#initiate root window, remove it from view, bind all keys (you could just
#bind '<Escape>' if preffered
root = Tk.tk
root.withdraW()
root.bind_all('<Key>', key)
i am aware this isnt a specific design to your problem however it will let you bind the escape key like you wished. this stopped the whole application in my example however you may have to include extra lines to ensure each part of your application is correctly ended.

Related

Importing an image into Tkinter [duplicate]

Consider below example:
import tkinter as tk
root = tk.Tk()
root.title("root")
other_window = tk.Tk()
other_window.title("other_window")
root.mainloop()
and also see below example that creates instances of Tk back-to-back instead of at once, so there's exactly one instance of Tk at any given time:
import tkinter as tk
def create_window(window_to_be_closed=None):
if window_to_be_closed:
window_to_be_closed.destroy()
window = tk.Tk()
tk.Button(window, text="Quit", command=lambda arg=window : create_window(arg)).pack()
window.mainloop()
create_window()
Why is it considered bad to have multiple instances of Tk?
Is the second snippet considered a bit better, or does it suffer from
the same conditions the first code does?
Why is it considered bad to have multiple instances of Tk?
Tkinter is just a python wrapper around an embedded Tcl interpreter that imports the Tk library. When you create a root window, you create an instance of a Tcl interpreter.
Each Tcl interpreter is an isolated sandbox. An object in one sandbox cannot interact with objects in another. The most common manifestation of that is that a StringVar created in one interpreter is not visible in another. The same goes for widgets -- you can't create widgets in one interpreter that has as a parent widget in another interpreter. Images are a third case: images created in one cannot be used in another.
From a technical standpoint, there's no reason why you can't have two instances of Tk at the same time. The recommendation against it is because there's rarely an actual need to have two or more distinct Tcl interpreters, and it causes problems that are hard for beginners to grasp.
Is the second snippet considered a bit better, or does it suffer from the same conditions the first code does?
It's impossible to say whether the second example in the question is better or not without knowing what you're trying to achieve. It probably is not any better since, again, there's rarely ever a time when you actually need two instances.
The best solution 99.9% of the time is to create exactly one instance of Tk that you use for the life of your program. If you need a second or subsequent window, create instances of Toplevel. Quite simply, that is how tkinter and the underlying Tcl/Tk interpreter was designed to be used.
I disagree with the tkinter community discouraging the use of multiple tk.Tk windows. You can have multiple tk.Tk windows. Using multiple instances of tk.Tk is the only way to create windows that are truly independent of each other. The only mistake most people make when creating multiple tk.Tk windows is that they forget to pass in master=... when creating PhotoImages/StringVars/IntVars/...
For example look at this code:
import tkinter as tk
root = tk.Tk()
root2 = tk.Tk()
variable = tk.StringVar() # Add `master=root2` to fix the problem
entry = tk.Entry(root2, textvariable=variable)
entry.bind("<Return>", lambda e: print(repr(variable.get())))
entry.pack()
root.mainloop()
The code above doesn't work. If you add master=root2 to the tk.StringVar(), then it will work perfectly fine. This is because tkinter stores the first instance of tk.Tk() in tk._default_root. Then if you don't pass in master=..., it will assume that you wanted the window in tk._default_root.
Another thing people get wrong is how many times should .mainloop() be called. It handles events from all tk.Tk windows that are alive so you only need one .mainloop().
For folks who disagree, I'd be interested in an example of where an actual problem is caused by the multiple tk.Tk windows.
The best reference I've found so far is the Application Windows section of the tkinterbook:
In the simple examples we’ve used this far, there’s only one window on the screen; the root window. This is automatically created when you call the Tk constructor
and
If you need to create additional windows, you can use the Toplevel widget. It simply creates a new window on the screen, a window that looks and behaves pretty much like the original root window
My take on it is that a Tk instance creates a Toplevel widget, plus things like the mainloop, of which there should be only one.
Tk() initializes the hidden tcl interpreter so that the code can run, as Tkinter is just a wrapper around tcl/tk. It also automatically creates a new window. Toplevel() just creates a new window, and wont work if Tk() hasn't been instantiated, as it requires the tcl interpreter that Tk() initializes. You cannot create any Tkinter widgets without instantiating Tk(), and Toplevel is merely a widget. In the question, you use Tk() to create a second window. You should instead create another file, because initializing the tcl interpreter multiple times can get confusing, as #Bryan Oakley explains so well. Then you should do:
from os import startfile
startfile(nameOfTheOtherFile)
, because, as Toplevel() is just a widget, it closes when the Tk() window is closed. Having the other window in a separate file makes it less confusing.

win32api module does not give the current language code when used in a while loop

I am trying to get the current language code using python and win32api module. I am on Windows 10 (64-bit) and my python version is 3.5.2. My problem is when I simply call win32api.GetKeyboardLayoutName() it gives me the current language code correctly. However, when I put the function in a while loop and change the keyboard layout while the script is running, I get the same language code. In other words it only shows the language code prior to running the script and does not update as I change the keyboard layout in real time. Any help on this is appreciated.
You can find a MWE here:
import win32api
import time
msg = 'The current language code is: %s'
while True:
current_language_code = win32api.GetKeyboardLayoutName()
print(msg % current_language_code)
time.sleep(0.1)
UPDATE:
I just figured out that win32api works in a per-process manner, i.e. when I run my script win32api.GetKeyboardLayoutName() gives me the keyboard layout for the current procress which does not change (unless I explicitly call win32api.LoadKeyboardLayout() myself in the script). That explains why the output of the while loop does not update (change) as I change the global keyboard layout (accessible in the tray bar).
So is there anyway to get the global keyboard layout and detect its changes in python via win32api?

Tkinter askopenfilename() won't close if destroy followed by an input

I can't get a Tkinter file dialog box to close. This Tkinter file dialog box closes just fine if there is no input statement downstream. But with it, it does not close, and crashes the app.
Through a process of elimination, it seems input() downstream from the root.destroy() command is interfering with the window closing.
Anyone have a fix?
Here's the code:
import tkinter
from tkinter import filedialog
###### THIS CODE WORKS #########
# Make a top-level instance and hide since it is ugly and big.
root = tkinter.Tk()
root.withdraw()
# Make it almost invisible - no decorations, 0 size, top left corner.
root.overrideredirect(True)
root.geometry('0x0+0+0')
# Show window again and lift it to top so it can get focus,
# otherwise dialogs will end up behind the terminal.
root.deiconify()
root.lift()
root.focus_force()
# get the file path of the trades to load.
new_file_path = filedialog.askopenfilename()
# Get rid of the top-level instance once to make it actually invisible.
root.destroy()
###### WHEN INPUT IS ADDED IT DOESNT WORK######
input('Testing testing testing ')
I've tried adding a time.sleep(1) just after root.destroy.
I've tried the solution here:
Tkinter askopenfilename() won't close
I'm using OSX 10.11.4 with Pycharm.

Is there a way to remove the background window that pops up when calling askopenfilename()?

When I call the basic command:
from tkinter.filedialog import askopenfilename
filename = askopenfilename()
A little window pops up behind the selector screen.
Is there a way to remove this?
It pops up because you need at least a "root" window for each tkinter application. In your case, you aren't creating any root window explicitly, so askopenfilename creates it automatically for you. One solution would be to create the root window explicitly and then hide it, something as follows
from tkinter.filedialog import askopenfilename
from tkinter import Tk
Tk().withdraw()
filename = askopenfilename()

Programmatically setting the Mac menubar title

How do I change the application title in the Mac menu bar programmatically (after starting the application)?
I know that I can set it before running the program via CFBundleName in the Info.plist file. However, I need to change it after initializing the application. Manipulating my own Info.plist also won't work.
There is no official API to do so, but in this discussion someone hinted that there is a secret function call that changes the bundle name / menubar title:
"There is SPI you can use if you look hard enough around for it, but
I'm not going to point anybody to it unless they can justify why they
need to use it :-)"
I think Java uses something similar, too. So what's the secret API?
OK, I've found a way. This is the code in Python (taken from here). It could use some cleaning and some error handling, but it works. The key is the undocumented CPSSetProcessName from ApplicationServices.
def _osx_set_process_name(app_title):
""" Change OSX application title """
from ctypes import cdll, c_int, pointer, Structure
from ctypes.util import find_library
app_services = cdll.LoadLibrary(find_library("ApplicationServices"))
if app_services.CGMainDisplayID() == 0:
print "cannot run without OS X window manager"
else:
class ProcessSerialNumber(Structure):
_fields_ = [("highLongOfPSN", c_int),
("lowLongOfPSN", c_int)]
psn = ProcessSerialNumber()
psn_p = pointer(psn)
if ( (app_services.GetCurrentProcess(psn_p) < 0) or
(app_services.SetFrontProcess(psn_p) < 0) ):
print "cannot run without OS X gui process"
print "setting process name"
app_services.CPSSetProcessName(psn_p, app_title)
return False
If you call it right away, it changes the process name in the Activity Monitor. I had to call it after a small timeout for some reason to also change the Menubar name:
import gobject
gobject.timeout_add(100, _osx_set_process_name, "MyTitle")
Note that if you combine this with an .app package you can get a really nice system integration of a python app. While you can change the Icon, the Finder display name, ..., all methods I tried leave the CFBundleName (the one in the Menu bar) to "Python". (This answer for example only changes the long name (displayed over dock icons). There are plenty others that almost work.)
While this answer gives the intended result, it's not very elegant. I'd appreciate some insight into why I need the timeout... I think Python or pygtk changes the process name itself when I first import gtk or run the main loop.

Resources