Unable to executing multiple functions using multiprocessing module from a TKinter GUI - python-3.x

Hi I have a small GUI which has a button using which two functions must be executed in different processors. In reality these two functions are heavy calculations. I do not want to use multi threading. I want them to run on 2 different processors. When I try to execute the button, another instance of the GUI gets created and it says
File "C:\Python3.7\lib\multiprocessing\reduction.py", line 60, in dump.
ForkingPickler(file, protocol).dump(obj)
TypeError: can't pickle _tkinter.tkapp objects
My code is as follows.
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
def F1(self):
print("Hello")
def F2(self):
print("World")
def execute(self):
self.P1 = Process(target = self.F1)
self.P2 = Process(target = self.F2)
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()
Please click here

The problem lies with pickling tkinter widgets. You simply cannot do it, as the Tcl interpreter does not understand the python pickle format.
Coming to your code, I tried the following and it prints as expected:
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
#staticmethod
def F1():
print("Hello")
#staticmethod
def F2():
print("World")
def execute(self):
self.P1 = Process(target = GUI.F1())
self.P2 = Process(target = GUI.F2())
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()

Related

Tkinter gives me a second window

I am writing code for a tkinter gui using a class, however I notice that when I run there is a second window besides the main one I made. I've tried a number of things but they either break the code or the window is black. See code below.
import tkinter as gui
class loginWindow(gui.Frame):
def __init__(self):
super(loginWindow, self).__init__()
self.logUI()
def logUI(self):
self.mainWindow = gui.Tk()
self.mainWindow.title("GLSC IT Inventory")
self.mainWindow.minsize(400, 150)
self.mainWindow.maxsize(400, 150)
self.mainWindow.geometry("400x150")
self.greet_label = gui.Label(self.mainWindow, text="Welcome!!!")
self.greet_label.place(x=180, y=5)
self.uname_label = gui.Label(self.mainWindow, text="Username:")
self.uname_label.place(x=10, y=24)
self.uname_input = gui.StringVar()
self.uname_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.uname_input)
self.uname_field.place(x=80, y=25, width=160)
self.pwd_label = gui.Label(self.mainWindow, text="Password:")
self.pwd_label.place(x=10, y=54)
self.pwd_input = gui.StringVar()
self.pwd_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.pwd_input, show="\u2022")
self.pwd_field.place(x=80, y=55, width=160)
self.login_button = gui.Button(self.mainWindow, text="Login", command=None)
self.login_button.place(x=180, y=95)
my_app = loginWindow()
my_app.mainloop()
When you create instance of loginWindow(), an instance of Tk() is required but there is none, so it will be created implicitly for you.
Then another instance of Tk() is created inside logUI(). So there are two instances of Tk().
One way to fix it is loginWindow not inherited from Frame:
class loginWindow:
def __init__(self):
self.logUI()
def logUI(self):
...
# add for calling tkinter.mainloop()
def mainloop(self):
self.mainWindow.mainloop()

How to implement a "processing request"-message in TkInter

I'm trying to implement a popup-window, which should display over the main window, while an operation is taking place in the background. Pressing the button from the code below results in freezing the GUI for 10s without displaying any message and eventually making the button green. The freezing is normal, but I would like to have the popup displayed during the 10 seconds.
Any help would be appreciated! Thanks in advance!
import tkinter as tk
import time
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.button1 = tk.Button(text="Start", command=self.make_green)
self.button1.pack()
def popup(self):
tl = tk.Toplevel(self)
tl.transient()
tk.Label(tl, text="Painting green").pack()
tl.grab_set()
return tl
def make_green(self):
wait_popup = self.popup()
time.sleep(10)
self.button1.config(bg="green")
wait_popup.destroy()
a = GUI()
a.mainloop()
You can use self.update() in your code to make the popup window appear.
import tkinter as tk
import time
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.button1 = tk.Button(text="Start", command=self.make_green)
self.button1.pack()
def popup(self):
tl = tk.Toplevel(self)
tl.transient()
tk.Label(tl, text="Painting green").pack()
self.update()
tl.grab_set()
return tl
def make_green(self):
wait_popup = self.popup()
time.sleep(10)
self.button1.config(bg="green")
wait_popup.destroy()
a = GUI()
a.mainloop()
Or you can use threading.
Start by importing Thread from threading.
from threading import Thread
Then make a new method
def thread_it(self):
return Thread(target=self.make_green, daemon=True).start()
and update your button's command
self.button1 = tk.Button(text="Start", command=self.thread_it)

Why does first window need a call to mainloop() while the second window doesn't

