I want the method tryme to run only when I push the "snackPlay" button in the gui, but it runs as soon as I run the script. What can I do to make tryme run only on command?
Thanks.
import threading
from Tkinter import *
from tkSnack import *
class MyThread ( threading.Thread ):
def tryme ( self ):
print 'up uP UP'
root = Tk()
initializeSnack(root)
f = Frame(root)
f.pack()
Button(f, bitmap='snackPlay', command=MyThread().tryme()).pack(side='left')
root.mainloop()
I don't know a lot about threading, but you should try command = MyThread().tryme instead of command = MyThread().tryme() (it works for me after I remove all the tkSnack stuff).
Tkinter callbacks expect callable objects, not function results.
Related
I have a tkinter GUI, and am interested in adding a threading component to prevent my program from freezing. Here is my code:
from tkinter import *
import os
import threading
root = Tk()
root.title('Calculation Program')
root.geometry('700x525')
def param_log_export():
to_discover = disc_mode_choice.get()
def RunCalculation():
os.system('python command_center.py')
def combine_funcs(*funcs):
def combined_func(*args, **kwargs):
for f in funcs:
f(*args, **kwargs)
return combined_func
run_click_frame= Canvas(root, width= 450, height= 525)
save_button = Button(run_click_frame, width=450,height=1,text='Run Analysis!',fg='white',relief='flat',borderwidth=5,
bg='#2F4FAA',font=(Font_tuple,15),command = threading.Thread(target=combine_funcs(param_log_export,RunCalculation)).start())
save_button.pack(side=BOTTOM)
root.mainloop()
Essentially when the button is clicked, another script is called, and that script calls all of the smaller scripts for a certain calculation:
from target_list_create import mh_table
from ion_list_format import ion_mod_full
from theo_list_generation import theo_list_record
from precursor_fragment_matching import precursor_matches
Previously, I was just using command=combine_functs(param_log_export,RunCalculation), which worked fine, other than beginning to freeze as the size of my calculations grows. So, now I am trying the threading approach using the above code, but before I even have the opportunity to click the button which would command the threaded script, the program is running as though the button was clicked immediately. The console returns: UnboundLocalError: local variable 'fdr_algorithm_report' referenced before assignment, which indicates that there is a leak and the program triggers the combine_functs function as soon as the GUI is executed.
If anyone could help me understand what the issue is, so that when I click the button it executes the command without freezing the program, I would be very appreciative.
With this code I was able to create a TK Inter pop-up with a button to run a Sample_Function.
This Sample_Function destroys the tk pop-up, runs another python file, and then opens itself (the first pop-up) again.
How can I run the other_python_file and pop-up 'itself' at the same time — so I can be able to trigger many functions before each one gets completed?
import sys, os
from tkinter import *
import tkinter as tk
root = Tk()
def Sample_Function():
root.destroy()
sys.path.insert(0,'C:/Data')
import other_python_file
os.system('python this_tk_popup.py')
tk.Button(text='Run Sample_Function', command=Sample_Function).pack(fill=tk.X)
tk.mainloop()
I think this will do close to what you want. It uses subprocess.Popen() instead of os.system() to run the other script and rerun the pop-up which doesn't block execution while waiting for them to complete, so they can now execute concurrently.
I also added a Quit button to get out of the loop.
import subprocess
import sys
from tkinter import *
import tkinter as tk
root = Tk()
def sample_function():
command = f'"{sys.executable}" "other_python_file.py"'
subprocess.Popen(command) # Run other script - doesn't wait for it to finish.
root.quit() # Make mainloop() return.
tk.Button(text='Run sample_function', command=sample_function).pack(fill=tk.X)
tk.Button(text='Quit', command=lambda: sys.exit(0)).pack(fill=tk.X)
tk.mainloop()
print('mainloop() returned')
print('restarting this script')
command = f'"{sys.executable}" "{__file__}"'
subprocess.Popen(command)
I am creating a gui application using Tkinter, which imports other pieces of code stored in external .py files which contain time consuming functions. What I want to do is have a progressbar on my gui window which gets updated according to some function running in my imported script.
Gui script example:
#gui script
import tkinter
from tkinter import ttk
from somefile import somefunc
progcomp = ttk.Progressbar(root, orient='horizontal', length=200, mode = 'determinate', maximum=100)
somefunc()
External function example:
#somefile.py
def somefunc():
for i in range(1000):
#dosomething
#update progressbar of gui script
My actual code is too long to show in a question like this, so I chose to represent it as simply as possible. My question is, is this possible or will I have to change the infrastructure to accomplish?
You could use threading to implement this. Below is a very rough example of this.
threaded_task.py
import threading
import time
class ThreadedTask(threading.Thread):
def __init__(self, progress):
threading.Thread.__init__(self)
self.progress = progress
def run(self):
for i in range(100):
self.progress.step(1) # Update progress bar
time.sleep(1) # Simulate long running process
main.py
from tkinter import *
from tkinter import ttk
from threaded_task import ThreadedTask
root = Tk()
progcomp = ttk.Progressbar(root, orient='horizontal', length=200, mode = 'determinate', maximum=100)
progcomp.grid()
task = ThreadedTask(progcomp)
task.start()
root.mainloop()
I've a GUI which will perform some functions when the buttons are pressed.
now i want to create a button in the GUI which will call and run a shell script in the background.
how can i achieve this ?
Not sure if your question is about how to call a shell script in Python, or how to make a button in your GUI. If the former, my comment above (recommending some research on subprocess.Popen) is the solution. Otherwise:
# assuming Python3
import tkinter as tk
import subprocess as sub
WINDOW_SIZE = "600x400"
root = tk.Tk()
root.geometry(WINDOW_SIZE)
tk.Button(root, text="Push me!", command=lambda: sub.call('path/to/script')).pack()
Python can run shell scripts using the supbprocess module. In order to run it in the background you can start it from a new thread.
To use the module
import subprocess
...
subprocess.call(['./yourScript.sh'])
For a good python threading resource you can try: How to use threading in Python?
Adding to what #lakesh said, below is the complete script :
import Tkinter
import subprocess
top = Tkinter.Tk()
def helloCallBack():
print "Below is the output from the shell script in terminal"
subprocess.call('./yourscript.sh', shell=True)
B = Tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
top.mainloop()
Please note that the shell script is in the same directory as that of the python script.
If needed, do chmod 777 yourscript.sh
subprocess.call('./yourscript.sh', shell=True)
and import Tkinter and not import tkinter solved the problems I was facing.
Use Tkinter to create a button. For more info, look at this video:http://www.youtube.com/watch?v=Qr60hWFyKHc
Example:
from Tkinter import *
root = Tk()
app = Frame(root)
app.grid()
button1 = Button(app,"Shell Script")
button1.grid()
root.mainloop()
To add the functionality:
change button1 line to:
button1 = Button(app,"Shell Script",command=runShellScript)
def runShellScript():
import subprocess
subprocess.call(['./yourScript.sh'])
When my program executes the python GUI freezes. Here is my main code. Can I get some help in doing threading? So the execution happens in the background and I can still be able to use the "x" button in the GUI if I want to end the execution? Currently I just ask the user to close the cmd to end the program.
if __name__ == "__main__":
root = Tk()
root.title('Log')
root.geometry("400x220")
font1=('times', 15)
font2=('times', 10)
#Label inside root
Label(root, relief=GROOVE, font=font2, text="level").pack()
variable = StringVar(root)
variable.set("INFO") # default value
w = OptionMenu(root, variable, "CRITICAL", "DEBUG")
w.pack()
Button(root, font=font1, background= "yellow", text='START',command=main).pack()
Label(root, text="To end just close the CMD window").pack()
root.mainloop()
UPDATE: Turns out the Button callback was autorunning launch because the function object wasn't being set as the callback, the called function itself was. The fix is to replace the callback lambda: spawnthread(fcn) so that a function object is set as the callback instead. The answer has been updated to reflect this. Sorry for missing that.
The GUI mainloop will freeze when you try to run some other function, and has no way to restart itself (because it's frozen.)
Let's say the command you'd like to run alongside the GUI mainloop is myfunction.
Imports:
import time
import threading
import Queue
You need to set up a ThreadedClient class:
class ThreadedClient(threading.Thread):
def __init__(self, queue, fcn):
threading.Thread.__init__(self)
self.queue = queue
self.fcn = fcn
def run(self)
time.sleep(1)
self.queue.put(self.fcn())
def spawnthread(fcn):
thread = ThreadedClient(queue, fcn)
thread.start()
periodiccall(thread)
def periodiccall(thread):
if(thread.is_alive()):
root.After(100, lambda: periodiccall(thread))
You then want the widget calling the function to instead call a spawnthread function:
queue = Queue.Queue()
Button(root, text='START',command=lambda: spawnthread(myfunction)).pack() #<---- HERE
N.B. I'm adapting this from a multithreaded tkinter GUI I have; I have all my frames wrapped up in classes so this might have some bugs since I've had to tweak it a bit.