How do I get the Mac "command" symbol in a Tkinter menu - python

I've written a Python program for a friend's business, and am using Tkinter for the interface. Up until now, all features have been added in the main program window, but I'm now adding a print feature, have created a simple "File" menu, and want to add a "Print" entry to that menu, including displaying the relevant keyboard shortcut.
On my Mac, I want the shortcut to be Command-P. I found the Mac "command" symbol's Unicode value, and have tried various ways to create an accelerator string that simply concatenates that symbol and the letter "P", but nothing works. I get either the symbol or the letter to display in the menu next to "Print", but never both.
Here is the full line of code that adds the menu item, with the latest attempt at building the string (I believe I found this unicode.join option elsewhere in Stack Overflow):
sub_menu.add_command(label="Print", command=self.print_, accelerator=unicode.join(u"\u2318", u"P"))
// Only the "P" displays
Here are some of the other options that I've tried (lines truncated for clarity). With each of these options, only the "command" symbol appears:
accelerator=u"\u2318\u0050"
accelerator=u"\u2318" + "P"
accelerator=u"\u2318" + u"P"
accelerator=u"\u2318P"
accelerator=u"".join([u"\u2318", u"P"])
Up until now I haven't had a need to learn much about Unicode strings, so perhaps there's something I'm doing wrong in that regard. However, all of the attempts that I've made have come as a result of various searches, both here and elsewhere, and so far nothing has worked. Any insight into how to make this work would be most welcome!
Python 2.7.3, Mac OS X 10.8.3

After further searching online, I finally found a page that has the solution. I was surprised (and a touch annoyed) that this solution was different than the one outlined in the PDF documentation for Tkinter 8.4 that I've been referencing thus far (the one published by New Mexico Tech). I found links to Tkinter 8.5 documentation, but they also list the incorrect process.
Anyway, it's a lot simpler than I thought, and is similar to the syntax used for key binding, but slightly different. Instead of directly including the command symbol in the accelerator string, Tkinter takes the literal word "Command" (or the abbreviated "Cmd"), and internally converts it to the displayable character "⌘" in the menu. So my resulting line is:
sub_menu.add_command(label="Print", command=self.print_, accelerator="Command-P")
...and what I get in my full menu item is:
Print ⌘P
As the page linked above shows, similar shortcut words exist for other modifier keys, and on Mac OS X, these are all automatically translated into their graphical equivalents.

It was actually really easy, all I did was use string formatting to concatenate text="%s%s" % (u"\u2318","P")
Here is a sample Tkinter App, the display is small but it shows what you need.
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.label = tk.Label(text="%s%s" % (u"\u2318","P"))
self.label.pack(padx=10, pady=10)
app = SampleApp()
app.mainloop()
Output:
⌘P

Related

The Ctrl+Shift+a not working inTkinter bind_all

I am trying to recreate the notepad. I have added a lot of shortcut keys with a combination of two keys. I am trying to make a three combination shortcut, which will be Ctrl+Shift+s. But when I used <Control-Shift-Key-s> it's not working. I had even tried app.bind<Control-Shift-KeyPress-s> which I found at the Control+Shift+Tab key binding in stack overflow. When I used Tab instead of s that worked, when I use s nothing happens. I want to create a key binding of Control+Shift+s. How Can I Do That?This Is My Code:
from tkinter import *
app = Tk()
def SaveAs(event):
#Some code to save as new file.
print('Pressed Ctrl+Shift+s.')
app.bind_all('<Control-Shift-Key-s>', SaveAs)
Make sure that you are not mixing up uppercase and lowercase, because in Tkinter, "<Control-S>" means CTRL-SHIFT-S and "<Control-s>" means CTRL-S.
So, this line:
app.bind_all('<Control-Shift-Key-s>', SaveAs)
must be
app.bind_all('<Control-S>', SaveAs)

How to specify input language for Entry or any other input field?

