I need a progressbar to show the user the progress of the svn updates, but I do not know how to do this simultaneously to the svn update, I can only do it afterwards and then only indirectly, but if I let the progressbar show after the update , then I need twice the time and that's not the point.
Please help me how to update the progressbar while the update is running which needs a variable time and please give me an example of what you mean
class test():
def __init__(self, dictionary_with_paths):
self.dictionary_with_paths = {}
self.progressbar = Progressbar(range_end)
def update_local_tag_folder(self):
os.system('svn update '+ self.dictionary_with_paths['local_tags'])
def update_progressbar(self, current_value):
"""
Progressbar() is a class that simply create an tk() frame with 1
label, an progressbar and a button and it need an start value and
an end point, the end point comes with an parameter in the class,
the start/current count are given from method
Progressbar().set_current_Value(current_value)
"""
self.progressbar.set_current_value(current_Value)
self.progressbar.update()
def get_update_progress_and_update_bar(self):
"""
here is the point i have no idea how can i can get an list of
files or any other useful things
"""
SVN_list = os.system('svn ls -v -R '+ \
self.dictionary_with_paths['repository_trunk'])
"""
Console shows an list with all files, but i cant read the lines to
count that
"""
I also thought about using subprocessor but I do not know exactly how to read the output of SVN_list so that I can count the lines
SOLVED:
Fixed problem, I have created new threads in which I can once run the svn update and in another I can simultaneously create the Progressbar and let it run ...
short snapshot what i mean...
from threading import Thread
from time import sleep
class Yeeeehaaa():
def mymethod1(self, arg):
for e in range(0,arg):
print('Yeeeee')
sleep(1)
def mymethod2(self, arg):
for e in range(0,arg):
print('haaaaa')
sleep(1)
def start_threads(self):
thread = Thread(target=mymethod1, args=(10,))
thread.start()
thread = Thread(target=mymethod2, args=(10,))
thread.start()
thread.join()
thread.join()
Related
My interface is freezing on pressing the button. I am using threading but I am not sure why is still hanging. Any help will be appreciated. Thanks in advance
class magic:
def __init__(self):
self.mainQueue=queue.Queue()
def addItem(self,q):
self.mainQueue.put(q)
def startConverting(self,funcName):
if(funcName=="test"):
while not self.mainQueue.empty():
t = Thread(target = self.threaded_function)
t.start()
t.join()
def threaded_function(self):
time.sleep(5)
print(self.mainQueue.get())
m=magic()
def helloCallBack():
m.addItem("asd")
m.startConverting("test") //this line of code is freezing
B = tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
top.mainloop()
Here's a recipe for doing an asynchronous task with a tkinter-based GUI. I adapted it from a recipe in the cited book. You should be able to modify it to do what you need.
To keep the GUI responsive requires not interfering with its mainloop() by doing something like join()ing a background thread—which makes the GUI "hang" until the thread is finished. This is accomplished by using the universal after() widget method to poll a Queue at regular intervals.
# from "Python Coobook 2nd Edition", section 11.9, page 439.
# Modified to work in Python 2 & 3.
from __future__ import print_function
try:
import Tkinter as tk, time, threading, random, Queue as queue
except ModuleNotFoundError: # Python 3
import tkinter as tk, time, threading, random, queue
class GuiPart(object):
def __init__(self, master, queue, end_command):
self.queue = queue
# Set up the GUI
tk.Button(master, text='Done', command=end_command).pack()
# Add more GUI stuff here depending on your specific needs
def processIncoming(self):
""" Handle all messages currently in the queue, if any. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
# Check contents of message and do whatever is needed. As a
# simple example, let's print it (in real life, you would
# suitably update the GUI's display in a richer fashion).
print(msg)
except queue.Empty:
# just on general principles, although we don't expect this
# branch to be taken in this case, ignore this exception!
pass
class ThreadedClient(object):
"""
Launch the main part of the GUI and the worker thread. periodic_call()
and end_application() could reside in the GUI part, but putting them
here means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI as well. We spawn a new thread for the worker (I/O).
"""
self.master = master
# Create the queue
self.queue = queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.end_application)
# Set up the thread to do asynchronous I/O
# More threads can also be created and used, if necessary
self.running = True
self.thread1 = threading.Thread(target=self.worker_thread1)
self.thread1.start()
# Start the periodic call in the GUI to check the queue
self.periodic_call()
def periodic_call(self):
""" Check every 200 ms if there is something new in the queue. """
self.master.after(200, self.periodic_call)
self.gui.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
def worker_thread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'. One important thing to remember is that the thread has
to yield control pretty regularly, be it by select or otherwise.
"""
while self.running:
# To simulate asynchronous I/O, create a random number at random
# intervals. Replace the following two lines with the real thing.
time.sleep(rand.random() * 1.5)
msg = rand.random()
self.queue.put(msg)
def end_application(self):
self.running = False # Stops worker_thread1 (invoked by "Done" button).
rand = random.Random()
root = tk.Tk()
client = ThreadedClient(root)
root.mainloop()
For anyone having a problem with sys.exit(1) in #martineau's code - if you replace sys.exit(1) with self.master.destroy() the program ends gracefully. I lack the reputation to add a comment, hence the seperate answer.
Windows 10 with Python 2.7.13 (32 bit)
I have a Python tkinter program with a progress bar. The program performs a long-running operation, and then waits for a set amount of time before repeating it.
To provide feedback to the user, a progress bar is updated in derterminate mode when counting down to the next operation, and then switches to an indeterminate bar during the operation (of unknown duration).
After 15-30mins, during a countdown using the code below, Windows pops up a python.exe has stopped working dialog, offering only the option to close the program, and the program is unresponsive (presumably because Windows has halted it).
I cannot figure out why this is occurring. During this time, in the GUI the following segment of code is running:
def wait_nextop(self, delay):
end = time.time() + delay
self.progress_bar.stop()
self.progress_bar.config(mode = 'determinate', max = delay)
while time.time() < end:
remaining = (end - time.time()) + 1
self.progress_bar.config(value = (delay - remaining))
remaining_text = str(datetime.timedelta(seconds = int(remaining)))
self.progress_text.config(text = 'Next operation in ' + remaining_text)
time.sleep(0.1)
self.progress_text.config(text = 'Operation in progress')
self.progress_bar.config(mode = 'indeterminate')
self.progress_bar.start()
In this, delay is an integer delay in seconds. self.progress_bar is an instance of ttk.ProgressBar and self.progress_text is an instance of tkinter.Label. time and datetime are from the standard libraries.
Python offers no stack trace, and I do not currently have other systems available to test this on. It should be noted that this GUI function is called by another thread, but is intended to execute within the main thread.
I've seen these similar questions, but couldn't find a working resolution:
Python tkinter code stops working
Tkinter python crashes on new thread trying to log on main thread
Based on advice from the comments, it seems that I should be using tkinter's events system, rather than calling functions in the GUI thread directly.
Here is a working example for Python2.7 using the described concepts. It creates the GUI with progress bar, and then updates it from another thread using the event system.
## Import the required modules
import threading
import Tkinter as tk
import ttk
import time
import random
## Class for creating the GUI
class GUI():
def __init__(self):
## Make the GUI
self.root = tk.Tk()
self.progress = ttk.Progressbar(self.root, orient = 'horizontal', length = 100, mode = 'determinate', max = 10)
## Bind an event to trigger the update
self.root.bind('<<MoveIt>>', self.update_progress)
## Pack the progress bar in to the GUI
self.progress.pack()
def run(self):
## Enter the main GUI loop. this blocks the main thread
self.root.mainloop()
## Updates the progress bar
def update_progress(self, event):
## Work out what the new value will be
new_value = self.progress['value'] + 1
## If the new value is less than 10..
if new_value < 10:
print('[main thread] New progress bar value is ' + str(new_value))
## Update the progress bar value
self.progress.config(value = new_value)
## Force the GUI to update
self.root.update()
## If the new value is more than 10
else:
print('[main thread] Progress bar done. Exiting!')
## Exit the GUI (and terminate the script)
self.root.destroy()
## Class for creating the worker thread
class Worker(threading.Thread):
def __init__(self, ui):
## Run the __init__ function from our threading.Thread parent
threading.Thread.__init__(self)
## Save a local reference to the GUI object
self.ui = ui
## Set as a daemon so we don't block the program from exiting
self.setDaemon(True)
def run(self):
print('[new thread] starting')
## Count to 10
for i in range(10):
## Generate an event to trigger the progress bar update
ui.root.event_generate('<<MoveIt>>', when = 'tail')
## Wait between one and three seconds before doing it again
wait = random.randint(1,3)
print('[new thread] Incrementing progress bar again in ' + str(wait) + ' seconds')
time.sleep(wait)
## Create an instance of the GUI class
ui = GUI()
## Create an instance of the Worker class and tell it about the GUI object
work = Worker(ui)
## Start the worker thread
work.start()
## Start the GUI thread (blocking)
ui.run()
My interface is freezing on pressing the button. I am using threading but I am not sure why is still hanging. Any help will be appreciated. Thanks in advance
class magic:
def __init__(self):
self.mainQueue=queue.Queue()
def addItem(self,q):
self.mainQueue.put(q)
def startConverting(self,funcName):
if(funcName=="test"):
while not self.mainQueue.empty():
t = Thread(target = self.threaded_function)
t.start()
t.join()
def threaded_function(self):
time.sleep(5)
print(self.mainQueue.get())
m=magic()
def helloCallBack():
m.addItem("asd")
m.startConverting("test") //this line of code is freezing
B = tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
top.mainloop()
Here's a recipe for doing an asynchronous task with a tkinter-based GUI. I adapted it from a recipe in the cited book. You should be able to modify it to do what you need.
To keep the GUI responsive requires not interfering with its mainloop() by doing something like join()ing a background thread—which makes the GUI "hang" until the thread is finished. This is accomplished by using the universal after() widget method to poll a Queue at regular intervals.
# from "Python Coobook 2nd Edition", section 11.9, page 439.
# Modified to work in Python 2 & 3.
from __future__ import print_function
try:
import Tkinter as tk, time, threading, random, Queue as queue
except ModuleNotFoundError: # Python 3
import tkinter as tk, time, threading, random, queue
class GuiPart(object):
def __init__(self, master, queue, end_command):
self.queue = queue
# Set up the GUI
tk.Button(master, text='Done', command=end_command).pack()
# Add more GUI stuff here depending on your specific needs
def processIncoming(self):
""" Handle all messages currently in the queue, if any. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
# Check contents of message and do whatever is needed. As a
# simple example, let's print it (in real life, you would
# suitably update the GUI's display in a richer fashion).
print(msg)
except queue.Empty:
# just on general principles, although we don't expect this
# branch to be taken in this case, ignore this exception!
pass
class ThreadedClient(object):
"""
Launch the main part of the GUI and the worker thread. periodic_call()
and end_application() could reside in the GUI part, but putting them
here means that you have all the thread controls in a single place.
"""
def __init__(self, master):
"""
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI as well. We spawn a new thread for the worker (I/O).
"""
self.master = master
# Create the queue
self.queue = queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.end_application)
# Set up the thread to do asynchronous I/O
# More threads can also be created and used, if necessary
self.running = True
self.thread1 = threading.Thread(target=self.worker_thread1)
self.thread1.start()
# Start the periodic call in the GUI to check the queue
self.periodic_call()
def periodic_call(self):
""" Check every 200 ms if there is something new in the queue. """
self.master.after(200, self.periodic_call)
self.gui.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
def worker_thread1(self):
"""
This is where we handle the asynchronous I/O. For example, it may be
a 'select()'. One important thing to remember is that the thread has
to yield control pretty regularly, be it by select or otherwise.
"""
while self.running:
# To simulate asynchronous I/O, create a random number at random
# intervals. Replace the following two lines with the real thing.
time.sleep(rand.random() * 1.5)
msg = rand.random()
self.queue.put(msg)
def end_application(self):
self.running = False # Stops worker_thread1 (invoked by "Done" button).
rand = random.Random()
root = tk.Tk()
client = ThreadedClient(root)
root.mainloop()
For anyone having a problem with sys.exit(1) in #martineau's code - if you replace sys.exit(1) with self.master.destroy() the program ends gracefully. I lack the reputation to add a comment, hence the seperate answer.
I'm writing a program with a GUI using TKinter, in which the user can click a button and a new process is started to perform work using multiprocess.Process. This is necessary so the GUI can still be used while the work is being done, which can take several seconds.
The GUI also has a text box where the status of the program is displayed when things happen. This is often straight forward, with each function calling an add_text() function which just prints text in the text box. However, when add_text() is called in the separate process, the text does not end up in the text box.
I've thought about using a Pipe or Queue, but that would require using some sort of loop to check if anything has been returned from the process and that would also cause the main (GUI) process to be unusable. Is there some way to call a function in one process that will do work in another?
Here's an simple example of what I'm trying to do
import time
import multiprocessing as mp
import tkinter as tk
textbox = tk.Text()
def add_text(text):
# Insert text into textbox
textbox.insert(tk.END, text)
def worker():
x = 0
while x < 10:
add_text('Sleeping for {0} seconds'.format(x)
x += 1
time.sleep(1)
proc = mp.Process(target=worker)
# Usually happens on a button click
proc.start()
# GUI should still be usable here
The asyncronous things actually require loop.
You could attach function to the TkInter's loop by using Tk.after() method.
import Tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.check_processes()
self.root.mainloop()
def check_processes(self):
if process_finished:
do_something()
else:
do_something_else()
self.after(1000, check_processes)
app=App()
I ended up using a multiprocessing.Pipe by using TKinter's after() method to perform the looping. It loops on an interval and checks the pipe to see if there's any messages from the thread, and if so it inserts them into the text box.
import tkinter
import multiprocessing
def do_something(child_conn):
while True:
child_conn.send('Status text\n')
class Window:
def __init__(self):
self.root = tkinter.Tk()
self.textbox = tkinter.Text()
self.parent_conn, child_conn = multiprocessing.Pipe()
self.process = multiprocessing.Process(target=do_something, args=(child_conn,))
def start(self):
self.get_status_updates()
self.process.start()
self.root.mainloop()
def get_status_updates()
status = self.check_pipe()
if status:
self.textbox.add_text(status)
self.root.after(500, self.get_status_updates) # loop every 500ms
def check_pipe():
if self.parent_conn.poll():
status = self.parent_conn.recv()
return status
return None
Here is the code sample:
class RunGui (QtGui.QMainWindow)
def __init__(self, parent=None):
...
QtCore.Qobject.connect(self.ui.actionNew, QtCore.SIGNAL("triggered()"), self.new_select)
...
def normal_output_written(self, qprocess):
self.ui.text_edit.append("caught outputReady signal") #works
self.ui.text_edit.append(str(qprocess.readAllStandardOutput())) # doesn't work
def new_select(self):
...
dialog_np = NewProjectDialog()
dialog_np.exec_()
if dialog_np.is_OK:
section = dialog_np.get_section()
project = dialog_np.get_project()
...
np = NewProject()
np.outputReady.connect(lambda: self.normal_output_written(np.qprocess))
np.errorReady.connect(lambda: self.error_output_written(np.qprocess))
np.inputNeeded.connect(lambda: self.input_from_line_edit(np.qprocess))
np.params = partial(np.create_new_project, section, project, otherargs)
np.start()
class NewProject(QtCore.QThread):
outputReady = QtCore.pyqtSignal(object)
errorReady = QtCore.pyqtSignal(object)
inputNeeded = QtCore.pyqtSignal(object)
params = None
message = ""
def __init__(self):
super(NewProject, self).__init__()
self.qprocess = QtCore.QProcess()
self.qprocess.moveToThread(self)
self._inputQueue = Queue()
def run(self):
self.params()
def create_new_project(self, section, project, otherargs):
...
# PyDev for some reason skips the breakpoints inside the thread
self.qprocess.start(command)
self.qprocess.waitForReadyRead()
self.outputReady.emit(self.qprocess) # works - I'm getting signal in RunGui.normal_output_written()
print(str(self.qprocess.readAllStandardOutput())) # prints empty line
.... # other actions inside the method requiring "command" to finish properly.
The idea is beaten to death - get the GUI to run scripts and communicate with the processes. The challenge in this particular example is that the script started in QProcess as command runs an app, that requires user input (confirmation) along the way. Therefore I have to be able to start the script, get all output and parse it, wait for the question to appear in the output and then communicate back the answer, allow it to finish and only then to proceed further with other actions inside create_new_project()
I don't know if this will fix your overall issue, but there are a few design issues I see here.
You are passing around the qprocess between threads instead of just emitting your custom signals with the results of the qprocess
You are using class-level attributes that should probably be instance attributes
Technically you don't even need the QProcess, since you are running it in your thread and actively using blocking calls. It could easily be a subprocess.Popen...but anyways, I might suggest changes like this:
class RunGui (QtGui.QMainWindow)
...
def normal_output_written(self, msg):
self.ui.text_edit.append(msg)
def new_select(self):
...
np = NewProject()
np.outputReady.connect(self.normal_output_written)
np.params = partial(np.create_new_project, section, project, otherargs)
np.start()
class NewProject(QtCore.QThread):
outputReady = QtCore.pyqtSignal(object)
errorReady = QtCore.pyqtSignal(object)
inputNeeded = QtCore.pyqtSignal(object)
def __init__(self):
super(NewProject, self).__init__()
self._inputQueue = Queue()
self.params = None
def run(self):
self.params()
def create_new_project(self, section, project, otherargs):
...
qprocess = QtCore.QProcess()
qprocess.start(command)
if not qprocess.waitForStarted():
# handle a failed command here
return
if not qprocess.waitForReadyRead():
# handle a timeout or error here
return
msg = str(self.qprocess.readAllStandardOutput())
self.outputReady.emit(msg)
Don't pass around the QProcess. Just emit the data. And create it from within the threads method so that it is automatically owned by that thread. Your outside classes should really not have any knowledge of that QProcess object. It doesn't even need to be a member attribute since its only needed during the operation.
Also make sure you are properly checking that your command both successfully started, and is running and outputting data.
Update
To clarify some problems you might be having (per the comments), I wanted to suggest that QProcess might not be the best option if you need to have interactive control with processes that expect periodic user input. It should work find for running scripts that just produce output from start to finish, though really using subprocess would be much easier. For scripts that need user input over time, your best bet may be to use pexpect. It allows you to spawn a process, and then watch for various patterns that you know will indicate the need for input:
foo.py
import time
i = raw_input("Please enter something: ")
print "Output:", i
time.sleep(.1)
print "Another line"
time.sleep(.1)
print "Done"
test.py
import pexpect
import time
child = pexpect.spawn("python foo.py")
child.setecho(False)
ret = -1
while ret < 0:
time.sleep(.05)
ret = child.expect("Please enter something: ")
child.sendline('FOO')
while True:
line = child.readline()
if not line:
break
print line.strip()
# Output: FOO
# Another line
# Done