simple key pressed event in python tkinter app - python

I'm new to python and Tkinter. I'm looking for a small GUI app which contains a textbox on it and if any change occurs in the textbox content then it fires up an event for me to do something. I tried but failed to write such an event.
Any help will be appreciated.

You can use bind the <Key> event to a callback like this:
import Tkinter as tk
class MyApp(object):
def __init__(self, master):
self.text = tk.Text(master)
self.text.bind('<Key>', self.callback)
self.text.pack()
self.text.focus()
def callback(self, event):
print('{k!r}'.format(k = event.char))
root = tk.Tk()
app = MyApp(root)
root.mainloop()
(Like Steven Rumbalski, I'm not quite sure what you intended by 'textbox'. Happily, the above code will still work if you change tk.Text to tk.Entry.)

Related

Is there some way I can pass information to a tkinter application after calling .mainloop()?

This is a very simple version of my application. Basically I have a Tkinter application I want to start from another python application, but I do not have the option to have tkinter as my main window.
So my question is, if it's possible to pass some information from my main class to my tkinter application. Not once, but repeatedly.
I tried something like this, but this does not work, probably because of the .mainloop() method.
Right now I just try to print the variable "labelContent" whenever the button is pressed.
Is there some way I can pass information to my tkinter application after calling .mainloop() (besides using external files like .txt because this would obviously work)?
gui.py
import tkinter
class BasicWindow:
def __init__(self):
tk = tkinter.Tk()
label = tkinter.Label(tk, text="Hello World!")
label.pack()
self.labelContent = ""
button = tkinter.Button(tk,text="OK",command=self.buttonMethod)
button.pack(side="bottom")
tk.mainloop()
def buttonMethod(self):
print(self.labelContent)
main.py
from gui import BasicWindow
import time
class Main:
def __init__(self):
self.gui = BasicWindow()
self.gui.labelContent = "This is the new label content"
mainApp = Main()
while True:
mainApp.gui.labelContent = time.time()
Thanks in advance! :)

Tkinter Threading Error: RuntimeError: threads can only be started once

