How to implement a "processing request"-message in TkInter - python-3.x

I'm trying to implement a popup-window, which should display over the main window, while an operation is taking place in the background. Pressing the button from the code below results in freezing the GUI for 10s without displaying any message and eventually making the button green. The freezing is normal, but I would like to have the popup displayed during the 10 seconds.
Any help would be appreciated! Thanks in advance!
import tkinter as tk
import time
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.button1 = tk.Button(text="Start", command=self.make_green)
self.button1.pack()
def popup(self):
tl = tk.Toplevel(self)
tl.transient()
tk.Label(tl, text="Painting green").pack()
tl.grab_set()
return tl
def make_green(self):
wait_popup = self.popup()
time.sleep(10)
self.button1.config(bg="green")
wait_popup.destroy()
a = GUI()
a.mainloop()

You can use self.update() in your code to make the popup window appear.
import tkinter as tk
import time
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.button1 = tk.Button(text="Start", command=self.make_green)
self.button1.pack()
def popup(self):
tl = tk.Toplevel(self)
tl.transient()
tk.Label(tl, text="Painting green").pack()
self.update()
tl.grab_set()
return tl
def make_green(self):
wait_popup = self.popup()
time.sleep(10)
self.button1.config(bg="green")
wait_popup.destroy()
a = GUI()
a.mainloop()
Or you can use threading.
Start by importing Thread from threading.
from threading import Thread
Then make a new method
def thread_it(self):
return Thread(target=self.make_green, daemon=True).start()
and update your button's command
self.button1 = tk.Button(text="Start", command=self.thread_it)

Related

tkinter state of button for multi-clicks

I have written a simple code. The button is disabled when process is running and it is enabled after the process finishes. It works as expected for the first time, but when I re-click the button (do not close the window), the button could not be disabled while the process is ok. Can anyone gives some hints.
import time
import tkinter as tk
import threading
win = tk.Tk()
class test(threading.Thread):
def __init__(self):
super(test, self).__init__()
def run(self):
print("start !!!")
time.sleep(3)
print("end !!!")
def monitor_state(thread):
if not thread.is_alive():
btn.configure(state=tk.NORMAL)
win.after(50, monitor_state, thread)
def py1_go():
btn.configure(state=tk.DISABLED)
test_thread = test()
test_thread.setDaemon(True)
test_thread.start()
monitor_state(test_thread)
btn = tk.Button(text='Display Results', command = py1_go)
btn.pack()
win.mainloop()
A Tkinter Button has three states : active, normal, disabled.
Snippet:
def run(self):
print("start !!!")
btn["state"] = "disabled"
time.sleep(3)
print("end !!!")
btn["state"] = "normal"

slot to right click menu action does does not work

I have written the below code to which I finally managed to add menu but connecitn menu to a function doesnt seem to work:
import os
from PyQt5 import uic
from PyQt5 import QtWidgets
from PyQt5 import QtCore
FILE_LOCATION = os.path.dirname(os.path.realpath(__file__))
class MainDialogWindow(QtWidgets.QDialog):
def __init__(self):
super(MainDialogWindow,self).__init__()
ui_file = os.path.join(FILE_LOCATION, "example.ui")
self._ui = uic.loadUi(ui_file, self)
self.registerCallbacks()
self.initUI()
def initUI(self):
"""Initialize the UI.
"""
self.textBrowser.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
def registerCallbacks(self):
self.textBrowser.customContextMenuRequested.connect(self.context_menu)
# self.connect(self.textBrowser, QtCore.Signal('customContextMenuRequested(const QPoint &)'), self.context_menu)
def context_menu(self, pos):
menu = QtWidgets.QMenu(self)
action = menu.addAction("clear")
menu.exec_(self.mapToGlobal(pos))
action.trigered.connect(self.clear)
def clear(self):
"""Slot to claer text.
"""
print("clear")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainDialogWindow()
window.show()
window.setGeometry(500, 300, 300, 300)
sys.exit(app.exec_())
please helpp,, I want call the clear function from the right click menu
I don't seem to understand how the menu.exec_() method works, that method blocks the execution of sequential tasks until the user selects a QAction from the QMenu. In your case, for example, until when you press "clear" and the triggered signal is emitted (note: you have a typo), but at that moment there is no connection, so the clear method will not be called. The solution is to make the connection before invoking the QMenu exec_():
def context_menu(self, pos):
menu = QtWidgets.QMenu(self)
action = menu.addAction("clear")
action.triggered.connect(self.clear)
menu.exec_(self.mapToGlobal(pos))

how do i stop the tkinter window being not interactable while a "computation heavy" function is running

