Tkinter lag when file encrypting - python

I have made a simple encryption app, here's the code - https://codeshare.io/ZJ7qJn
But when I press encrypt my tkinter app lags and says Not Responding and so I can't press anything within the tkinter app, but it does complete the encryption process.
Is there any way to make it lag-free?

Try this:
In function encfile replace the call to fiencdone() with:
root.event_generate("<<encryption_done>>")
Add the following new function definitions after the definition of function encfile:
def run_encfile():
from threading import Thread
root.bind('<<encryption_done>>', encryption_done)
Thread(target=encfile).start()
def encryption_done(*args):
fiencdone()
Finally, change line 75 to invoke run_encfile instead of encfile:
file_enc_button = tk.Button(fibutton_frame, text='Encrypt',font='Raleway 15 bold', width=15,command=run_encfile, borderwidth=3)
This will run the encryption in a separate thread but have the call to fiencdone signaling that the encryption is complete done in the main thread, which is required since it updates the GUI.

Related

Enable multithreading/processes in tkinter GUI

My tkinter application has a function which has a time.sleep() function in it so anytime I run the function that has the time.sleep() function, the whole application freezes until seconds specified in the time.sleep() function has elapsed. This can be a very bad User experience and I need to fix this.
The function that has the time.sleep() function is a call back function from a button so I tried doing something like this:
from tkinter import *
import threading
root = Tk()
schedule_msg_btn = Button(root, text="Schedule Msg", command=lambda : threading.Thread(target=schedule_msg_func).start())
root.mainloop()
It seems to be doing absolutely nothing.

Which code does the mainloop() processes infinitely until any event occurs?

To understand my question kindly follow the paragraphs written below:
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
consider the code:
from tkinter import *
window = Tk()
print("lol")
print("Hello World")
window.mainloop()
the output didn't print "Hello World" or "lol" infinite number of times, so the mainloop() doesn't loop the code of the current module.
Now consider this code:
from tkinter import *
print("lol")
window = Tk()
print("Hello World")
while True:
window.update()
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
Now the first question arises what does the window.update() function do to update the values in the GUI, does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
The second question is :
I read this article
"Mainloop in Python Tkinter is an infinite loop of the application window which runs forever so that we can see the still screen.
The application window is like a frame that keeps on destroying every microsecond but the main loop keeps on creating a new updated window.
This process of destroying old window screens and creating a new one happens so fast that human eyes don’t even realize it.
Since the process runs infinite time that is why we are able to see the application in front of us and when we close the window then the loop terminates or exits."
Now if this is true then to recreate an updated window the root.mainloop() must read the entire root GUI code again and again entirely or is there another explanation to it.
I have been trying to understand this for the past 6hrs and I have visited every site and I cannot find the solution for the life of me.
Regards,
Rashik
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
No.
Via this function, it calls this C code which has the embedded Tcl interpreter process one event, or wait for Tkinter_busywaitinterval before trying to process another event
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
window.update() calls TCL update, which is described to
[...] bring the application “up to date” by entering the event loop repeatedly until all pending events (including idle callbacks) have been processed.
Your infinite loop doesn't have a sleep, so it's spinning your CPU as hard as possible to do practically nothing.
[...] Does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
It certainly doesn't re-read your code. It processes any pending widget updates, which may have happened by running e.g. window.text("...") in e.g. a click callback or an .after() timeout, etc.
I read this article [...]
That article seems wrong and/or at least over-simplifies things.
This simple example clock should clarify how things work:
import time
import tkinter as tk
root = tk.Tk()
text = tk.Label(root)
text.pack()
def tick():
text["text"] = time.ctime() # update `text` widget's content
root.after(1000, tick) # schedule for this function to be called after 1 second
if __name__ == '__main__':
tick() # call the `tick` function once before entering main loop
root.mainloop()

Killing a multiprocess pool using a tkinter button

