How to run a function as soon as GUI started? - python-3.x

I need to run a check function as soon as the tkinter GUI is available. I tried with the following code, but the Messagebox which comes up is unresponsive and I can't press the OK button.
import tkinter.messagebox as mbox
import tkinter
from tkinter import ttk
class MQ(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
if mycheck=True:
mbox.showinfo("Title","message")
...
...
if __name__ == '__main__':
root = tkinter.Tk()
MQ(root)
root.mainloop()

You can use after_idle to run something as soon as the GUI starts up, or you can use after to have it run after a brief amount of time. The two have slightly different behaviors with respect to whether the code runs before or after the root window is displayed (which might be platform dependent; I'm not sure)
import tkinter as tk
from tkinter import messagebox
def say_hello(root, message):
tk.messagebox.showinfo("Info", message)
root = tk.Tk()
root.after(1, say_hello, root, "Hello, world")
root.mainloop()

Related

why does importing a tk variable from main break this code?

I'm trying to organize my code into two separate modules - main, and new_window
main passes a tk object to new_window to populate it with a label and a button.
Pressing the button is supposed to close the window.
This is main:
import tkinter as tk
from new_window import *
#I create a tk object called "main_menu"
main_menu = tk.Tk()
#The tk object is passed to a class in another module to have its properties applied
main_menu_obj = MainMenu(main_menu)
#the main loop function is called as required by tk
main_menu.mainloop()
This is new_window:
import tkinter as tk
#to close the window, the tk variable created in main must is imported
from main import main_menu
class MainMenu:
#when this module receives the tk object from main, it adds a label and a button
def __init__(self, root):
print('main menu')
root.title('Main Menu')
Label_1 = tk.Label(root, text='Main Menu')
Button_1 = tk.Button(root, text='close',command=self.close)
Label_1.pack()
Button_1.pack()
#when the button is pressed, the tk object "main_menu" is destroyed
def close(self):
print('closing window')
main.main_menu.destroy()
When I run this, I receive: NameError: name 'MainMenu' is not defined. But I just defined this class in new_window. What mistake am I making? If I comment out "from main import main_menu" and comment out "main.main_menu.destroy()" it works great and prints "closing window" when I press the close button. (but obviously it doesn't close the window then).
It is circular import issue, i.e. A imports B and B imports A.
Actually you don't need to import main inside new_window because you have already passed the reference of the root window to MainMenu class. Therefore you can use this reference to close the root window.
Updated new_window.py:
import tkinter as tk
# don't need to import main here
class MainMenu:
#when this module receives the tk object from main, it adds a label and a button
def __init__(self, root):
# save argument root
self.root = root
print('main menu')
root.title('Main Menu')
Label_1 = tk.Label(root, text='Main Menu')
Button_1 = tk.Button(root, text='close',command=self.close)
Label_1.pack()
Button_1.pack()
#when the button is pressed, the tk object "main_menu" is destroyed
def close(self):
print('closing window')
self.root.destroy()

Why does the label show only the last set image? (PyQt5) [duplicate]

I have got this problem. I´m trying to set text on a lineEdit object on pyqt4, then wait for a few seconds and changing the text of the same lineEdit. For this I´m using the time.sleep() function given on the python Time module. But my problem is that instead of setting the text, then waiting and finally rewrite the text on the lineEdit, it just waits the time it´s supposed to sleep and only shows the final text. My code is as follows:
from PyQt4 import QtGui
from gui import *
class Ventana(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
import time
self.lineEdit.setText('Start')
time.sleep(2)
self.lineEdit.setText('Stop')
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())
You can't use time.sleep here because that freezes the GUI thread, so the GUI will be completely frozen during this time.
You should probably use a QTimer and use it's timeout signal to schedule a signal for deferred delivery, or it's singleShot method.
For example (adapted your code to make it run without dependencies):
from PyQt4 import QtGui, QtCore
class Ventana(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.lineEdit = QtGui.QLineEdit(self)
self.button = QtGui.QPushButton('clickme', self)
self.layout().addWidget(self.lineEdit)
self.layout().addWidget(self.button)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
self.lineEdit.setText('Start')
QtCore.QTimer.singleShot(2000, lambda: self.lineEdit.setText('End'))
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())
Also, take a look at the QThread sleep() function, it puts the current thread to sleep and allows other threads to run. https://doc.qt.io/qt-5/qthread.html#sleep
You can't use time.sleep here because that freezes the GUI thread, so the GUI will be completely frozen during this time.You can use QtTest module rather than time.sleep().
from PyQt4 import QtTest
QtTest.QTest.qWait(msecs)
So your code should look like:
from PyQt4 import QtGui,QtTest
from gui import *
class Ventana(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
import time
self.lineEdit.setText('Start')
QtTest.QTest.qWait(2000)
self.lineEdit.setText('Stop')
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())

Start the script in PyQt5 and python3 [duplicate]

I have created a form using PyQt4 which has a push button. On this push button I want to call another python script which looks like this:
File1.py:
import sys
from PyQt4 import QtCore, QtGui
from file1_ui import Ui_Form
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
File1_ui.py
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8("Form"))
Form.resize(400, 300)
self.pushButton = QtGui.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(120, 200, 95, 20))
self.pushButton.setObjectName(_fromUtf8("pushButton"))
self.retranslateUi(Form)
QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("clicked()")), Form.close)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("Form", "Close", None, QtGui.QApplication.UnicodeUTF8))
File2.py
import sys
from PyQt4 import Qt
from taurus.qt.qtgui.application import TaurusApplication
app = TaurusApplication(sys.argv)
panel = Qt.QWidget()
layout = Qt.QHBoxLayout()
panel.setLayout(layout)
from taurus.qt.qtgui.panel import TaurusForm
panel = TaurusForm()
model = [ 'test/i1/1/%s' % p for p in props ]
panel.setModel(model)
panel.show()
sys.exit(app.exec_())
File1_ui.py is created from the Qtdesigner and then I am using File1.py to execute it.So File2.py when executed alone opens up a panel and displays few attributes.I want this script to be called on the button click in the first form(file1.py) which I created using Qtdesigner.Could you let me know how I could achieve this functionality.Thanks.
You will need to make some modifications to File2.py to make the appropriate calls depending on whether it is running standalone or not. When you are launching the script via File1.py there will already be a QApplication instance with event loop running, so trying to create another and run its event loop will cause problems.
Firstly, move the core part of your script into its own function. This will allow you to easily call it from File1.py. You can then handle the case where the script is running standalone and needs to create a QApplication instance and start its event loop. (I am not familiar the the taurus library you are using, but you can probably substitute TaurusApplication for QtGui.QApplication)
File2.py:
import sys
from PyQt4 import QtCore, QtGui
def runscript():
panel = QtGui.QWidget()
layout = QtGui.QHBoxLayout(panel)
return panel # Must return reference or panel will be deleted upon return
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
panel = runscript()
panel.show()
sys.exit(app.exec_())
Assuming your files are in the same directory you can simply write import File2 and use File2.runscript() to run your code. You then just need to connect the function to your pushbuttons clicked() signal to run it. The only problem here is that the reference to the QWidget returned from the runscript() function will be lost (and the object deleted) if you connect directly to runscript(). For this reason I created a method launch_script() which saves a reference in MyForm.
File1.py:
import sys
from PyQt4 import QtCore, QtGui
from file1_ui import Ui_Form
import File2
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
# This is a bit of a hack.
self.ui.pushButton.clicked.connect(self.launch_script)
def launch_script(self):
self.panel = File2.runscript()
self.panel.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
I don't use Qt Designer, so I don't know the correct way to go about connecting the signal to launch_script(). The code I have written should work, but obviously violates OOP principles and is dependent on the name of the pushbutton widget assigned by the software.