i have a tkinter window that does has some widgeds that the user can interact with and when a function that takes a little bit of time to run the whole window cant be interacted with.and i want to change that
i heard you can solve this problem by implementing multiprocessing to the function but i dont really understand how to implement it.
my code is something like this :
import tkinter as tk
from tkinter import *
def functionthattakessometime():
while True:
print("haa")
root=tk.Tk()
b=Button(root,text="print haha",command=functionthattakessometime)
a=Button(root,text="do nothing")
b.pack()
root.mainloop()
You cant press the "do nothing" window after you pressed the "print haha" button and i want to change that
so you can press the "do nothing" button even after you pressed the "print haha" button.
The threading library is what you want here.
Here is a simple example of how it would work with your code.
import tkinter as tk
import threading
root = tk.Tk()
allow_print = True
def function_that_takes_sometime():
while allow_print:
print("haha")
def start_thread():
global allow_print
allow_print = True
thread = threading.Thread(target=function_that_takes_sometime)
thread.start()
def stop_thread():
global allow_print
allow_print = False
tk.Button(root, text="print haha", command=start_thread).pack()
tk.Button(root, text="Stop print", command=stop_thread).pack()
root.mainloop()
That said I would change a few things.
First I would remove from tkinter import * as you should never import tkinter twice and it is better to just use import tkinter as tk because this prevents us from overwriting any methods on accident.
Second I would build this in a class so we can avoid global variables.
Here is an OOP version:
import tkinter as tk
import threading
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.allow_print = True
tk.Button(self, text="print haha", command=self.start_thread).pack()
tk.Button(self, text="Stop print", command=self.stop_thread).pack()
def function_that_takes_sometime(self):
while self.allow_print:
print("haha")
def start_thread(self):
self.allow_print = True
thread = threading.Thread(target=self.function_that_takes_sometime)
thread.start()
def stop_thread(self):
self.allow_print = False
Main().mainloop()
And to simplify this further because often threading is overkill we can use the after() method to manage the loop and not affect the mainloop.
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.allow_print = True
tk.Button(self, text="print haha", command=self.function_that_takes_sometime).pack()
tk.Button(self, text="Stop print", command=self.stop_print).pack()
def function_that_takes_sometime(self):
if self.allow_print:
print("haha")
self.after(1000, self.function_that_takes_sometime)
def stop_print(self):
self.allow_print = False
Main().mainloop()

Unable to executing multiple functions using multiprocessing module from a TKinter GUI

Hi I have a small GUI which has a button using which two functions must be executed in different processors. In reality these two functions are heavy calculations. I do not want to use multi threading. I want them to run on 2 different processors. When I try to execute the button, another instance of the GUI gets created and it says
File "C:\Python3.7\lib\multiprocessing\reduction.py", line 60, in dump.
ForkingPickler(file, protocol).dump(obj)
TypeError: can't pickle _tkinter.tkapp objects
My code is as follows.
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
def F1(self):
print("Hello")
def F2(self):
print("World")
def execute(self):
self.P1 = Process(target = self.F1)
self.P2 = Process(target = self.F2)
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()
Please click here
The problem lies with pickling tkinter widgets. You simply cannot do it, as the Tcl interpreter does not understand the python pickle format.
Coming to your code, I tried the following and it prints as expected:
from multiprocessing import Process
from tkinter import Button, Tk, Frame
class GUI(Frame):
def __init__(self):
super().__init__()
self.button = Button(self, text="Start", command=self.execute)
self.button.pack()
self.pack()
#staticmethod
def F1():
print("Hello")
#staticmethod
def F2():
print("World")
def execute(self):
self.P1 = Process(target = GUI.F1())
self.P2 = Process(target = GUI.F2())
self.P1.start()
self.P2.start()
self.P1.join()
self.P2.join()
Root = Tk()
Software = GUI()
Root.mainloop()

PyQt keyPressEvent not triggered from QDialog

I have a simple example of of a dialog window that has the keyPressEvent method. However, no matter what is typed when the sub window has focus, the event is not triggered.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import PyQt4.Qt
class KpeWindow(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
main = QVBoxLayout(self)
label = QLabel(self)
label.setText('Test the keyPressEvent')
self.adjustSize()
self.setLayout(main)
def keyPressEvent(self, event):
QMessageBox.warning(self, 'MDI', 'keyPressEvent')
super().keyPressEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('KeyPressEvent Test')
child = KpeWindow()
self.setCentralWidget(child)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
The following code works:
class KpeWindow(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self,parent)
main = QVBoxLayout(self)
label = QLabel(self)
label.setText('Test the keyPressEvent')
main.addWidget(label)
self.adjustSize()
self.setLayout(main)
def keyPressEvent(self, event):
QMessageBox.warning(self, 'MDI', 'keyPressEvent')
self.parent().keyPressEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('KeyPressEvent Test')
main = QVBoxLayout(self)
child = KpeWindow(self)
child.setFocusPolicy(Qt.StrongFocus)
self.setFocusProxy(child)
main.addWidget(child)
child.setFocus(True)
self.adjustSize()
self.setLayout(main)
I am not sure which of my changes work, I suspect setFocusProxy. In general I would recommend using QWidget as the child, and putting things into layouts even when there are no siblings.
The keyPressEvent is sensitive to the focus policy. In your example, the event is going to the QMainWindow (if you move the keyPressEvent to there, it does receive key events).
Is there any reason to have a dialog within a window? If you launch the dialog in the usual way, using child.show(), child.exec_() instead of setCentralWidget, it shows in a separate window and captures the key event.

Resources