I wrote a script to download file. I tinker it using tkinter. My idea is to show a new window when download button is clicked and show the progress of the download in it. I create new window but problem is it will not show the window until the download is complete..
b2 = Button(text = "Image Download",font=("Raleway", 10),command = lambda: download('Image','.jpg'), width=20)
b2.pack()
when this execute the download() executes in download():
window = Toplevel(root)
window.geometry('600x350+305+220')
window.wm_title(TYPE + ' Download')
this is for creating new window. But it only shows when the download() finish executes
What should i do? Help me
You have not shown the code that downloads the data but it is clearly using a synchronous method. All windowing systems operate by processing events. When a window is created and event is generated to map it onto the screen and another to get it to draw on screen. If you do not allow the thread to process events then apparently nothing happens until you stop being busy.
To work around this you either need to use an asynchronous method of downloading the data or you can use a synchronous method but you must do this on another thread. The UI thread (the one you create your windows on) MUST keep processing events. This basically means never do anything that will take a long time. The Tkinter after() method is a common scheme to break up a long job into lots of small pieces that are posted as events to keep things working. In this case its probably simplest to create a worker thread to do the download and post progress updates to the UI thread regularly.
Related
I have a main process that does some stuff (e.g. analyzing data) and it runs alone just fine. I also want to make a simple GUI that displays the result of the main task using PyQT5. The idea is that the GUI should not interfere in the main process, that is, if I remove the GUI it shouldn't cause any issue to the main process.
The code of the main process is quite simple:
if __name__ == '__main__':
# initialize the object that performs the main task
tasker = Task()
# the graphical interface to visualize the result of tasker
gui = GUI(task) # GUI is a separate class that keeps a reference to tasker
# read the input data and do stuff on each new data instance
for f in listdir(inrepo):
data = read_new_data(f) # an utility function that reads new data from file
result = tasker.process(data) # tasker processes the new data and return some results
gui.update(data, result) # pass the data and result in the GUI to update it
The code of the GUI class is quite long so I only paste a few lines here, but the lines I skip are just to create widgets, nothing fancy (I didn't connect any event yet)
class GUI(QApplication):
def __init__(self, tasker):
"""Initialize the application"""
super().__init__([])
self.tasker = tasker
# define the main window
self.window = QWidget()
self.window.setWindowTitle('GUI')
... # layout and components etc.
# show stuff
self.window.show()
self.exec()
So I want the GUI to be completely independent from my main process. For example, if I don't need the GUI anymore, I can just comment out the 2 lines gui = GUI(task) and gui.update(data, result).
However, the problem is that starting the GUI blocks the entire process (I assume it's because of self.exec() in GUI.__init__, so my main process cannot proceed to loop over the data. Could you please show me how to make PyQT non-blocking? Is it even feasible?
Some options I have considered:
Threading: it seems more complicated than necessary for my use case and it may make referencing to the task instance difficult from a thread. All new updates to task should be reflected in the GUI. If I'm not mistaken, PyQT's application already runs on a thread. So multi-level threading may be troublesome.
Run the GUI from another Python process, communicated via shared folders: may incur high latency. Any new data and result from task should be immediately reflected in the GUI. Writing to file then reading from file then updating the GUI will cause some delay.
Perform the task in GUI: I could use some timeout event to read new data periodically and run task on them, but then everything depends heavily on the GUI, and I can't just comment it out if I don't need the GUI anymore.
Any suggestion is very much appreciated! Thank you very much!
Switching your program between GUI mode/console mode is often not as simple as commenting out some lines. PyQt in particular does not allow you to run a GUI loop from anything other than a main thread. Not all hope is lost, though - this simply means that you should decide as early as possible whether your program is going to run as a console application or as a GUI.
Instead of relying in commenting out code, you can create a "switch" of sorts inside your code that tells your code how to execute. One way to do this is to check the command-line arguments when you execute the code, e.g.:
import sys
if "--headless" in sys.argv[1:]: # checking the command-line arguments
run_code_without_gui()
else:
run_code_with_gui()
This way, executing your code as python mycode.py --headless will execute it without the GUI (through the run_code_without_gui function), while executing python mycode.py will run it as a GUI (through the run_code_with_gui function). (Although if you're actually going to parse command-line arguments, I recommend using the argparse library).
You can even keep the analysis code completely decoupled from the GUI code, so that you simply run a function from, say, analysis.py when executing without the GUI, and have the GUI call that exact same function when e.g. the user clicks on the "Analyze" button.
Another thing to note is that if your analysis code takes long to execute, it may inadvertently block the GUI. In this case, you should run the analysis code in a separate "worker" thread that spawns upon clicking the "Analyze" button, keeping the GUI responsive while it executes. This might be the way to go if you want the analysis to keep running alongside the GUI indefinitely - create the worker thread for the analysis at the same time that you display the GUI to the user.
I have a PySide GUI in which several buttons trigger a long processing function, which subsequently updates GUI elements. Pushing the button freezes the GUI for a while until the processing completes. Since it takes a while, I wanted to add a progress bar. However, a QProgressBar does not show its progress updates until the main thread is idle, so simply calling progressBar.setValue() inside the processing loop doesn't work - the progressBar will just sit at 0 and then jump to 100% when the processing finishes.
I specifically DON'T want the entire GUI to remain responsive - there are a lot of elements which affect each other, and allowing the user to change anything while the processing is running can get things in an invalid state.
I have tried:
1) Using QProgressBar.update()
(How to show QProgressBar smoothly?).
Unfortunately this shows the progressBar updates for only about the first 5 seconds, but then the "Waiting" cursor shows up and the progressBar stops updating until the processing finishes.
2) Using QtGui.QApplication.processEvents()
(QProgressBar not showing progress?)
This shows the progress bar nicely for the full processing duration, but it keeps the rest of the GUI responsive - which in my case means the ability to queue multiple instances of the processing, potentially getting certain GUI elements "out of sync".
I have not yet tried moving the processing loop into a separate thread (also recommended by the link in #2) since I would expect that to have the same effect where it would be possible to get the GUI out of synq.
The brute-force solution would be to explicitly disable all the buttons and text edits that have the potential to get the GUI out of synq, and then reenable once the processing completes. But I'm hoping for a clean solution, something I can call inside the processing loop - instead of QtGui.QApplication.processEvents() - which will update the progressbar only, rather than the entire GUI.
I am building an app that, when the user hits a 'run' button, generates a table of buttons.
Because this process takes a while, I want to add a popup or progress bar to alert the user that the function is running and not frozen. To do this I decided to create a popup and call my function using threading so that the screen will be updated when the function starts (as opposed to once it is done).
mythread = threading.Thread(target=run_function)
mythread.start()
The trouble is that when I call my function from the above code it works very strangely: the columns of my table are the wrong width, some of my buttons are arbitrarily empty, and others have the wrong fill color. To fix this, all I need to do is to remove the threading operation and simply call run_function()
Any idea why this is happening?
I am new to Python, so it is likely some dumb mistake, but I have no idea. What is different between a process running as a thread and its default operation?
Disclaimer: I haven't worked with Kivy.
Not every framework works well with multithreading.
But most of the GUI frameworks have an event loop which is responsible for managing user events (mouse clicks, keyboard) and queued drawing operations (widgets).
In your case if don't want the UI to be freezed, you should regularly give control to your framework's event loop.
I guess kivy.base.EventLoopBase.dispatch_input is what you need to call to show an added widget or to handle user events.
I have a function that needs to work between two different windows. It starts off working in one active window. I want it to work with that window until I call
self.Close(true)
This will successfully close the active window, but as far as I understand it, it also terminates any more execution statements I have inside the function. The execution statements that are after the Close call (and still in the same function) are ones I want to be applied to the newly active window.
How can I achieve this? Is there something other than Close I can call?
The usual way to go about this is to have a main wxPython program (i.e. a main window). When you call the long running process, you can hide the main window by calling the frame's Hide() method. When the long running process finishes, you can re-show the main window or you could instantiate a secondary frame and show that while keeping the main window hidden. The secondary frame can show the output from the long running process.
Another approach would be to hide the main frame during the long running process and then when the process finishes, you just update the main frame and re-show it. You could create a completely different panel for the frame and swap it out for the original to make it look like a completely different frame. This tutorial talks about doing this latter suggestion.
I'm trying to make a file downloader with a Tkinter GUI window, which will download a file using the following line:
urllib.urlretrieve(url = fileurl, filename = file, reporthook = progBar)
Progbar is a progress bar on my Tkinter GUI window. When I run my code on Windows, the download works fine and runs normally, as does the progress bar.
However, when I run the same code on a Mac, the download and progress bar will only progress if there is activity in the Tkinter GUI window. For example, if the window is in the background, the download will pause until the window is clicked on. Even then, when the Tkinter GUI window is not in the background, the download will only progress if I am doing something like moving the mouse around the screen or repeatedly pressing buttons on the keyboard, otherwise the download pauses again. It seems like it is timing out for some reason, and I'm not sure how to fix this or why it only happens on Mac and not windows.
If I put a print statement in the progBar method, the download also slows down.
I have also tried removing the reporthook argument from the call to urllib.urlretrieve, when I do this the download progresses fine.
My progBar method is as follows:
def progBar(blocks, blocksize, totalsize) :
global pb
bytesdownloaded = blocksize*blocks
mbdownloaded = bytesdownloaded/1024/1024
mbsize = float(blocksize)/float(totalsize)
totalsize = totalsize/1024/1024
percent = mbsize*100
global v
va.set("(" + str(mbdownloaded) + 'MB out of ' + str(totalsize) + 'MB)')
pb.step(percent)
I am using Python 2.7 if this helps.
Edit: for further information, the download (call to urllib.urlretrieve) is performed in a background thread, while the GUI window is meant to be the main thread. This may have something to do with the problem.
This is just a guess, but it's possible that the UI output command in the reporthook is hanging due to the fact that the event loop is (I'm guessing) paused for a program when it's in the background. I have vague recollection of the Tkinter UI model but one solution might be to set some global variable with your current progress in the hook, and have UI paint it separately - do not issue UI commands in the hook.