I have created a tkinter GUI in the following structure:
import tkinter as tk
import threading
class App:
def __init__(self, master):
self.display_button_entry(master)
def setup_window(self, master):
self.f = tk.Frame(master, height=480, width=640, padx=10, pady=12)
self.f.pack_propagate(0)
def display_button_entry(self, master):
self.setup_window(master)
v = tk.StringVar()
self.e = tk.Entry(self.f, textvariable=v)
buttonA = tk.Button(self.f, text="Cancel", command=self.cancelbutton)
buttonB = tk.Button(self.f, text="OK", command=threading.Thread(target=self.okbutton).start)
self.e.pack()
buttonA.pack()
buttonB.pack()
self.f.pack()
def cancelbutton(self):
print(self.e.get())
self.f.destroy()
def okbutton(self):
print(self.e.get())
def main():
root = tk.Tk()
root.title('ButtonEntryCombo')
root.resizable(width=tk.NO, height=tk.NO)
app = App(root)
root.mainloop()
main()
I want to prevent the GUI from freezing when running a function (in the example code it's the function of the ok-button). For that I found the solution of using the thread-module as best practice. But the problem is that when I want to run the code once again, python returns this traceback:
RuntimeError: threads can only be started once
I'm totally aware of the problem that threads can be only starten once as stated in the error message. My question is: How can I stop a thread to start it a second time or does anybody has a better workaround for preventing the GUI from freezing and pressing a button/running a function multiple times?
BR and thank you
Lorenz
Your code will only create one thread and assign its start function reference to command option. Therefore same start() function will be called whenever the button is clicked.
You can use lambda instead:
command=lambda: threading.Thread(target=self.okbutton).start()
Then whenever the button is clicked, a new thread will be created and started.

How to do loading screen in tkinter? [duplicate]

I have a main tkinter window that can take up to a few seconds to load properly. Because of this, I wish to have a splash screen that shows until the init method of the main class has finished, and the main tkinter application can be shown. How can this be achieved?
Splash screen code:
from Tkinter import *
from PIL import Image, ImageTk
import ttk
class DemoSplashScreen:
def __init__(self, parent):
self.parent = parent
self.aturSplash()
self.aturWindow()
def aturSplash(self):
self.gambar = Image.open('../output5.png')
self.imgSplash = ImageTk.PhotoImage(self.gambar)
def aturWindow(self):
lebar, tinggi = self.gambar.size
setengahLebar = (self.parent.winfo_screenwidth()-lebar)//2
setengahTinggi = (self.parent.winfo_screenheight()-tinggi)//2
self.parent.geometry("%ix%i+%i+%i" %(lebar, tinggi, setengahLebar,setengahTinggi))
Label(self.parent, image=self.imgSplash).pack()
if __name__ == '__main__':
root = Tk()
root.overrideredirect(True)
progressbar = ttk.Progressbar(orient=HORIZONTAL, length=10000, mode='determinate')
progressbar.pack(side="bottom")
app = DemoSplashScreen(root)
progressbar.start()
root.after(6010, root.destroy)
root.mainloop()
Main tkinter window minimum working example:
import tkinter as tk
root = tk.Tk()
class Controller(tk.Frame):
def __init__(self, parent):
'''Initialises basic variables and GUI elements.'''
frame = tk.Frame.__init__(self, parent,relief=tk.GROOVE,width=100,height=100,bd=1)
control = Controller(root)
control.pack()
root.mainloop()
EDIT: I can use the main window until it has finished loading using the .withdraw() and .deiconify() methods. However my problem is that I cannot find a way to have the splash screen running in the period between these two method calls.
a simple example for python3:
#!python3
import tkinter as tk
import time
class Splash(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.title("Splash")
## required to make window show before the program gets to the mainloop
self.update()
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.withdraw()
splash = Splash(self)
## setup stuff goes here
self.title("Main Window")
## simulate a delay while loading
time.sleep(6)
## finished loading so destroy splash
splash.destroy()
## show window again
self.deiconify()
if __name__ == "__main__":
app = App()
app.mainloop()
one of the reasons things like this are difficult in tkinter is that windows are only updated when the program isn't running particular functions and so reaches the mainloop. for simple things like this you can use the update or update_idletasks commands to make it show/update, however if the delay is too long then on windows the window can become "unresponsive"
one way around this is to put multiple update or update_idletasks command throughout your loading routine, or alternatively use threading.
however if you use threading i would suggest that instead of putting the splash into its own thread (probably easier to implement) you would be better served putting the loading tasks into its own thread, keeping worker threads and GUI threads separate, as this tends to give a smoother user experience.

Python Tkinter hide and show window via hotkeys

I'm trying to write a program that I can hide and show via hotkeys. I managed to get the application to show and hide using the library "keyboard", however due to the "wait" function of the library, it prevents the Text box from functioning correctly. I have tried using the key bindings within Tkinter, however I had a different problem, whereby once the program was hidden or another application was selected, I couldn't return the focus to the hidden window via the hotkey.
import Tkinter as Tk
import keyboard
class MyApp(object):
def __init__(self, parent):
self.root = parent
self.root.title("Main frame")
self.frame = Tk.Frame(parent)
self.frame.pack()
self.editor = Tk.Text(self.frame)
self.editor.pack()
self.editor.config(font="Courier 12")
self.editor.focus_set()
keyboard.add_hotkey('ctrl+alt+s', self.show)
keyboard.add_hotkey('ctrl+alt+h', self.hide)
keyboard.wait()
self.root.withdraw()
def show(self):
self.root.update()
self.root.deiconify()
def hide(self):
self.root.withdraw()
if __name__ == "__main__":
root = Tk.Tk()
root.geometry("800x600")
app = MyApp(root)
root.mainloop()
Any assistance would be great :)
Just drop this wait command, its an additional mainloop, which is not needed as Tkinter does its job. I tried to fix your problem with threading, but as I wanted to check exactly what is NOT working, I accidentially made what I suppose you wanted to. So the Code is:
import tkinter as tk
import keyboard
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("800x600")
self.title("Main frame")
self.editor = Tk.Text(self)
self.editor.pack()
self.editor.config(font="Courier 12")
self.editor.focus_set()
keyboard.add_hotkey('ctrl+alt+s', self.show)
keyboard.add_hotkey('ctrl+alt+h', self.hide)
def show(self):
self.update()
self.deiconify()
def hide(self):
self.update()
self.withdraw()
if __name__ == "__main__":
App().mainloop()
I hope this works for you. I'd also recommend changing this key settings. Testing with those in PyZo is IMPOSSIBLE! It always tries to "save as...", which I don't want to...

Python Tkinter menu bars don't display

I'm trying to make a GUI using Tkinter and have come to implementing a menu bar. I've looked at a few tutorials and written some code for it, but a menu bar never seems to appear - just a blank frame with a white background. This doesn't just happen for my code though; on copying and pasting the code of one of the aforementioned tutorials into a new script, the same behaviour is exhibited.
I'd appreciate it if anyone could shed any light on what's causing this. My system is OS X 10.5, Python 2.7, Tk 8.4. Here's the code from the tutorial that doesn't appear to work:
#!/usr/local/bin/python2.7
from Tkinter import *
from ttk import *
class App(Frame):
def __init__(self):
Frame.__init__(self)
self.master.geometry('400x300')
self.master.title(__file__)
self.pack()
self.menu = Menu(tearoff=False)
self.master.config(menu = self.menu)
fm = self.file_menu = None
fm = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label='File', menu = fm)
fm.add_command(label='Say Hello', command = self.say_hello)
fm.add_separator()
fm.add_command(label='Quit', command = self.quit)
self.mainloop()
def say_hello(self, *e):
self.label = Label(self.master, text='Hello there!')
self.label.pack(anchor=CENTER, fill=NONE, expand=YES, side=LEFT)
if __name__ == '__main__':
App()
and my code is here:
from Tkinter import *
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
parent.title("Cluedo Solver 1.0")
menubar = Menu(root)
menubar.add_command(label="File")
menubar.add_command(label="Quit", command=root.quit())
root.config(menu=menubar)
root=Tk()
root.geometry("300x250+300+300")
app=App(root)
root.mainloop()
Based on some comments you made to one of the answers, you are apparently running this on a Macintosh. The code works fine, but the menu appears in the mac menubar rather than on the window like it does on Windows and Linux. So, there's nothing wrong with your code as far as the menubar is concerned.
Code with Explanation
From personal experience, I have found that it is usually easier to manage all widgets in a widgets method. That is what I did here, and it worked. Also, instead of parent, I used master. I will now walk you through the code step-by-step.
from Tkinter import *
We import Tkinter (GUI stuff)
class App(Frame):
We create a class called App, which is the Frame where widgets are held.
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.widgets()
We create a method called __init__. This initializes the class, and runs another method called widgets.
def widgets(self):
menubar = Menu(root)
menubar.add_command(label="File")
menubar.add_command(label="Quit", command=root.quit())
root.config(menu=menubar)
We create the widgets method. This is where the widget, menubar is added. If we were to create anymore widgets, they would also be here.
root=Tk()
root.title("Menubar")
app=App(root)
root.mainloop()
Lastly, we give the entire window some properties. We give it a title, Menubar, and run the App class. lastly, we start the GUI's mainloop with root.mainloop.
I am trying the code as above, but all I get is "Python" on the macOS menubar and it's usual pulldown. tkinter just doesn't seem to work menus on macOS 10.14.1
I think what is happening is that there are mac specific fixups that are changing the event codes so certain menu items will end up under the Python menu item instead of where expected, I saw some of this in my own experiments. When I expanded my code and used some of the reserved FILE event codes instead of the standard ones, things worked better.
#!/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7
# -*- coding: utf-8 -*-# -*- coding: utf-8 -*-
from tkinter import *
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.widgets()
def widgets(self):
menubar = Menu(root)
menubar.add_command(label = 'File')
menubar.add_command(label = 'quit', command = root.quit())
root.config(menu = menubar)
root = Tk()
root.title('Menubar')
app = App(root)
root.mainloop()
Check your mac menu bar if you are doing any GUI that involves menubar which you will like to view or test. Its subtle and you may think you code is not to working. Click on the app(in this case python window), it will show a drop down menubar.
guys to solve the problem
look the file part that's where the menubar for mac is located

Categories