I am making a application that needs 2 windows. The first one, I do all the standard stuff like
root = tk.Tk()
...code...
root.mainloop()
But for my second window, I only call
root = tk.Tk()
and it works. If I do
root = tk.Tk()
...code...
root.mainloop()
it still works. Out of pure curiosity, why?
Code:
import tkinter as tk
from tkinter.messagebox import showerror
from time import sleep
class DecompilingChecker(object):
def __init__(self):
self.master = tk.Tk()
self.master.withdraw()
self.master.title("Test Program: Update")
def check(self, file, directory):
self.master.update()
self.master.deiconify()
class TestProgram(object):
pass
class GUI(object):
def __init__(self, master):
self.master = master
self.master.title("Test Program")
tk.Text(self.master).grid(row=0, column=0)
self.decompilingchecker = DecompilingChecker()
self.decompilingchecker.check(None, None)
class Bridge(object):
def __init__(self):
self.root = tk.Tk()
GUI(self.root)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
try:
bridge = Bridge()
bridge.run()
except Exception as e:
showerror("Test Program: ERROR!", "An error has occurred!\n{0}".format(str(e)))
``
You should not call tk.Tk() more than once. For additional windows, use tk.Toplevel(). Calling update is only necessary in some rare cases, your GUI is most times better off relying on the mainloop for the updates.
import tkinter as tk
class DecompilingChecker(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("DecompilingChecker Toplevel")
def check(self):
print('checking')
class Bridge(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Bridge Toplevel")
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.title("GUI window")
self.bridge = Bridge(self)
self.d_checker = DecompilingChecker(self)
self.d_checker.check()
if __name__ == "__main__":
GUI().mainloop()

in Python3/tkinter is it possible to create a toolbar into a separate class, but still allowing it to interact with the main app?

I am about to begin a new Python3/tkinter project and I want to make sure that I get as much code out of my way as possible. I am creating an app that will have, for now, one window composed of 3 areas:
Toolbar
Center/main area
Statusbar
I am trying to keep the main app class as clean as possible, offloading code to other auxiliary classes. So, following some tutorials and adapting from what I have been doing until now, I was able already to set an external toolbar class that can be changed on demand, from the main app. Now, I am trying to create a class for the toolbar, but I am afraid it won't be possible to create the buttons and their respective callbacks in a separate class, as I don't know how to make them call functions that are in the main app. Is that even possible?
This is what I got right now:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
class App:
""" main class for the application """
def __init__(self,master):
mainframe = ttk.Frame(master)
topframe = ttk.Frame(mainframe)
centerframe = ttk.Frame(mainframe)
bottomframe = ttk.Frame(mainframe)
my_toolbar = Toolbar(topframe)
my_statusbar = StatusBar(mainframe)
my_statusbar.set("This is the statusbar")
centerlabel = ttk.Label(centerframe, text="Center stuff goes here")
centerlabel.pack()
topframe.pack(side=TOP, fill=X)
centerframe.pack(side=TOP, fill=BOTH)
bottomframe.pack(side=BOTTOM, fill=X)
mainframe.pack(side=TOP, expand=True, fill=BOTH)
def button_function(self, *event):
print("filter")
class StatusBar(ttk.Frame):
""" Simple Status Bar class - based on Frame """
def __init__(self,master):
ttk.Frame.__init__(self,master)
self.label = ttk.Label(self,anchor=W)
self.label.pack()
self.pack(side=BOTTOM, fill=X)
def set(self,texto):
self.label.config(text=texto)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
class Toolbar:
""" Toolbar """
def button_one(self):
print("button 1 pressed")
def button_two(self):
print("button 2 pressed")
def __init__(self,master):
self.button1 = ttk.Button(master,text="One",command=self.button_one())
self.button2 = ttk.Button(master,text="Two",command=self.button_two())
self.button1.grid(row=0,column=0)
self.button2.grid(row=0,column=1)
if __name__ == "__main__":
root = Tk()
app = App(root)
root.mainloop()
Let's say that I need to make button1 to trigger button_function() in order to update some info being shown there. Should I simply move the toolbar into the App class, for instance in a class method called from its __init__()? Or is there a better way?
Maybe I should add that i intend later to add some Toplevelwindows that probably could make use of some of these general classes. I want to pave the road in a nice way.
This is certainly possible. There are two possibilities here. The first one to make app inherit from ttk.Frame instead of using mainframe. Then you can pass App as master to toolbar etc. Here is the redone code:
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
class App(ttk.Frame):
""" main class for the application """
def __init__(self,master,*args,**kwargs):
super().__init__(master,*args,**kwargs)
self.my_toolbar = Toolbar(self)
self.my_statusbar = StatusBar(self)
self.my_statusbar.set("This is the statusbar")
self.centerframe = CenterFrame(self)
self.pack(side=tk.TOP, expand=True, fill=tk.BOTH)
def button_function(self, *event):
print("filter")
class CenterFrame(ttk.Frame):
def __init__(self,master,*args,**kwargs):
super().__init__(master,*args,**kwargs)
self.master = master
self.pack(side=tk.BOTTOM, fill=tk.X)
self.centerlabel = ttk.Label(self, text="Center stuff goes here")
self.centerlabel.pack()
class StatusBar(ttk.Frame):
""" Simple Status Bar class - based on Frame """
def __init__(self,master):
ttk.Frame.__init__(self,master)
self.master = master
self.label = ttk.Label(self,anchor=tk.W)
self.label.pack()
self.pack(side=tk.BOTTOM, fill=tk.X)
def set(self,texto):
self.label.config(text=texto)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
class Toolbar(ttk.Frame):
""" Toolbar """
def button_one(self):
print("button 1 pressed")
def button_two(self):
print("button 2 pressed")
self.master.button_function()
def __init__(self,master):
super().__init__(master)
self.master = master
self.pack(side=tk.TOP, fill=tk.X)
self.button1 = ttk.Button(self,text="One",command=self.button_one)
self.button2 = ttk.Button(self,text="Two",command=self.button_two)
self.button1.grid(row=0,column=0)
self.button2.grid(row=0,column=1)
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.mainloop()
The second one is to just pass App as an argument to the other classes.
You are missing some self., button commands assignments don't need parenthesis and after that you can call the button configuration from anywhere in your program. So for the button1 command this will be:
app.my_toolbar.button1.config(command=app.button_function)
I am fixing your errors as is, not make the program better:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
class App:
""" main class for the application """
def __init__(self,master):
self.mainframe = ttk.Frame(master)
self.topframe = ttk.Frame(self.mainframe)
self.centerframe = ttk.Frame(self.mainframe)
self.bottomframe = ttk.Frame(self.mainframe)
self.my_toolbar = Toolbar(self.topframe)
self.my_statusbar = StatusBar(self.mainframe)
self.my_statusbar.set("This is the statusbar")
self.centerlabel = ttk.Label(self.centerframe, text="Center stuff goes here")
self.centerlabel.pack()
self.topframe.pack(side=TOP, fill=X)
self.centerframe.pack(side=TOP, fill=BOTH)
self.bottomframe.pack(side=BOTTOM, fill=X)
self.mainframe.pack(side=TOP, expand=True, fill=BOTH)
def button_function(self, *event):
print("filter")
class StatusBar(ttk.Frame):
""" Simple Status Bar class - based on Frame """
def __init__(self,master):
ttk.Frame.__init__(self,master)
self.label = ttk.Label(self,anchor=W)
self.label.pack()
self.pack(side=BOTTOM, fill=X)
def set(self,texto):
self.label.config(text=texto)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
class Toolbar:
""" Toolbar """
def button_one(self):
print("button 1 pressed")
def button_two(self):
print("button 2 pressed")
def __init__(self,master):
self.button1 = ttk.Button(master,text="One",command=self.button_one)
self.button2 = ttk.Button(master,text="Two",command=self.button_two)
self.button1.grid(row=0,column=0)
self.button2.grid(row=0,column=1)
if __name__ == "__main__":
root = Tk()
app = App(root)
app.my_toolbar.button1.config(command=app.button_function)
root.mainloop()

PySide: Threading causes GUI to crash

I'm trying to learn the basics of threading with PySide, and so put together the below code. What I'm trying to do is launch a thread that will update a QPlainTextEdit widget using a list of string, with a delay between each string. Instead what I'm getting is a crash to desktop, and I can't understand why:
import sys
import time
from PySide import QtCore, QtGui
class Worker(QtCore.QThread):
to_log = QtCore.Signal(str)
def __init__(self, txt, parent=None):
super(Worker, self).__init__(parent)
self.txt = txt
def run(self):
for i in self.txt:
self.to_log.emit(i)
time.sleep(1)
class TestThreadsApp(QtGui.QWidget):
def __init__(self):
super(TestThreadsApp, self).__init__()
self.initUI()
def initUI(self):
self.log = QtGui.QPlainTextEdit()
self.pb = QtGui.QPushButton('Go')
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.log)
hbox.addWidget(self.pb)
self.setLayout(hbox)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test')
self.show()
self.pb.clicked.connect(self.get_worker)
def get_worker(self):
self.proceed = False
worker = Worker(['This is a test', 'to understand threading'])
worker.to_log.connect(self.to_log)
worker.start()
def to_log(self, txt):
self.log.appendPlainText(txt)
def main():
app = QtGui.QApplication(sys.argv)
ex = TestThreadsApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If I update the get_worker() method to the below, it will run, but the QPlainTextEdit widget is updated with all the strings simultaneously, where as the behavior I'm wanting is for the widget to be updated by the threaded processes as each string is emitted - not altogether after both have been emitted:
def get_worker(self):
self.proceed = False
worker = Worker(['This is a test', 'to understand threading'])
worker.to_log.connect(self.to_log)
worker.start()
while not worker.isFinished():
pass
You need to keep a reference to the thread, otherwise it will be garbage-collected as soon as get_worker returns.
So do something like this, instead:
def get_worker(self):
self.worker = Worker(['This is a test', 'to understand threading'])
self.worker.to_log.connect(self.to_log)
self.worker.start()

Resources