The following code is runnable, you can just copy/paste:
from tkinter import *
import multiprocessing
startingWin = Tk()
def createClientsWin():
def startProcess():
clientsWin = Tk()
label = Label(clientsWin, text="Nothing to show")
label.grid()
clientsWin.mainloop()
if __name__ == "__main__":
p = multiprocessing.Process(target=startProcess)
p.start()
button = Button(startingWin, text="create clients", command=lambda: createClientsWin())
button.grid()
startingWin.mainloop()
So I simply want to create a completely separated Tk() window using multiprocessing. When I click on the create button, I just get the original window (not the intended one) and it gives me this error:
AttributeError: Can't pickle local object 'createClientsWin.<locals>.startProcess'
*Could someone explain how to start a separate new Tk() window using multiprocessing? *
Update: Not A Duplicate
Even if I follow the solution provided in the possible duplicate question, that doesn't help solving my question. Simply because, Tkinter is being used in my case. The modified code:
def createClientsWin():
clientsWin = Tk()
label = Label(clientsWin, text="Nothing to show")
label.grid()
clientsWin.mainloop()
def createClientsWinProcess():
if __name__ == "__main__":
p = multiprocessing.Process(target=createClientsWin)
p.start()
startingWin = Tk()
button = Button(startingWin, text="create clients", command=lambda: createClientsWinProcess())
button.grid()
startingWin.mainloop()
Function in global scope should be used for multiprocess target function, so the startProcess() should be moved into global scope.
Also the checking of if __name__ == "__main__" inside startProcess() will cause the code inside the if block not being executed.
Finally the creation of startingWin should be put inside if __name__ == "__main__" block, otherwise every process started will create the startingWin.
Below is the proposed changes to solve the above issues:
from tkinter import *
import multiprocessing
def startProcess():
clientsWin = Tk()
label = Label(clientsWin, text="Nothing to show")
label.grid()
clientsWin.mainloop()
def createClientsWin():
p = multiprocessing.Process(target=startProcess)
p.start()
if __name__ == '__main__':
startingWin = Tk()
button = Button(startingWin, text="create clients", command=createClientsWin)
button.grid()
startingWin.mainloop()
It is easier to use classes when using multiprocessing with tkinter. Try the following code:
import tkinter as Tk
import multiprocessing as mp
class A:
def __init__(self, master):
self.master = master
label = Tk.Label(self.master, text = 'A')
label.pack()
root_b = Tk.Toplevel()
GUI_B = B(root_b)
mp.Process(target = GUI_B.mainloop())
def mainloop(self):
self.master.mainloop()
class B:
def __init__(self, master):
self.master = master
label = Tk.Label(self.master, text = 'B')
label.pack()
def mainloop(self):
self.master.mainloop()
if __name__=='__main__':
root = Tk.Tk()
GUI_A = A(root)
mp.Process(target = GUI_A.mainloop())
I am developing a Tkinter app with Python. I have a two background operations and one operation with user demand. Here is my sample code:
from threading import Thread
import tkinter as tk
import time
class Controller(object):
def __init__(self, master):
self.master = master
self.btn1 = tk.Button(self.master, text="Start Recording", width=16, height=5, command=lambda: self.start_background_opt())
self.btn1.grid(row=2, column=0)
self.btn3 = tk.Button(self.master, text="Fly", width=16, height=5, command=lambda: self.fly_button())
self.btn3.grid(row=3, column=0)
self.entry = tk.Entry(self.master)
self.entry.grid(row=4, column=0)
self.connect_button_clicked = False
self.thread1 = None
self.thread2 = None
self.thread3 = None
self.flight_completed = False
def background_opt1(self):
while True:
if self.connect_button_clicked:
print("Battery Fetching")
else:
return
def background_opt2(self):
while True:
if self.connect_button_clicked:
print("Signal Fetching")
else:
return
def start_background_opt(self):
if not self.connect_button_clicked:
self.connect_button_clicked = True
self.thread1 = Thread(target=self.background_opt1).start()
self.thread2 = Thread(target=self.background_opt2).start()
else:
self.connect_button_clicked = False
self.thread1 = None
self.thread2 = None
def flight_operation_controller(self):
if self.flight_completed:
self.thread3 = None
def fly_button(self):
self.flight_completed = False
self.thread3 = Thread(target=self.static_sim()).start()
def static_sim(self):
while True:
if not self.flight_completed:
for _ in range(100):
print('Simulating')
time.sleep(0.1)
print("Simulation completed")
self.flight_completed = True
else:
return
if __name__ == '__main__':
root = tk.Tk()
# Set the window size
root.geometry("900x600+0+0")
control = Controller(root)
root.mainloop()
So when user click to "start recording", it starts 2 background operations. They should run as a background. Then when user click to "fly" button, fly operation should be executed.
In order to not blocking my main UI, I have put them in seperate threads.
Actually my all operations are working properly. I have put time.sleep
for replicating my fly operation; but when it runs, it blocks my entire, even though it is running in seperate thread.
Could you please tell me why I am seeing this?
Is my interpretation okey regarding the multithreading in Pyhton tkinter?
Best Regards
Take a look at this line of code:
self.thread3 = Thread(target=self.static_sim()).start()
The above code works exactly the same way as this code;
result = self.static_sim()
self.thread3 = Thread(target=result).start()
See the problem? You are calling your function outside of the thread. Because static_sim() has an infinite loop, it never returns.
When you set the target for Thread, it must be a callable. Change the code to this (note the lack of trailing ()):
self.thread3 = Thread(target=self.static_sim).start()
I write a pipeline for a lab, so I know it is impossible to insert a "console" in a GUI, so I made it with a Frame and I put label on it.
But the problem is, I am a beginner in threading, and I don't know how to use it to put my label into my frame after a function execution in a loop.
So this is my code (python 3.x) :
########
# IMPORTS #
########
from tkinter import *
from tkinter import ttk
from tkinter.filedialog import *
from tkinter.messagebox import *
import os
import glob
from datetime import date, time, datetime
#########
# FUNCTION #
#########
def OneFile(path1,DB,path2,seq,seq2,typ,path3):
"""
This function is very long, take all the sequences of the input file and BLAST it to the Library DATABASE
path : path to the library databse
DB : library database name
path2 : path of the file with the query sequences
seq : name of the file with the query sequences append with a job ID
seq2 : name of the file with the query sequence
Typ : Nucleotide or Proteine
"""
from datetime import date, time, datetime
import subprocess
import platform
import time
OS = platform.system()
if OS == 'Linux' or OS == 'Darwin':
pathLibrary = path1+'/'
pathSequence = path2+'/'
pathFolder = path3+'/'
if OS == 'Windows':
pathLibrary = path1+'\\'
pathSequence = path2+'\\'
if typ not in [(1,1),(2,2),(1,2),(2,1)]:
showerror('Error : Missing Type !', "You do not choose your type\n(nucleotides or proteins)")
else:
library = DB
if os.path.isfile(pathLibrary+library) != True:
showerror('Error : Missing File !', "You must choose a Library Database file")
else:
if os.path.isfile(pathSequence+seq2) != True:
showerror('Error : Missing File !', "You must choose your sequence file")
else:
if typ == (1,1):
typ = "blastn"
if typ == (2,2):
typ = "blastp"
if typ == (1,2):
typ = "blastx"
if typ == (2,1):
typ = "tblastn"
if OS == 'Linux' or OS == 'Darwin':
t0 = time.time()
query = str(seq2)
blast = str(seq)+'_Blast.txt'
seqs = str(seq)+'_seqs.txt'
subprocess.call(typ+" -query "+pathSequence+query+" -db "+pathLibrary+library+" -evalue 1e-10 -out "+pathFolder+blast, shell=True)
subprocess.call("grep '\(Sbjct\|>\)' "+pathFolder+blast+" > "+pathFolder+seqs, shell=True)
t1 = time.time()
print('Job finish in '+str(round(t1-t0,2))+' seconds')
if OS == 'Windows':
t0 = time.time()
query = str(seq2)
blast = str(seq)+'_Blast.txt'
seqs = str(seq)+'_seqs.txt'
subprocess.call(typ+' -query '+pathSequence+query+' -db '+pathLibrary+library+' -evalue 1e-10 -out '+pathSequence+blast, shell=True)
print('Fichier n° '+str(1)+' '+str(seq2))
subprocess.Popen('findstr "Sbjct >" '+pathSequence+blast+' > '+pathSequence+seqs, shell=True)
t1 = time.time()
print('Job finish in '+str(round(t1-t0,2))+' seconds')
#######
# CLASS #
#######
class GraphicalUserInterface():
#principal application
def __init__(self):
#constructor
self.fen = Tk()
self.fen.title("Starch Enzyme Pipeline")
#first label
self.label1 = Label(self.fen, text="Folder with your set(s) : ")
self.label1.grid(row=0, columnspan=2, sticky="W")
#first button
self.browse1 = Button(self.fen)
self.browse1.config(text="Browse",command=self.folderPath)
self.browse1.grid(row=1,column=0, sticky="W")
#label to show the path
self.varLabel1 = StringVar()
self.pathLabel1 = Label(self.fen, textvariable=self.varLabel1, relief=SUNKEN)
self.pathLabel1.grid(row=1,column=1, sticky="EW")
#second title
self.label2 = Label(self.fen, text="Folder with your library database(s) ")
self.label2.grid(row=2,column = 0, columnspan=2 , sticky="W")
#second button
self.browse2 = Button(self.fen)
self.browse2.config(text="Browse",command=self.folderPath2)
self.browse2.grid(row=3,column=0, sticky="W")
#label to show the path for database
self.varLabel2 = StringVar()
self.pathLabel2 = Label(self.fen, textvariable=self.varLabel2, relief=SUNKEN)
self.pathLabel2.grid(row=3,column=1, sticky = "EW")
#Frame wrappe listBox and other
self.frameListBoxAll = Frame(self.fen)
self.frameListBoxAll.grid(row=6,columnspan=2)
#list box label
self.labListBox1 = Label(self.frameListBoxAll, text="Your sets :",padx=10)
self.labListBox1.grid(row=0,column=0)
self.labListBox2 = Label(self.frameListBoxAll, text="Your library database :",padx=10)
self.labListBox2.grid(row=0,column=1)
#frame with listbox1
self.frame1 = Frame(self.frameListBoxAll, bd=2, relief=SUNKEN)
self.frame1.grid(row=1,column=0)
#frame with listbox1
self.frame2 = Frame(self.frameListBoxAll, bd=2, relief=SUNKEN)
self.frame2.grid(row=1,column=1)
#scrollbar listbox1
self.scrollbar1 = Scrollbar(self.frame1)
self.scrollbar1.grid(row=0,column=1, sticky="NS")
self.scrollbar2 = Scrollbar(self.frame2)
self.scrollbar2.grid(row=0,column=3, sticky="NS")
self.scrollbar3 = Scrollbar(self.frame1, orient=HORIZONTAL)
self.scrollbar3.grid(row=1,column=0, sticky="WE")
self.scrollbar4 = Scrollbar(self.frame2, orient=HORIZONTAL)
self.scrollbar4.grid(row=1,column=2, sticky="WE")
#liste box
self.listeBox1 = Listbox(self.frame1, selectmode=EXTENDED, exportselection=0, yscrollcommand=self.scrollbar1.set, xscrollcommand=self.scrollbar3.set)
self.listeBox1.grid(row=0,column = 0)
self.scrollbar1.config(command=self.listeBox1.yview)
self.scrollbar3.config(command=self.listeBox1.xview)
#liste box2
self.listeBox2 = Listbox(self.frame2, selectmode=EXTENDED, exportselection=0, yscrollcommand=self.scrollbar2.set, xscrollcommand=self.scrollbar4.set)
self.listeBox2.grid(row=0,column = 2)
self.scrollbar2.config(command=self.listeBox2.yview)
self.scrollbar4.config(command=self.listeBox2.xview)
#radioboutton list box 1
self.var = IntVar()
for item in [1,2]:
if item == 1:
self.rb = Radiobutton(self.frameListBoxAll, text='Nucleotides',value=item,variable=self.var)
self.rb.grid(row=2, column=0)
if item == 2:
self.rb = Radiobutton(self.frameListBoxAll, text='Proteins',value=item,variable=self.var)
self.rb.grid(row=3, column=0)
#radioboutton list box 2
self.var2 = IntVar()
for item in [1,2]:
if item == 1:
self.rb2 = Radiobutton(self.frameListBoxAll, text='Nucleotides',value=item,variable=self.var2)
self.rb2.grid(row=2, column=1)
if item == 2:
self.rb2 = Radiobutton(self.frameListBoxAll, text='Proteins',value=item,variable=self.var2)
self.rb2.grid(row=3, column=1)
#variables
self.path1 = str()
self.path2 = str()
self.path3 = str()
#RUN Buttun
self.runbutton = Button(self.fen, text="RUN",command=self.start_foo_thread).grid(row=7,column=0,columnspan=2)
#FRAME CONSOLE
self.console = Frame(self.fen)
self.console.config(relief=SUNKEN, bg="black", height=200, width=400)
self.console.grid(row=8, columnspan=10)
self.console.grid_propagate(False) #to block the size of the frame
#QUIT BUTTON
self.quitButton = Button(self.fen)
self.quitButton.config(text="QUIT", command=self.fen.destroy)
self.quitButton.grid(row=100,column=0)
def folderPath(self):
path = askdirectory(title='Choose your set folder')
self.varLabel1.set(path)
self.listeBox1.delete(0, END)
for filename in sorted(glob.glob(path+'/*')):
if os.path.isfile(filename):
#stockage of path
self.path1 = os.path.split(filename)[0]
name = os.path.split(filename)[1]
self.listeBox1.insert(END, name)
def folderPath2(self):
path = askdirectory(title="Choose your library database folder")
self.varLabel2.set(path)
self.listeBox2.delete(0, END)
for filename in sorted(glob.glob(path+'/*')):
if os.path.isfile(filename):
#stockage of path
self.path2 = os.path.split(filename)[0]
name = os.path.split(filename)[1]
self.listeBox2.insert(END, name)
def run(self):
self.fen.mainloop()
def createJobName():
job = str(datetime.now())
job = job.replace(" ","-")
return job
def typeNP(self):
liste = []
#selection of query files
valListBox1 = [self.listeBox1.get(idx) for idx in self.listeBox1.curselection()]
#selection of database file
valListBox2 = [self.listeBox2.get(idx) for idx in self.listeBox2.curselection()]
#selection of sequence type
typ = (self.var.get(),self.var2.get())
# loop
for i in range(len(valListBox2)):
job = GraphicalUserInterface.createJobName()
path1 = self.path2
path2 = self.path1
DB = valListBox2[i]
path3 = os.getcwd()+"/"+DB+job
if os.path.isdir(DB+job) == True:
showwarning('Warning', "The folder already exist \n or they are no folder name !\nChange or get the folder name")
else:
os.mkdir(DB+job)
for filename in valListBox1:
seq = filename+job
seq2 = filename
#stock data for OneFile function
liste.append([path1,DB,path2,seq,seq2,typ,path3])
return liste
def start_foo_thread(self):
liste = self.typeNP()
for i in range(len(liste)):
global foo_thread
import threading
print('Fichier n°'+str(i+1)+' '+str(liste[i][4]))
stringLabel = Label(self.console,text='Fichier n°'+str(i+1)+' '+str(liste[i][4]),bg='black', fg='white')
stringLabel.grid(row=i,sticky="W")
foo_thread = threading.Thread(target=OneFile(liste[i][0],liste[i][1],liste[i][2],liste[i][3],liste[i][4],liste[i][5],liste[i][6]))
foo_thread.daemon = True
foo_thread.start()
#########
# AUTORUN #
#########
if __name__ == '__main__':
app = GraphicalUserInterface()
app.run()
The problem is when the loop began in:
def start_foo_thread(self):
liste = self.typeNP()
for i in range(len(liste)):
With the print function, I see the function run, but the label does not go in the frame when the iteration finishes. When the loops are completed, I see my label in the frame.
What is the correct code to have my label in my frame at the same time my function runs in my loop?
You sometimes have to manually update the widget. You have posted too much code to wade through, so this simple example should show the problem. Run as is, the labels don't show up until the function returns. Run with update_idletasks() uncommented does what I think you want. Also note that the program stops until the call to subprocess returns.
import sys
if sys.version_info[0] < 3:
import Tkinter as tk ## Python 2.x
else:
import tkinter as tk ## Python 3.x
class GraphicalUserInterface():
def __init__(self):
#constructor
self.fen = tk.Tk()
self.fen.title("Starch Enzyme Pipeline")
self.console=tk.Frame(self.fen)
self.console.grid()
self.liste=["one", "two", "three"]
tk.Button(self.fen, text="Exit", command=self.fen.quit).grid(row=1)
self.start_foo_thread()
self.fen.mainloop()
def start_foo_thread(self):
for ctr in range(len(self.liste)):
lit='Fichier n %s' % (ctr)
print(lit)
stringLabel = tk.Label(self.console, text=lit,
bg='black', fg='white')
stringLabel.grid(row=ctr,sticky="W")
##self.console.update_idletasks()
print("Waiting 2 seconds")
self.fen.after(2000) ## wait 2 seconds to show effect
print("Return from function")
if __name__ == '__main__':
app = GraphicalUserInterface()
so I'm coding this GUI-program (using tkinter) and I'm using three Entryboxes in the same function. I wanna use their values in the main function, so how do I get the values into some kind of global variable or in a way that I can use them in a different function?
def options():
options_root = Tk()
textFrame = Frame(options_root)
textFrame.grid()
widthlabel = Label(textFrame, text="w:", justify=LEFT)
widthlabel.grid(column="0", row="0")
widthinput = Entry(textFrame)
widthinput.grid(column="1", row="0")
heightlabel = Label(textFrame, text="h:", justify=LEFT)
heightlabel.grid(column="0", row="1")
heightinput = Entry(textFrame)
heightinput.grid(column="1", row="1")
mlabel = Label(textFrame, text="m:", justify=LEFT)
mlabel.grid(column="0", row="2")
minput = Entry(textFrame)
minput.grid(column="1", row="2")
width = widthinput.get()
height = heightinput.get()
m = minput.get()
start_game_button = Button(options_root, text="Start", justify=LEFT, command=lambda:tabort(options_root))
start_game_button.grid(column="0",row="3")
exit_button = Button(options_root, text = "Exit", justify=LEFT, command=exit)
exit_button.grid(column="1", row="3")
mainloop()
def main():
options()
w = widthinput.get()
h = heightinput.get()
m = minput.get()
main()
Keep a reference to the widgets, then use the get() method. This becomes much easier if you design your application as a class:
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, ...):
...
self.width_entry = tk.Entry(...)
self.height_entry = tk.Entry(...)
self.minput_entry = tk.Entry(...)
...
def main(...):
w = self.width_entry.get()
h = self.height_entry.get()
m = self.input_entry.get()
...
...
app = SampleApp()
app.mainloop()