im trying to load some files from folders and i want to show the progress of it with progress bar. At this point i tried multiprocessing, timers, threads and nothing is working... I dont know why my code is not working.
after run this code it shows me basic window but after line where i want to compare files, which is heavy operation, but im calling refresh of prograss bar here and in my console i see that it is working but the window with progress bar is frozen the whole time and never refresh :(( (Not responding)
#here is box with progress bar
self.ex[0]=showLoadingBox(len(files[1]))
#this is the heavy operation that i want to show the progress
duplsCompare=self.comPictures(files, sett.qualityMeasure)
#---------------------------------------------------------------------------
def comPictures(self,ignSource, prepQ):
dp = []
ret = []
source = ignSource[1]
for index, item in enumerate(source):
#here im trying to refresh progress bar
reload(self.ex[0],index+1)
for p in source[index + 1:]:
#do some stuff with files...
#return comparedFiles
# --------------------------------------------------------------------------------------------reloading method
def reload(self,load):
if(not(self.count==0)):
val=int((load/self.count)*100)
else:
val=100
self.pBar.setValue(val)
print(val)
if(not(load<self.count)):
self.close()
After end of comparing the window start working but that worthless for me i need that prograss bar show the prograss of comparing.
UPDATE:
so i use threading module but still no progress here literally. Window with progress bar just frozen and show me no progress and not reponding still the same problem even with different thread...
retComPic=[]
t=Thread(target=self.comPictures,args=(files, sett.qualityMeasure,retComPic))
t.daemon = True
t.start()
while(not(len(retComPic))):
reload(self.ex[0])
t.join()
# self.ex[0].close()
duplsCompare=retComPic[0]
The result is the same percentage right in the console but progress window frozen
Related
I am trying to get a popup for my pygame screen using tkinter, but i just want a simple messagebox that i pass in the message, and message type (like: "error"). What i don't know how to do is make it so that they can't avoid not answering it, if they click somewhere else it will not let the user do anything till they answer it, not even go to desktop sort of thing.
what i have so far:
def popUp(self, message, messagetype='Error'):
#Tk().wm_withdraw() #to hide the main window
messagebox.showinfo(messagetype, message)
For this to work your games's mainloop must be in a function (let's call it play). Let's say you have very simple code.
When a condition is met you can access the new function popUp. Which can have your tkinter window. When the button is pressed you can have its command as ...command=play) if the player wants to restart. As the popUp function is not inside the mainloop the game will be unresponsive. An example:
def play():
while carryOn:
for event in pygame.event.get():
if event.type==pygame.QUIT:
carryOn=False
#Checking if a key is pressed and then responding e=with function in the sprite class
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.moveLeft(5)
if keys[pygame.K_RIGHT]:
player.moveRight(5)
if keys[pygame.K_UP]:
player.moveUp(5)
if keys[pygame.K_DOWN]:
player.moveDown(5)
if keys[pygame.K_a]:
player.moveLeft(5)
if keys[pygame.K_d]:
player.moveRight(5)
if keys[pygame.K_w]:
player.moveUp(5)
if keys[pygame.K_s]:
player.moveDown(5)
#yourcondition
# if ... :
# carryOn = False
# popUp()
#Game Logic
all_sprites_list.update()
#Setting the background
screen.fill(GREEN)
#Now let's draw all the sprites
all_sprites_list.draw(screen)
#Refresh Screen
pygame.display.flip()
#Number of frames per second
clock.tick(60)
This is not full code - missing sprite class and code before that. You could then have your tkinter window (after importing tkinter) like:
global window #so it can be destroyed in the other function
window = Tk()
#message = ...
#message.grid ...
button = Button(window, text="restart", width=5, command=play)
button.grid # where you want it
The game will be frozen until the user presses restart (you may also want a window .destroy() in your code somewhere. You can adapt this to pretty much whatever you want but it is no where close to complete code. I hope this was useful.
In my application I have multiple windows that change based on events(one close and another open) and show only one window at a time. During one window close and another open its take some time since fetch data from database and prepare for window.
Here problem is that during the time of one window close and another open user can see and feel that one is being open and another is being close by seeing the background.
What I want, until second screen is not fully loaded, first window be visible on the screen.
My current code is something like,
import PySimpleGUI as sg
layout = [[sg.Button('Users', key='show_user_list')]]
window = sg.Window('users').Layout(layout)
while True:
event, values = window.Read()
if event == 'show_user_list':
window.Close()
# code ommited here for simplicity
# do mysql stuff to fetch data
# layout2 = ...
# window2 = sg.Window('user listing').Layout(layout2)
# while True:
# event, values = window2.Read()
# ...
# like that I have multiple windows
else:
pass
How I can give users feel like the window content is changing not one window closes and another opens?
Sure, you can make sure the background is not seen by first opening your Window 2, which will be created on top of Window 1, THEN closing your Window 1.
To do this, add a .Finalize() onto the Window 2 creation. This will cause the window to immediate show up. Then on the next line, close Window 1.
import PySimpleGUI as sg
layout = [[sg.Button('Users', key='show_user_list')]]
window = sg.Window('users').Layout(layout)
while True:
event, values = window.Read()
if event == 'show_user_list':
# code ommited here for simplicity
# do mysql stuff to fetch data
# layout2 = ...
# window2 = sg.Window('user listing').Layout(layout2).Finalize()
# window.Close()
# while True:
# event, values = window2.Read()
# ...
# like that I have multiple windows
else:
pass
The key to making this kind of window update work is to create the windows at the same location. The default is to make windows that are centered on the screen. This means if your windows are not the same size then you'll likely notice a small "blip" as you change from one to the other. But it shouldn't look bad because it'll happen so quickly.
If you really want to get fancy, you can add another step which will make the switch between the windows even smoother. This new step involves creating window 2 with Alpha=0, meaning that it's invisible, then after it's fully formed (using Finalize()) you change the Alpha to 1 which will make the window appear.
import PySimpleGUI as sg
layout = [[sg.Text('Example of window-replacement')],
[sg.Combo(['abdeffg', 'rrrfwwew'], size=(10, 4))],
[sg.B('Enable Filter'), sg.B('Warning'), sg.B('Reopen')],]
window = sg.Window('My Text Editor', layout)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
print(event, values)
if event == 'Reopen':
layout2 = [[sg.Text('This is a completely different window')],
[sg.Combo(['abdeffg', 'rrrfwwew'], size=(10, 4))],
[sg.B('Enable Filter'), sg.B('Warning'), sg.B('Reopen')], ]
window2 = sg.Window('My Text Editor', layout2, alpha_channel=0).Finalize()
window2.SetAlpha(1)
window.Close()
window = window2
window.Close()
This removed some of the "painting" of the window that I was seeing. That shouldn't happen because I use this same trick when creating the window to begin with. Alpha is used to hide the window while it's being created.
I have a GUI created in Python 3.x using tkinter from Visual Studio 2017 Community. A top widget is created and within it a frame and listbox are added. When the user clicks on an entry in the listbox the index of the selection is passed to twoSecondFunction() which takes around 2 seconds to complete.
How can I stop the user from making another selection during this time?
I have tried using the line myListbox.config(state=tkinter.DISABLED) to disable the listbox when a selection has been made and only enabling the listbox again once the twoSecondFunction() has completed.
When I run the code from Visual Studio myListbox click events are handled by myListboxSelection and 'twoSecondFunction()' and print() are called. The window appears to be unresponsive for the duration of the myListboxSelection().
if the user clicks on another entry before
Function finished
has been output myListboxSelection() is called another time.
I can queue over 10 commands by clicking quickly before "Function finished" is output. The GUI appears unresponsive for the duration of all queued events and only responds to other commands after executing all the queued myListboxSelection() calls.
I have tried removing the line myListbox.config(state=tkinter.NORMAL) and this then registers only one click for the duration of the program, so myListbox.config(state=tkinter.DISABLED) is working as it should when it is called. myListbox also greys out.
I have also added extra print() lines throughout the program to ensure all operations happen in the expected order which they do.
It seems as though the logic executes a lot more quickly than the GUI itself responds, so myListbox becomes enabled far more quickly than the GUI responds. I do not see myListbox grey out during the execution of 'twoSecondFunction()'.
Is it because the myListbox.config(state=tkinter.DISABLED) only takes effect after the event handler completes its execution? I.e. myListbox.config(state=tkinter.DISABLED) never takes effect because myListbox.config(state=tkinter.NORMAL) is set before myListbox is actually disabled?
import tkinter #For the GUI tools
#Event handler for mouse click of myListbox
def myListboxSelection(event):
myListbox.config(state=tkinter.DISABLED) #Disable myListbox
myListboxIndex = int(myListbox.curselection()[0]) #Get selection index
twoSecondFunction(myListboxIndex) #Call function that takes 2 seconds
print("Function finished") #Output to console on completion
myListbox.config(state=tkinter.NORMAL) #Enable Listbox
#Create GUI
GUITopWidget = tkinter.Tk(screenName = "myListboxGUI") #Create Top Level Widget
myFrame = tkinter.Frame(GUITopWidget, name = "myFrame") #Create frame
myFrame.pack() #Pass to Geometry Manager
myListbox = tkinter.Listbox(authoritiesListFrame) #Create myListbox
myListbox.bind('<<ListboxSelect>>', myListboxSelection) #Bind mouse click event
populateListbox(myListbox) #Add entries to the listbox
myListbox.pack() #Pass to Geometry Manager
#Run the GUI loop
GUITopWidget.mainloop()
It appears that if you have an event handler and use mainloop() it does indeed stop any logic from executing during the callback. Instead define a loop which can handle the event calls, other logic and call update() on the GUI manually:
import tkinter #For the GUI tools
rootWindowClosed = False
requestQueued = False #True request is queued
myListboxIndex= -1 #Set to -1 between requests
#Event handler for mouse click of myListbox
def myListboxSelection(event):
myListbox.config(state=tkinter.DISABLED) #Disable myListbox
myListboxIndex = int(myListbox.curselection()[0]) #Get selection index
#On closing of root window
def closeGUIHandler():
#Set the rootWindowClosed flag to True
global rootWindowClosed
rootWindowClosed = True
#Destroy GUI Root
GUITopWidget.destroy()
#Create GUI
GUITopWidget = tkinter.Tk(screenName = "myListboxGUI") #Create Top Level Widget
GUITopWidget.protocol("WM_DELETE_WINDOW", closeGUIHandler) #Set delete window protocol
myFrame = tkinter.Frame(GUITopWidget, name = "myFrame") #Create frame
myFrame.pack() #Pass to Geometry Manager
myListbox = tkinter.Listbox(authoritiesListFrame) #Create myListbox
myListbox.bind('<<ListboxSelect>>', myListboxSelection) #Bind mouse click event
populateListbox(myListbox) #Add entries to the listbox
myListbox.pack() #Pass to Geometry Manager
#Run the GUI loop
while rootWindowClosed == False:
#If requestQueued then execute queued request
if requestQueued == True:
twoSecondFunction(myListboxIndex) #Call function that takes 2 seconds
myListbox.config(state=tkinter.NORMAL) #Enable Listbox
myListboxIndex = -1
#Update GUI window if not closed
time.sleep(0.05) #Sleep for 50 ms to allow event handling before executing code
if rootWindowClosed == False:
GUIRootWidget.update()
That's the basic idea.
I couldn't understand them while reading about all this in python's documentation. There's simply too much of it to understand it.
Could someone explain me how to get a working progress bar with a multi-threaded(or multi-process) client?
Or is there any other way to "update" progress bar without locking program's GUI?
Also I did read something about "I/O" errors when these types of clients try to access a file at the same time & X-server errors when an application doesn't call multi-thread libs correctly. How do I avoid 'em?
This time I didn't write code, I don't want to end with a zombie-process or something like that (And then force PC to shutdown, I would hate to corrupt precious data...). I need to understand what am I doing first!
Something like the below might get you started? - Ive tried to annotate as much as possible to explain the process.
from Tkinter import *
from Queue import Queue
import ttk, threading
import time
queue = Queue()
root = Tk()
# Function to do 'stuff' and place object in queue for later #
def foo():
# sleep to demonstrate thread doing work #
time.sleep(5)
obj = [x for x in range(0,10)]
queue.put(obj)
# Create thread object, targeting function to do 'stuff' #
thread1 = threading.Thread(target=foo, args=())
# Function to check state of thread1 and to update progressbar #
def progress(thread, queue):
# starts thread #
thread.start()
# defines indeterminate progress bar (used while thread is alive) #
pb1 = ttk.Progressbar(root, orient='horizontal', mode='indeterminate')
# defines determinate progress bar (used when thread is dead) #
pb2 = ttk.Progressbar(root, orient='horizontal', mode='determinate')
pb2['value'] = 100
# places and starts progress bar #
pb1.pack()
pb1.start()
# checks whether thread is alive #
while thread.is_alive():
root.update()
pass
# once thread is no longer active, remove pb1 and place the '100%' progress bar #
pb1.destroy()
pb2.pack()
# retrieves object from queue #
work = queue.get()
return work
work = progress(thread1, queue)
root.mainloop()
Hope this helps. Let me know what you think!
What I want to do is colour in a single pixel in the centre of the screen, then at random choose an adjacent pixel and colour it in, and then repeat until some condition is met - anything such as time, or the screen is full, or after a certain number of pixels are full. This ending isn't too important, I haven't got that far yet, and I think I could manage to work that out myself.
I have no experience with tkinter, but I decided it was the best way to display this, since I don't really no any other way. Some of this code (mainly the tkinter functions like Canvas, PhotoImage etc) is therefore copy-pasted (and slightly edited) from examples I found here.
What my code does when run is hard to tell - it uses the CPU as much as it can seemingly indefinitely, and slowly increases its memory usage, but doesn't appear to do anything. No window opens, and the IDLE interpreter goes to a blank line, as usual when calculating something. When killed, the window opens, and displays a white page with a little black blob in the bottom right corner - as if the program had done what it was meant to, but without showing it happening, and starting in the wrong place.
So:
Why does it do this?
What should I do to make my program work?
What would be a better way of coding this, changing as many things as you like (ie. no tkinter, a different algorithm etc)?
from tkinter import Tk, Canvas, PhotoImage, mainloop
from random import randrange
from time import sleep
def choose_pixel(pixel_list):
possible_pixels = []
for x in pixel_list:
#adjacent pixels to existing ones
a = [x[0] + 1, x[1]]
b = [x[0] - 1, x[1]]
c = [x[0], x[1] + 1]
d = [x[0], x[1] - 1]
#if a not in pixel_list:
possible_pixels.append(a)
#if b not in pixel_list:
possible_pixels.append(b)
#if c not in pixel_list:
possible_pixels.append(c)
#if d not in pixel_list:
possible_pixels.append(d)
pixel_choosing = randrange(len(possible_pixels))
final_choice = possible_pixels[pixel_choosing]
return final_choice
def update_image(img_name, pixel):
img.put("#000000", (pixel[0], pixel[1]))
WIDTH, HEIGHT = 320, 240
window = Tk()
#create white background image
canvas = Canvas(window, width=WIDTH, height=HEIGHT, bg="#ffffff")
canvas.pack()
img = PhotoImage(width=WIDTH, height=HEIGHT)
canvas.create_image((WIDTH, HEIGHT), image=img, state="normal")
first_pixel = [int(WIDTH/2), int(HEIGHT/2)]
pixel_list = [first_pixel]
img.put("#000000", (first_pixel[0], first_pixel[1]))
canvas.pack()
runs = 0
while True:
next_pixel = choose_pixel(pixel_list)
pixel_list.append(next_pixel)
window.after(0, update_image, img, next_pixel)
canvas.pack()
runs+=1
window.mainloop()
The pattern for running something periodically in tkinter is to write a function that does whatever you want it to do, and then the last thing it does is use after to call itself again in the future. It looks something like this:
import tkinter as tk
...
class Example(...):
def __init__(self, ...):
...
self.canvas = tk.Canvas(...)
self.delay = 100 # 100ms equals ten times a second
...
# draw the first thing
self.draw_something()
def draw_something(self):
<put your code to draw one thing here>
self.canvas.after(self.delay, self.draw_something)
After the function draws something, it schedules itself to run again in the future. The delay defines approximately how long to wait before the next call. The smaller the number, the faster it runs but the more CPU it uses. This works, because between the time after is called and the time elapses, the event loop (mainloop) is free to handle other events such as screen redraws.
While you may think this looks like recursion, it isn't since it's not making a recursive call. It's merely adding a job to a queue that the mainloop periodically checks.