I'm building an application that does thousands (possibly millions) of calculations based off of what the user inputs. Because these calculations can take a long time, I want to use Python's multiprocessing module. Here is my dilemma; I want to set up a tkinter window with a cancel button to stop the calculations running throughout the processors I set up using Pool. I tried using threads to allow the popup window to run, but some funny things happen when I run the code.
When I press the 'start' button, the Pool starts going as expected. However, my popup window does not show even though it is on its own thread. Even weirder, when I tell my IDE (PyCharm) to stop the program, the popup window shows and the calculations are still running until I either press the 'exit' button from the popup window, or kill the program altogether.
So my questions are: Why won't my popup window show even though it is on its own thread? Am I utilizing the multiprocessing module correctly? Or is the problem something totally different?
import tkinter as tk
from tkinter import ttk
import random
import threading
import time
from multiprocessing import Pool
def f(x):
print(x*x)
time.sleep(random.randrange(1,5)) # simulates long calculations
# Extra math stuff here...
def processor():
global calc
calc = Pool(4)
calc.map(f, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30])
print("Done")
def calculation_start():
p = threading.Thread(target=progress_window) # Thread to open a new window for the user to cancel a calculation
p.start()
processor() # Calls the function to run the calculations
def progress_window():
# This is the window that pops up to let a user cancel a calculation.
popup = tk.Toplevel()
endButton = ttk.Button(popup, text="End", command=lambda :(calc.terminate()))
endButton.pack()
master = tk.Tk()
startButton = ttk.Button(master, text="Start", command=calculation_start)
startButton.pack()
tk.mainloop()
EDIT:
I tried switching the processor function to a thread instead of the progress_window function.
def calculation_start():
p = threading.Thread(target=processor) # Thread for the function to run the calculations
p.start()
progress_window() # Open a new window for the user to cancel a calculation
My popup window appears and the 'end' button looks like it stops the processor when pressed, but it never continues past that point. It's like it's stuck at calc.map(f, [1,2,3,...] in the processor() function.
From the Pool.map documentation:
It blocks until the result is ready.
That means that, yes, the work is being done on other threads, but the main thread (the one calling map) will not execute anything else until the other threads are complete. You want to use map_async and (for a long-running, interactive process), provide a callback for what to do when the pool is finished (probably hide the pop-up).
If you want your program to simply show the pop-up then exit, you want to use the AsyncResult returned by map_async. Set up an idle handler using after_idle (See tkinter universal widget methods and remember you'll need to manually re-add the handler after every call). After each idle call, you could call either AsyncResult.ready() or AsyncResult.get(<timeout>) to see if the pool has completed. Once it's finished, you're safe to process the results and exit.

python get arrow keys from command line

i have a script which should interact with the users input (pressing the arrow keys), but i cannot get the keys. i tried raw_input and some other functions, but they didnt work. this is my sample code, how it should look like (running bool can be set to False in another function)
running = True
while running:
#if input == Arrow_UP:
# do_Sth
#elif ...
display()
time.sleep(1)
another question is, how can i call the display function only once every second, but react on the input immediately?
There are different situations:
If you use a graphical frontend such as TKinter or PyGame, you can bind an event to the arrow key and wait for this event.
Example in Tkinter taken from this answer:
from Tkinter import *
main = Tk()
def leftKey(event):
print "Left key pressed"
def rightKey(event):
print "Right key pressed"
frame = Frame(main, width=100, height=100)
main.bind('<Left>', leftKey)
main.bind('<Right>', rightKey)
frame.pack()
main.mainloop()
If your application stays in the terminal, consider using curses as described in this answer
Curses is designed for creating interfaces that run in terminal (under linux).
If you use curses, the content of the terminal will be cleared when you enter the application, and restored when you exit it. If you don't want this behavior, you can use a getch() wrapper, as described in this answer. Once you have initialized getch with getch = _Getch(), you can store the next input using key = getch()
As to how to call display() every second, it again depends on the situation, but if you work in a single process in a terminal, the process won't be able to call your display() function while it waits for an input. The solution is to use a different thread for the display() function, as in
import threading;
def display ():
threading.Timer(1., display).start ();
print "display"
display ()
Here display schedules itself one second in the future each time it is called. You can of course put some conditions around this call so that the process stops when some conditions are met, in your case when an input has been given. Refer to this answer for a more thoughout discussion.

close a window and continue execution in the other one?

I'm really lost...I open a window with two buttons, and when you click on the button called "REGISTER SOME KEY PRESSES" it runs the script called registerSomeKeyPresses.py, BUUUUT once finished I want to close that execution but keep the first window displaying...it's being impossible for me....
Please, i would reaaaally appreciate any help...
Thanks!
#!/usr/bin/env python
from Tkinter import *
import threading
v0 = Tk()
def finishApplication(): v0.destroy()
def registerSomeKeyPresses():
t = threading.Thread(target=execfile("registerSomeKeyPresses.py"))
t.start()
def waitAndRun(f): v0.after(200, f)
b1=Button(v0,text="TERMINAR APLICACION",command=lambda: finishApplication()).pack()
button_keyPresses=Button(v0,text="REGISTER SOME KEY PRESSES",command=lambda: waitAndRun(registerSomeKeyPresses())).pack()
v0.mainloop()
================ registerSomeKeyPresses.py ===========================
Do several things and last command:
io.quit()
When you destroy the instance of Tk, your programm will (and should) exit. If you want to create and destroy windows, create and destroy an instance of Toplevel while keeping the main window active. If you don't want to see the main window you can hide it.
Also, tkinter and threads don't mix very well. You cannot call any methods on any widgets from another thread. I've heard other people say you can call event_generate from another thread, but I think that's the only tkinter function you can call from another thread.
Edit 1
A second try as a response to your comment:
from Tkinter import *
from subprocess import call
import sys
t = Tk()
def click():
t.iconify()
try:
call([sys.executable, 'script.py'])
finally:
t.deiconify() # if it should close do t.quit() and t.destroy()
b = Button(t, command= click)
b.pack()
t.mainloop()
Old Version
What does that do?
================ registerSomeKeyPresses.py ===========================
v0.quit()
v0.destroy()
io.mainloop()
An other error is:
threading.Thread(target=execfile, args = ("registerSomeKeyPresses.py",))
if you really neeed a thread.
Do never mix tkinter mainloop things with threads. Threads can use event_generate - thats safe.

Categories