So I've been trying to get a threadsafe adapter for tkinter (I'm using Python 3.3) to help with some of my projects. I stumbled upon a recipe called "safetkinter" (recipe 578153), which promises to route all gui calls to the main thread. I tried it out, but I am having problems with a RuntimeError that keeps sticking its head up and ruining my endeavors. I've tried various ways of fixing it with no luck. So... Does anyone here know the trick to getting this to work? (also, safetkinter makes use of the threadbox recipe, and the affinity recipe by dependence. I also believe that the affinity module is the culprit of this problem.)
The script I was using to test the module:
import safetkinter as tkinter
class Main(tkinter.Tk):
def __init__(self):
self._setup()
self.mainloop()
def _setup(self):
tkinter.Button(self, text='Push me!').pack()
if __name__ == '__main__':
Main()
Also, here is a link to the exception traceback taken from the console:
Traceback
As I see at the example of safetkinter usage, the initialization should be done in another way. Here is a short working code based on your code and safetkinter example (Frame with a Button). You can extend and improve it:
import safetkinter as tkinter
class Main(tkinter.Frame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
#classmethod
def main(cls):
root = cls.create_application_root()
self = cls.setup_class_instance(root)
root.mainloop()
#staticmethod
def create_application_root():
root = tkinter.Tk()
root.minsize(400, 340)
root.title('Title')
return root
#classmethod
def setup_class_instance(cls, root):
instance = cls(root)
instance.button = tkinter.Button(root, text='Push me!').pack()
return instance
if __name__ == '__main__':
Main.main()
Related
I wrote a GUI program with Python tkinter. To achieve some function, in a Toplevel window, some event triggered a method of it which would call the Treeview widget's item(ID, tags=(some_tag)) of the Tk window to change the style of Treeview's content. But it doesn't work even if the snippet containing .item() have been run and no error occurs. My corresponding code snippet is as follows(some irrelevant part is omitted).
class Main_window(Tk):
# some_code_omitted...
def create_widgets():
# some_code_omitted...
self.tv1 = ttk.Treeview()
class A_Toplevel(Toplevel):
def __init__(self, parent):
self.parent = parent
# some_code_omitted...
def some_foo(self, event):
self.parent.tv1.item(ID, tags=(some_tag))
After some attempt, I found it seems that only when tv.item() is called in the Main_window, it works. later I wrote a method in Main_window to call tv.item(). But when the instance of A_Toplevel call it, it still doesn't work at all.
class Main_window(Tk):
# some_code_omitted...
def create_widgets():
# some_code_omitted...
self.tv1 = ttk.Treeview()
def a_foo(self, ):
self.tv1.item(ID, tags=(some_tag))
class A_Toplevel(Toplevel):
def __init__(self, parent):
self.parent = parent
# some_code_omitted...
def some_foo(self, event):
self.parent.a_foo()
What's wrong and how can I solve this problem?
Oh! Some progress.
Today, I found a way to solve it occasionally with threading module. Codes are as follows:
def a_foo_thr(self, ID, some_tag):
thr = threading.Thread(target=self.a_foo, args=(ID, some_tag))
thr.start()
def a_foo(self, ID, some_tag):
self.tv1.item(ID, some_tag)
But I have no idea why it succeeded, even whether it could make some unexpected problem.
I wrote a basic script in Python3 to create a basic function that would display then close a window. I would like to have a function to close the window, so I could import the script into a different file I have without cluttering it up too much. I tried this, however the compiler returns with name tkdel() is not defined.
def tkdisp(t):
import tkinter as tk
class disp(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.distk = tk.Label(self)
self.distk["text"] = t
self.distk.pack(side="top")
root = tk.Tk()
disp(master=root).mainloop()
def tkdel():
global tkdel
root.destroy
tkdisp('nice')
tkdel()
well I had to change the code quite a bit but this works (Hope it helps):
import tkinter as tk
root = tk
class disp(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.distk = tk.Label(self)
self.distk["text"] = t
self.distk.pack(side="top")
root = tk.Tk()
disp(master=root).mainloop()
def tkdel():
#global tkdel
app = None
exit()
disp.tkdel()
I try to not use globals too much as it can get complicated in a big program. :) it's better to send data between places directly. :)
I just reread the question. If you want to use another FILE - like file.py you can import the file on startup just by saying: import file
If you want to close the window from anywhere just use disp.tkdel() as this is a direct path. Really hope this helps. :)
So I'm trying to use my "controller" to call my "view." But if I don't use super, I get an infinite recursion when buildTK tries to build iiBar. Using Super everything is fine. I'd just like to understand why that is.
import inputhandler as iH
import buildtk as tA
import scanhandler as aS
class ControlHandler:
def __init__(self):
#self.view = tA.buildTK() #does not work
self.view = super(tA.buildTK) #works
self.smodel = aS.aScan()
self.imodel = iH.InputHandler()
The buildTK class:
import tkinter as tt
import controlhandler as cH
class buildTK(tt.Frame):
def __init__(self, master = None):
self.frame = tt.Frame(master, bg="tan")
self.frame.grid()
self.ibar = iiBar(self.frame)
...
class iiBar:
def __init__(self, master):
print(repr(self)) #prints forever
self.mbar = tt.Frame(master, relief = 'raised', bg="blue")
self.mbar.grid(column=0, row=0) #Show File Bar
self.tryFile()
self.tryTool()
self.tryH()
EDIT: those try methods have no effect when commented out, but the basic code is:
def tryTool(self):
# Create tools menu
self.toolsbutton = tt.Menubutton(self.mbar, text = 'Tools', )
self.toolsbutton.grid(row=0, column=2)
self.toolsmenu = tt.Menu(self.toolsbutton, tearoff=0)
self.toolsbutton['menu'] = self.toolsmenu
# Populate tools menu
self.toolsmenu.add('command', label = 'tools', command = root.destroy)
And out of curiosity, is there a best-practice for going about what I'm trying to do? Eventually I'd like a "build handler" to instantiate the form (either tkinter, html, xml) - in which case the controller would instantiate the build handler which would determine what to build.
Found my issues. Some python oversights.
the import seems to take care of the instantiation
part of this has to do with the inconsistency I used at the end of my classes, i.e;
a. #if name == "main": ControlHandler()
forgot that was commented out
b. buildTK
root = tt.Tk()
all = buildTK(root)
root.mainloop()
c. buildTK0
def main():
root = tk.Tk()
app = Application(root)
app.mainloop()
if __name__ == "__main__":main()
else: main()
Super appreciate the feedback guys! Gave me lots to consider/review!
What you show here is not a good example of inheritance - it is somehwat messed up. The same thing goes for yout attempeted uses of super - it just does not crash because you are not doing anything with the super-object at all, after creating it.
So., first, define if you are inheriting Tkinter components of compositing, having attributes of your classes that reference Tkinter objects. This code snippet:
class buildTK(tt.Frame):
def __init__(self, master = None):
self.frame = tt.Frame(master, bg="tan")
self.frame.grid()
self.ibar = iiBar(self.frame)
does a little of both.
I consider (and a lot of the internet) that trying to inherit from a complex GUI class is a waste of time and effort - although OO purists from the 90's thought that tobe a good idea and documented it that way. Tkinter classes have hundreds of methods and attributes - if you try to subclass one of those, your chances of name-clashing sky-rocket, to start with.
Otherwise, if you just don't try to inherit from any tkinter component, and just use them, and give-up on your non-use of supe, your app have a nice chance of working -
as for what "super" does: it provides a clean mean to call the method you are overriding in a sublass on its superclasses, respecting Python's method-resolution-order (mro), and without hardcoding the super-class reference inside the method. supershould be used only for that - for example, if you were to properly inherit tkinter.frame:
class buildTK(tt.Frame):
def __init__(self, master = None):
super(buildTK, self).__init__(master, bg="tan")
# this is for composition: self.frame = tt.Frame(master, bg="tan")
self.grid()
# a prefix on our attributes and methods avoid clashing with
# tkinter:
self._myapp_ibar = iiBar(self.frame)
I have a tkinter GUI python code that creates a gui interface to my code, in the code later snack sound toolkit is used (which also uses Tk and creates an instance using root = Tk()). As, mainloop of the previously GUI application is already running to everytime snack function is called a new empty default tk window pops up. As this happens quite a lot, there are hundreds of empty tk windows on screen when this code executes. I have tried to close them using numerous methods root.destroy,root.withdraw, WM_DELETE_WINDOW etc. but to no solution.
Is there any way this can be done in tkinter?
import tkSnack
import thread
import Tkinter as tk
class my_gui(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.grid(row=8)
def on_button(self):
thread1 = thread.start_new_thread(run, (PATH_TO_WAVE_FILE,))
def run(path):
for k in range(10):
PITCH_VALUES = snack_work(path)
print PITCH_VALUES
def snack_work(INPUT_WAVE_FILE):
# initializing the snack tool
root = tk.Tk()
tkSnack.initializeSnack(root)
# root.withdraw()
mysound = tkSnack.Sound()
# processing original wave file
mysound.read(INPUT_WAVE_FILE)
PITCH_VALUES = mysound.pitch()
return PITCH_VALUES
app = my_gui()
app.mainloop()
Make run() and snack_work() into instance methods of your app object, so that they can easily access that object's attributes. To work with a more minimal MCVE that doesn't rely on external libraries or files, I tested the following with simple print() (I'm on Python 3) and after() calls rather than snack stuff, as all I wanted to check was that the other functions could access a tkinter object.
import tkSnack
import thread
import Tkinter as tk
class my_gui(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.button.grid(row=8)
def on_button(self):
thread1=thread.start_new_thread(self.run,(PATH_TO_WAVE_FILE,))
def run(self, path):
for k in range(10):
PITCH_VALUES = self.snack_work(path)
print PITCH_VALUES
def snack_work(self, INPUT_WAVE_FILE):
## initializing the snack tool
tkSnack.initializeSnack(self) # use self instead of the separate root object
# self.withdraw()
mysound=tkSnack.Sound()
## processing original wave file
mysound.read(INPUT_WAVE_FILE)
PITCH_VALUES= mysound.pitch()
return PITCH_VALUES
app = my_gui()
app.mainloop()
I'm trying to make a simple program that continually displays and updates a label that displays the CPU usage, while having other unrelated things going on.
I've done enough research to know that threading is likely going to be involved. However, I'm having trouble applying what I've seen in simple examples of threading to what I'm trying to do.
What I currently have going:
import Tkinter
import psutil,time
from PIL import Image, ImageTk
class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
self.labelVariable = Tkinter.StringVar()
self.label = Tkinter.Label(self,textvariable=self.labelVariable)
self.label.pack()
self.button = Tkinter.Button(self,text='button',command=self.A)
self.button.pack()
def A (self):
G = str(round(psutil.cpu_percent(), 1)) + '%'
print G
self.labelVariable.set(G)
def B (self):
print "hello"
if __name__ == "__main__":
app = simpleapp_tk(None)
app.mainloop()
In the above code I'm basically trying to get command A continually running, while allowing command B to be done when the users presses the button.
You should never attempt to alter a UI element from a thread that isn't the main thread.
What you probably want is after(delay_ms, callback, args). Some information can be over at http://www.pythonware.com/library/tkinter/introduction/x9507-alarm-handlers-and-other.htm.
As a sample, here's a quick script to show a clock (Note: I've never really used Tk).
from Tkinter import *
from time import strftime
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.label_var = StringVar()
self.label = Label(self, textvariable=self.label_var)
self.label.pack()
# Start the loop
self.go()
def go(self):
self.label_var.set(strftime("%H:%M:%S"))
# The callback is only called once, so call it every time
self.after(1000, self.go)
app = App()
mainloop()
You don't need threads for such a simple task. You can simply schedule your task to run every second or so, which can be done with the 'after' method;
First, add this method to your simpleapp_tk class:
def update(self):
G = str(round(psutil.cpu_percent(), 1)) + '%'
self.labelVariable.set(G)
self.after(1000, self.update)
Then, in your initialize method add this call:
self.update()
This will cause the label to be updated to the current cpu value. The update method will then re-schedule itself to run again in one second.