How to switch frames in tkinter using keyboard events - python

I have been using the following example to create a tkinter GUI which can switch between different frames.
Switch between two frames in tkinter
I would like to add a keyboard short cut to switch between the frames. E.g pressing '1' on the keyboard switches to page one in the example. I'm not sure how to use the existing structure to add in this new feature. I've read the effbot.org documentation on events and bindings but I'm confused how their examples can be applied to this particular case. Any help would be greatly appreciated, thanks.

One can use bind method to attach keyboard events to call methods such as show_frame(given that it selects the pages in a list: "StartPage", "PageOne", "PageTwo":
def on_key_release(event):
key_mapping = {'0':"StartPage", '1':"PageOne", '2':"PageTwo"}
key_released = event.keysym
if key_released in key_mapping:
app.show_frame(key_mapping[key_released])
if __name__ == "__main__":
app = SampleApp()
app.bind('<KeyRelease>', on_key_release)
app.mainloop()

Related

Real Time Data with tkinter

I am looking for a simple way to display changing real time data in a GUI in python. I am connected to 2 devices and want to display data constantly (like 20 different values), and when I press a button I want to control the one device.
Unfortunately I fail already with the display of the data. For this I have looked at some tkinter tutorials and explanations.
My idea was to implement it with a config function and to overwrite the label continuously. As example how I wanted to display one value:
import tkinter as tk
from pydualsense import pydualsense
# connect to the device
dualsense = pydualsense()
dualsense.init()
# create a window
window = tk.Tk()
# function for updating data
def show_data():
global dualsense
data_label_output.config(text=dualsense.state.LX)
# showing the data as a lable
data_label_output = tk.Label(window)
data_label_output.grid(row=1, column=1)
show_data()
#### or different solution
# showing the data as a lable
data_label_output = tk.Label(window, comand=show_data)
data_label_output.grid(row=1, column=1)
window.mainloop()
Unfortunately, the value is displayed only once at the beginning and nothing changes after that.
Another problem:
When I press the button, I want to be able to control the one device. For this I have a while True loop that permanently checks if a button is pressed and then executes actions. As a separate program no problem, but how do I integrate this into the tkinter GUI? When I start this PyCharm always crashes.
I use PyCharm and Python 3.8
About simple and functional ideas I would be happy, also to other tools/modules etc., as long as you can easily and quickly implement the idea. It's only for a research project and the programming is only a means to an end.
You can use the after method in tkinter to run something after a short delay. The following code will run show_data once the GUI is ready and then again every 1000 milliseconds.
import tkinter as tk
from pydualsense import pydualsense
# connect to the device
dualsense = pydualsense()
dualsense.init()
# create a window
window = tk.Tk()
# function for updating data
def show_data():
global dualsense
data_label_output.config(text=dualsense.state.LX)
window.after(1000,show_data)
# showing the data as a lable
data_label_output = tk.Label(window)
data_label_output.grid(row=1, column=1)
window.after_idle(show_data)
window.mainloop()
This resolves the updating issue, I'm not sure what behaviour you want when you press the button but if you elaborate and explain, I might be able to help and update this answer.

Tkinter add Widgets (a Button) to a new second Frame

I created a Frame, gave it a menubar. Works just fine. The purpose of the entry in the menubar is to open a new frame, in which u can change some settings. The creation of the new Window works also. However I can't create widgets on the new created window. I tried it with a Button and got a
TclError: can't invoke "button" command: application has been destroyed
I tried to google it and found Cannot invoke button command: application has been destroyed which didn't quite helped me.
Further I found a solution were u have to create a parent class (which inherrits from Frame) and than create all other Frames within it, but on the first view it looked pretty complicated. Especially because the creation of the second window seems to work in the first place.
I know this is probably a really basic question, so thanks in advance for your time
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
perfFrame.mainloop()
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
# Button(perfFrame, text='Abbrechen', command=perfFrame.destroy, width=37).grid(row=0 ,column=1 )
class perfSettingsValue:
def __init__(self):
self.bvhSteps = 0
def getValues(self):
pass
#Hauptfenster
root = Tk(className="BoneMapping & SkeletonEstimation")
root.configure(bg='#F2F2F2')
root.geometry("1300x600")
myPerfSettingValue = perfSettingsValue()
menubar = Menu(root)
sdmenu = Menu(menubar, tearoff=0)
sdmenu.add_command(label="Performanz", command=perfSettings)
menubar.add_cascade(label='Einstellungen',menu=sdmenu)
root.config(menu=menubar)
The key problem here is that you are trying to add a button after starting the mainloop which effectively blocks the execution of the program. The error you are getting is because the line that adds the button gets executed after the window has been closed.
Your problem will be solved if you modify your function like this:
def perfSettings():
perfFrame = Tk(className=" Performanz Einstellungen")
perfFrame.configure(bg='#F2F2F2')
perfFrame.geometry("300x300")
btn = Button(master=perfFrame, text='Speichern', command=myPerfSettingValue.getValues, width=37)
btn.pack()
perfFrame.mainloop()
This is not the only problem though. Instead of creating a new instance of Tk, you should create a new Toplevel instance, which will, in your case, act just as a Tk instance, but have a lot less tendency to cause trouble.
Finally, you should consider reading on the object oriented approach to designing tkinter applications. There are far too many variants of that to be appropriately elaborated here but I certainly recommend you take the effort to learn to use one of them. It will make your code more comprehensible and maintainable. My usual approach is to create a class that inherits from Toplevel or Tk for every type of window I am going to use.

python tkinter.ttk combobox down event on mouseclick

I do not find a correct answer to my issue, despite intense research and a rather simple problem.
All I would like to do, is my comboboxes drop down when clicked on by 'Button-1'. But regardless of what I code, the combos don't behave as I wish.
following I prepared a simple code to demonstrate my problem:
from tkinter import *
import tkinter.ttk
def combo_events(evt):
if int(evt.type) is 4:
w = evt.widget
w.event_generate('<Down>')
root = Tk()
li = ('row 1', 'row 2', 'row 3')
combo1 = tkinter.ttk.Combobox(root, value=li)
combo2 = tkinter.ttk.Combobox(root, value=li)
combo1.bind('<Button-1>', combo_events)
combo2.bind('<Button-1>', combo_events)
combo1.pack()
combo2.pack()
root.mainloop()
Well, if I try this code, the combos do dropdown, but not as expected. So, I tried to add a bind of the 'FocusIn' event but that rather complicates the situation and inhibits a 'FocusOut' ...
Can any1 help me to achieve my goal?
ps: I know, that the combo will drop down by clicking on the frame of the widget, but to be more precise I would like to drop it, when clicking into it.
And by the way, where do I find a rather complete list of events a combobox can trigger?
thx for effort and answer.
Why are you using int(evt.type) is 4 instead of int(evt.type) == 4 ?
Applying this change it works for me.
Edit 1
First of all, thank you for explaining to us what you really want to have. Did not expect this from your initial question.
If you want to override the editing behaviour it is time to dig a little deeper.
The widget you click into inside the combobox is an entry widget. What you can do now is to define when your event shall be fetched inside the event chain. Will apply code soon.
Edit 2
To get it at the first mouse click:
w.event_generate('<Down>', when='head')
Why? Because default of Event Generate is to append the generated Event to the Event Chain (put it at its end, value = 'tail'). Changing to when='head' gives your desired behaviour.

PyGTK: Allow usage of Tab in AccelGroup

When using gtk.AccelGroup any combination with Tab charater is invalid. Now I do understand that UI navigation is done using this key but in some special cases I need to override that behavior. Is there a way to make AccelGroup allow usage of this key?
For example:
group = gtk.AccelGroup()
group.connect(gtk.gdk.keyval_from_name('Tab'), gtk.gdk.CONTROL_MASK, 0, callback)
You can easily get key names and values with this :
#!/usr/bin/env python
import gtk
import gtk
def catch_button(window, event, label):
keyval = event.keyval
name = gtk.gdk.keyval_name(keyval)
mod = gtk.accelerator_get_label(keyval, event.state)
label.set_markup('<span size="xx-large">%s\n%d</span>'% (mod, keyval))
window = gtk.Window()
window.set_size_request(640,480)
label = gtk.Label()
label.set_use_markup(True)
window.connect('key-press-event',catch_button, label)
window.connect('destroy', gtk.main_quit)
window.add(label)
window.show_all()
gtk.main()
But I found that the keynames returned were locale-dependent, of no great use for me. The keyval can probably be used.
Cheers,
Louis
This below is one way to do it. Although if you don't wish for the program to listen for every keypress as you stated above, I should say that I've never run across a way of tying Tab to an AccelGroup. I've tried various things myself, but to no avail.
widget.connect("key-press-event",self.on_key_pressed)
def on_key_pressed(self,widget,event,*args):
if event.keyval == gtk.keysyms.Tab:
do_something()

Cursor event handling in python+Tkinter

I'm building a code in which I'd like to be able to generate an event when the user changes the focus of the cursor from an Entry widget to anywhere, for example another entry widget, a button...
So far i only came out with the idea to bind to TAB and mouse click, although if i bind the mouse click to the Entry widget i only get mouse events when inside the Entry widget.
How can I accomplish generate events for when a widget loses cursor focus?
Thanks in advance!
The events <FocusIn> and <FocusOut> are what you want. Run the following example and you'll see you get focus in and out bindings whether you click or press tab (or shift-tab) when focus is in one of the entry widgets.
from Tkinter import *
def main():
global text
root=Tk()
l1=Label(root,text="Field 1:")
l2=Label(root,text="Field 2:")
t1=Text(root,height=4,width=40)
e1=Entry(root)
e2=Entry(root)
l1.grid(row=0,column=0,sticky="e")
e1.grid(row=0,column=1,sticky="ew")
l2.grid(row=1,column=0,sticky="e")
e2.grid(row=1,column=1,sticky="ew")
t1.grid(row=2,column=0,columnspan=2,sticky="nw")
root.grid_columnconfigure(1,weight=1)
root.grid_rowconfigure(2,weight=1)
root.bind_class("Entry","<FocusOut>",focusOutHandler)
root.bind_class("Entry","<FocusIn>",focusInHandler)
text = t1
root.mainloop()
def focusInHandler(event):
text.insert("end","FocusIn %s\n" % event.widget)
text.see("end")
def focusOutHandler(event):
text.insert("end","FocusOut %s\n" % event.widget)
text.see("end")
if __name__ == "__main__":
main();
This isn't specific to tkinter, and it's not focus based, but I got an answer to a similar question here:
Detecting Mouse clicks in windows using python
I haven't done any tkinter in quite a while, but there seems to be "FocusIn" and "FocusOut" events. You might be able to bind and track these to solve your issue.
From:
http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm

Categories

Resources