Why second python (PyQt4) GUI window shuts down as soon as it opens? [duplicate]

I have written python pyqt code to open a new window with a label from another window on a button click. The issue is ,new window exits as soon as it opens.How do i fix this.
The code I wrote is
import sys
from PyQt4 import QtGui,QtCore
class Window(QtGui.QWidget):
def __init__(self):
super(Window,self).__init__()
self.btn=QtGui.QPushButton('button',self)
self.btn.clicked.connect(display)
self.show()
class display(QtGui.QWidget):
def __init__(self):
super(display,self).__init__()
self.lab=QtGui.QLabel()
self.lab.setText("hi")
self.show()
def main():
App=QtGui.QApplication(sys.argv)
Gui=Window()
sys.exit(App.exec_())
main()
You need to keep a reference to the QWidget object for your second window. Currently when you click the button, the clicked signal is fired and it calls disp1. That creates the widget, but then it is immediately garbage collected.
Instead do this to keep a reference:
import sys
from PyQt4 import QtGui,QtCore
class Window(QtGui.QWidget):
def __init__(self):
super(Window,self).__init__()
self.btn=QtGui.QPushButton('button',self)
self.btn.clicked.connect(self.open_new_window)
self.show()
def open_new_window(self):
# creates the window and saves a reference to it in self.second_window
self.second_window = disp1()
class displ(QtGui.QWidget):
def __init__(self):
super(displ,self).__init__()
self.lab=QtGui.QLabel()
self.lab.setText("hello")
self.show()
def main():
App=QtGui.QApplication(sys.argv)
Gui=Window()
sys.exit(App.exec_())
main()
When passing a function as parameter, maybe it's better not to include the parentheses? Try
sys.exit(App.exec_)
Instead of
sys.exit(App.exec_())