In short, i try to type letters (in input components like "Entry", "Text") that are allowed by Windows language-keyboard (i'm using "Latvan(QWERTY)" keyboard) and i can't write long letters like 'ā', 'č', 'ģ' and others.
For example, when i try to write 'ā', the result is 'â'.
The interesting part - when i focus on specific GUI input fiend and change Windows keyboard-language (with "Alt+Shift" shortcut or manually) twice (for example, from "Latvan(QWERTY)" to "Russian" and back to "Latvan(QWERTY)") - then i can write all letters i needed.
What i want is to set all input fields keyboard-language so i could write all letters i want without doing things mentioned above every time i launch my GUI program.
If you need more info or there is already place where this question is answered, please leave a comment and i will act accordingly.
Edit 1:
I am using PyCharm to write my Python Tkinter code. I tried to assign necessary keyboard to my program's generated GUI form according to this guide but it didn't work (i guess that because i used it on temporary created GUI forms).
I was able to solve my problem by using pynput.
Here is simplified version of my final code:
from tkinter import *
from pynput.keyboard import Key, Controller
def change_keyboard_lang(event):
keyboard = Controller()
for i in range(2):
keyboard.press(Key.alt)
keyboard.press(Key.shift_l)
keyboard.release(Key.shift_l)
keyboard.release(Key.alt)
root = Tk()
word_input = Entry(root)
word_input.focus_set()
word_input.bind("<FocusIn>", change_keyboard_lang)
word_input.pack()
root.mainloop()
In short, if cursor is focused on Entry field "word_input", system calls function "change_keyboard_lang" that changes input language from original to other and back to original - and now i can write necessary letters.
It's not the best solution since i need to bind event to every input field in my GUI form but it gets the job done. If you have a better solution please post it here.

How do I reset the image on a button in tkinter?

I've searched around on the Internet and couldn't seem to find a way to remove the image I placed on a button. I was wondering if there is a way to remove the image but keep the button or any other simple quick fix. Here's some of my code for reference.
def breakcup():
if firstroom.cupnotbroken:
messagebutton.config(text="You broke the cup, and the key was inside the cup.")
cup.config(image=photo4)
firstroom.cupnotbroken=False
else:
cup.config(image=None, state=DISABLED)
messagebutton.config(text="You picked up the key")
firstroom.keynotfound=False
Obviously, image=None does not work, but it was the closest thing I could find as a solution.
root = Toplevel(bob)
root.geometry("640x360+200+250")
root.resizable(0, 0)
app = Room1(root)
The windows are made using the Toplevel(parent) function. Just to clarify.
This appears to be a bug with Tkinter. From my experimentation it seems safe to set the image to an empty string rather than None:
messagebutton.configure(image="")
This works because in the underlying tcl/tk interpreter "everything is a string". That is, the tcl equivalent to None is "". When you specify an empty string, Tkinter passes that empty string to tcl, and tcl interprets it as "no image".

Tkinter international bind

Is there a way in Tkinter to bind a combination of keys that will work in all keyboard layouts? (bind by scancode)
For example, I need 'Control-Z' binding that will work with the same physical key in the lower left corner of the keyboard in all layouts, such as:
* Russian layout,
* Greek layout, etc.
Here's what I tried:
from Tkinter import *
root=Tk()
def f(event):
print 'undo'
button1=Button(root, text=u'Button')
button1.pack()
button1.bind('<Control-z>', f)
root.mainloop()
It doesn't work for Russian and Greek keyboard layouts.
Update-2:
I did some more experiments with Windows and now the general rule is like that:
1) If the language is based on latin character set, keys are mapped "by value" (German, French, Dvorak) so that the same action is mapped to different physical keys.
2) If it is not (eg Russian, Greek), then all major accelerators are mapped "by position" (to match the corresponding English letter usually marked on the same key).
Only the second case needs special attention. Any ideas if this is implemented in some lib already?
Update-3
It is simply reproduced even without Russian keyboard or Russian Windows.
1) Start->Control Panel->Regional and Language Options
2) Language->Details
3) Add Russian language.
That's it. Now Alt-Shift will switch you to Russian and you'll be able to type the following funny symbols:
another Alt-Shift will switch you back.
Forget what Wikipedia says about phonetic Russian layouts. They are not used these days. At least inside Russia.
All Windows applications (including wxPython ones) use Ctrl-я for undo, Ctrl-ч for cut, Ctrl-с for copy and so on.
Thanks to #acw1668 for help!
You need to do something like this to use hotkeys with any language layout (the callback from this example is running when Control key is pressed, and prints the key that is pressed in the same time with Control:
from tkinter import *
def callback(event):
if (event.state & 4 > 0):
print("Ctrl-%s pressed" % chr(event.keycode))
root = Tk()
root.bind("<Key>", callback)
root.mainloop()
PS: This example was checked on Windows 10 when English, Ukrainian, Russian, Arabic, Amharic, Armenian, Greek, Georgian, French, Chinese, Japanese and other language layouts were used.
What I'm primarily interested in is Russian layout in Windows.
The quick and dirty workaround I currently use is:
import Tkinter
def copy(event):
print 'Ctrl-C'
root = Tkinter.Tk()
root.bind('<Control-ntilde>', copy)
root.mainloop()
which could potentially lead to a conflict with <Ctrl + actual ntilde> in some other language.
It could be overcome if I could determine which layout is currently active, thus second question: Tkinter determine keyboard layout.
Another drawback is that due to 'universal' treatment of modifier keys it also fires when I press Ctrl-Alt-V, but that's another story as it applies to English layout as well.
I have a partial and rather ugly solution for this. In the code below I have a window with Text widget, which have some "in-box" connection between standard Ctrl+C keyboard events and their proper handling. However, if I simply change the keyboard layout to, say, Russian, these functions do not work anymore. To solve the problem I re-wrote implementation for these events, and now everything works fine. But I feel slightly frustrated about such a solution. Does anyone have any better ideas?.. For instance, is there a way to trigger (or mimic) "normal" key press in python tkinter?
import tkinter
root = tkinter.Tk()
class MyWind (tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
self.create_UI()
def create_UI(self):
text_field = tkinter.Text(self)
text_field.insert(tkinter.END, "Hello world")
text_field.pack()
def print_event(event):
print ("Event character code <char>: '%s'" % event.char)
print (" Event key symbol <keysym>: '%s'" % event.keysym)
print (" Event key code <keycode>: '%s'" % event.keycode)
def work_out_event(event): # Here is the solution
widget_type = type(event.widget)
if widget_type == tkinter.Text:
content = event.widget.selection_get()
print ("Selected content = '%s'" % content)
root.clipboard_clear()
root.clipboard_append(content)
def lurker1(event):
print ("Crtl + C (english) pressed!")
print_event(event)
def lurker2(event):
print ("Crtl + C (russian) pressed!")
print_event(event)
work_out_event(event)
root.bind("<Control-c>", lurker1) # "C" character with the english keyboard layout
root.bind("<Control-ntilde>", lurker2) # "C" character with the russian keyboard layout
root.app = MyWind(root)
root.app.pack()
root.mainloop()
Another option already suggested in the old 1999 is to switch from Tkinter to wxPython where accelerators handling is done for all types of keyboard layouts automatically (eg Editor example here: http://wiki.wxpython.org/AnotherTutorial).
def copy(event):
print 'Ctrl-C'
master.clipboard_append('text')
and it works!

Hyperlink in Tkinter Text widget?

I am re designing a portion of my current software project, and want to use hyperlinks instead of Buttons. I really didn't want to use a Text widget, but that is all I could find when I googled the subject. Anyway, I found an example of this, but keep getting this error:
TclError: bitmap "blue" not defined
When I add this line of code (using the IDLE)
hyperlink = tkHyperlinkManager.HyperlinkManager(text)
The code for the module is located here and the code for the script is located here
Anyone have any ideas?
The part that is giving problems says foreground="blue", which is known as a color in Tkinter, isn't it?
If you don't want to use a text widget, you don't need to. An alternative is to use a label and bind mouse clicks to it. Even though it's a label it still responds to events.
For example:
import tkinter as tk
class App:
def __init__(self, root):
self.root = root
for text in ("link1", "link2", "link3"):
link = tk.Label(text=text, foreground="#0000ff")
link.bind("<1>", lambda event, text=text: self.click_link(event, text))
link.pack()
def click_link(self, event, text):
print("You clicked '%s'" % text)
root = tk.Tk()
app = App(root)
root.mainloop()
If you want, you can get fancy and add additional bindings for <Enter> and <Leave> events so you can alter the look when the user hovers. And, of course, you can change the font so that the text is underlined if you so choose.
Tk is a wonderful toolkit that gives you the building blocks to do just about whatever you want. You just need to look at the widgets not as a set of pre-made walls and doors but more like a pile of lumbar, bricks and mortar.
"blue" should indeed be acceptable (since you're on Windows, Tkinter should use its built-in color names table -- it might be a system misconfiguration on X11, but not on Windows); therefore, this is a puzzling problem (maybe a Tkinter misconfig...?). What happen if you use foreground="#00F" instead, for example? This doesn't explain the problem but might let you work around it, at least...

Categories

Resources