Python 3.5 tkinter confirmation box created progress bar and reads in csv not quite working

I'm realtively new to python and am making a GUI app that does a lot of file i/o and processing. To complete this i would like to get a confirmation box to pop-up when the user commits and actions. From this when clicking 'yes' the app then runs the i/o and displays a progress bar.
From other threads on here I have gotten as far as reading about the requirement to create an addtional thread to take on one of these processes (for example Tkinter: ProgressBar with indeterminate duration and Python Tkinter indeterminate progress bar not running have been very helpful).
However, I'm getting a little lost because I'm not activating the threaded process from the Main() function. So I'm still getting lost in how, and where, I should be creating the progress bar and passing of the i/o process to another thread (reading in a csv file here).
Here is my code and I would be very grateful for any help anyone can give me:
import tkinter as tk
import tkinter.messagebox as messagebox
import csv
import tkinter.ttk as ttk
import threading
class ReadIn(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Read in file and display progress")
self.pack(fill=tk.BOTH, expand=True)
self.TestBtn = tk.Button(self.parent, text="Do Something", command=lambda: self.confirm_pb())
self.TestBtn.pack()
def confirm_pb(self):
result = messagebox.askyesno("Confirm Action", "Are you sure you want to?")
if result:
self.handle_stuff()
def handle_stuff(self):
nf = threading.Thread(target=self.import_csv)
nf.start()
self.Pbar()
nf.join()
def Pbar(self):
self.popup = tk.Tk()
self.popup.title('Loading file')
self.label = tk.Label(self.popup, text="Please wait until the file is created")
self.progressbar = ttk.Progressbar(self.popup, orient=tk.HORIZONTAL, length=200,
mode='indeterminate')
self.progressbar.pack(padx=10, pady=10)
self.label.pack()
self.progressbar.start(50)
def import_csv(self):
print("Opening File")
with open('csv.csv', newline='') as inp_csv:
reader = csv.reader(inp_csv)
for i, row in enumerate(reader):
# write something to check it reading
print("Reading Row " + str(i))
def main():
root = tk.Tk() # create a Tk root window
App = ReadIn(root)
root.geometry('400x300+760+450')
App.mainloop() # starts the mainloop
if __name__ == '__main__':
main()
The statement nf.join() in function handle_stuff() will block tkinter's main loop to show the progress bar window. Try modify handle_stuff() as below:
def handle_stuff(self):
nf = threading.Thread(target=self.import_csv)
nf.start()
self.Pbar()
#nf.join() # don't call join() as it will block tkinter's mainloop()
while nf.is_alive():
self.update() # update the progress bar window
self.popup.destroy()

Resources