I am new to python and GUI programming as well.
I need to build a front-end GUI application and for the back - end application I have already built in python 2.7.
My question is, if I develop a front end application in PyQT, will I be able to integrate the python code in that (or) will the PYQT support python modules like socket, threading etc.?
PyQt doesn't handle the back-end code. It is used to set up hooks so that when a user interacts with the GUI, some code is launched in Python.
In other words, yes, you'll be able to implement things like threading.
Here's an example where I'm clicking a button which has a 'hook' to launch a process. In this case, I'm launching the process in another thread so that the GUI doesn't freeze up.
import sys
import time
import threading
import datetime as dt
# Import from qtpy if you're running Anaconda3
from qtpy import QtCore, QtGui, QtWidgets
# Import from PyQt5 otherwise
# from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.resize(500, 220)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(400, 180, 93, 28))
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(10, 10, 471, 141))
font = QtGui.QFont()
font.setPointSize(20)
self.label.setFont(font)
self.label.setAlignment(QtCore.Qt.AlignCenter)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtCore.QCoreApplication.translate("MainWindow", "MainWindow", None))
self.pushButton.setText(QtCore.QCoreApplication.translate("MainWindow", "Start", None))
self.label.setText(QtCore.QCoreApplication.translate("MainWindow", "", None))
def main():
# Continually update LineEdit with the time when the button is pressed
# We can do this without freezing the GUI by launching update_time()
# in another thread
ui.pushButton.clicked.connect(lambda: threading.Thread(target=update_time).start())
def update_time():
while True:
current_time_text = dt.datetime.now().strftime('%b %d %Y %I:%M:%S %p')
ui.label.setText(current_time_text)
time.sleep(.01)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
main()
sys.exit(app.exec_())
Related
My application, created with Pyinstaller, worked fine until I upgraded from High Sierra to Mojave. In order to demonstrate the issue I create the simple application.
Main window has only one button. When you press the button its text should be changed to "Please wait" for 10 seconds.
When I run this program as .py script, everything works fine, but after creating .app file with Pyinstaller it behaves differently. The text is not updated until you click anywhere outside of the window.
I tried to reinstall Pyinstaller, but the problem still exists.
from PyQt5 import QtCore, QtGui, QtWidgets
import time
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(303, 304)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 80, 300, 43))
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(80, 170, 113, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton.setDefault(True)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.pushButton.clicked.connect(self.click)
self.thread = Thread()
self.thread.finished.connect(lambda: self.pushButton.setEnabled(True))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Click me"))
def click(self):
if not self.thread.isRunning():
self.pushButton.setEnabled(False)
self.pushButton.setText("Please wait")
self.label.setText("The button below should display \n 'Please wait' for 10 seconds")
self.thread.start()
class Thread(QtCore.QThread):
def run(self):
time.sleep(10)
ui.pushButton.setEnabled(False)
ui.pushButton.setText("Click me")
ui.label.setText("")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I found an answer to my question. In order to solve this rendering issue you need to add the following line for a ui element, which needs to be updated. In my case it is required only if I need to run this application on macOS Mojave.
<element>.repaint()
For example:
def click(self):
self.pushButton.setEnabled(False)
self.pushButton.setText("Button is clicked...")
self.pushButton.repaint()
my error message is
AttributeError: type object 'MainWindow' has no attribute
'nameLineEdit'
i try use pyqt unittest check login name and password (cause my project is pyqt5)
but many unittest are for not gui python project
so i google a tutorial(the tutorial is for pyqt4 but i will need use pyqt5)
http://johnnado.com/pyqt-qtest-example/
below is my project source code
https://drive.google.com/file/d/1gxVzY2yg83_y5OmHYUprsljMoP5xhbLT/view?usp=sharing
and i record my screen
https://youtu.be/yiMUGphm6fk
about project
my simple pyqt5 project to practice unittest to check login status
i follow a pyqt4 project tutorial ,but not work the project include three scripts
first script(main.py)
# -*- coding: utf-8 -*-
"""
Module implementing MainWindow.
"""
import sys
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication
from Ui_main import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
"""
Class documentation goes here.
"""
def __init__(self, parent=None):
"""
Constructor
#param parent reference to the parent widget
#type QWidget
"""
super(MainWindow, self).__init__(parent)
self.setupUi(self)
#pyqtSlot()
def on_loginButton_clicked(self):
if self.nameLineEdit.text()=='admin' and self.passwordLineEdit.text()=='123456':
self.loginStatusLabel.setText('Success Login')
else:
self.loginStatusLabel.setText('Failed Login')
if __name__=="__main__":
app=QApplication(sys.argv)
Main=MainWindow()
Main.show()
sys.exit(app.exec_())
second script (test_login.py)
import sys
import unittest
from PyQt5 import QtCore
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtTest import QTest
import main
app = QApplication(sys.argv)
class test01(unittest.TestCase):
'''Test the margarita mixer GUI'''
def setUp(self):
print('inital ok')
def test_defaults(self):
self.nameLineEdit=main.MainWindow.nameLineEdit
self.passwordLineEdit=main.MainWindow.passwordLineEdit
loginButton = main.MainWindow.loginButton
self.assertEqual(self.nameLineEdit.text(), 'admin')
self.assertEqual(self.passwordLineEdit.text(), '123456')
QTest.mouseClick(loginButton, QtCore.Qt.LeftButton)
if __name__ == "__main__":
unittest.main()
third script(Ui_main)
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'F:\project\pyqt_unitest01\main.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(302, 157)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.loginButton = QtWidgets.QPushButton(self.centralWidget)
self.loginButton.setGeometry(QtCore.QRect(200, 100, 81, 41))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(16)
self.loginButton.setFont(font)
self.loginButton.setObjectName("loginButton")
self.nameLineEdit = QtWidgets.QLineEdit(self.centralWidget)
self.nameLineEdit.setGeometry(QtCore.QRect(110, 10, 181, 31))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(16)
self.nameLineEdit.setFont(font)
self.nameLineEdit.setObjectName("nameLineEdit")
self.passwordLineEdit = QtWidgets.QLineEdit(self.centralWidget)
self.passwordLineEdit.setGeometry(QtCore.QRect(110, 60, 181, 31))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(16)
self.passwordLineEdit.setFont(font)
self.passwordLineEdit.setObjectName("passwordLineEdit")
self.nameLabel = QtWidgets.QLabel(self.centralWidget)
self.nameLabel.setGeometry(QtCore.QRect(20, 10, 81, 31))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(16)
self.nameLabel.setFont(font)
self.nameLabel.setObjectName("nameLabel")
self.passwordLabel = QtWidgets.QLabel(self.centralWidget)
self.passwordLabel.setGeometry(QtCore.QRect(20, 60, 81, 31))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(16)
self.passwordLabel.setFont(font)
self.passwordLabel.setObjectName("passwordLabel")
self.loginStatusLabel = QtWidgets.QLabel(self.centralWidget)
self.loginStatusLabel.setGeometry(QtCore.QRect(20, 105, 141, 31))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(16)
self.loginStatusLabel.setFont(font)
self.loginStatusLabel.setObjectName("loginStatusLabel")
MainWindow.setCentralWidget(self.centralWidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.loginButton.setText(_translate("MainWindow", "login"))
self.nameLabel.setText(_translate("MainWindow", "name"))
self.passwordLabel.setText(_translate("MainWindow", "password"))
self.loginStatusLabel.setText(_translate("MainWindow", "login_status"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
how to correct use unittest to test pyqt5 project (or any better idea or framework)
thank you everybody!!
The problem that you show is the little knowledge about OOP, the classes are abstractions or molds, they only serve to create objects, and that is what you have not done, so one part of the solution is to create an object of the MainWindow class.
On the other hand the testing is done to verify the correct functioning, in your case the verification could not occur in the QLineEdits since for this to have text you must place it, the correct thing is to verify the loginStatusLabel since the GUI is the one that has control about it, that is, the user places the username and password, clicks and consequently the indicated label must change.
import sys
import unittest
from PyQt5 import QtCore, QtWidgets, QtTest
from main import MainWindow
app = QtWidgets.QApplication(sys.argv)
class test01(unittest.TestCase):
'''Test the margarita mixer GUI'''
def setUp(self):
self.form = MainWindow()
def test_defaults(self):
self.form.nameLineEdit.setText('admin')
self.form.passwordLineEdit.setText('123456')
QtTest.QTest.mouseClick(self.form.loginButton, QtCore.Qt.LeftButton)
self.assertEqual(self.form.loginStatusLabel.text(), 'Success Login')
if __name__ == "__main__":
unittest.main()
I'm looking for a better way to handle python UI "update" from QtDesigner without overwrite button event. The workflow I got now is:
Design UI layout in QtDesigner
convert .ui to .py by pyuic5
adding button event in .py file
excute .py to see window and button action
So if my UI keep changing the design, how do I keep all the button event I add into .py without being overwrite after convert? Thank you.
Answer my own question, what I found is have three python files. Main.py, CallUI.py and MainWindow.py. (Named as you want.)
So you can keep regenerate UI and override MainWindow.py without clear button event you wrote.
1.Main.py is the one to launch everything, name == "main". Call CAllUI.py's setup function.
#Main.py
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import CallUI
def setUp():
CallUI.setUpWindow()
raw_input()
if __name__ == "__main__":
setUp()
2.CallUI.py is the one to use "QtWidgets.QApplication(sys.argv)" to show UI and add button click functions.
#CallUI.py
import sys
from MainWindow import Ui_Dialog
from PyQt5 import QtCore, QtGui, QtWidgets
import os
class CallUI(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.setUpBtnconnect()
def setUpBtnconnect(self):
self.ui.pushButton.clicked.connect(self.myFunction)
def myFunction(self):
os.system("ipconfig")
raw_input()
def setUpWindow():
app = QtWidgets.QApplication(sys.argv)
nowWindow = CallUI()
nowWindow.show()
sys.exit(app.exec_())
3.MainWindow.py is the one you converted from pyuic5, it's describe all the UI layout.
#MainWindow.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("MainWindow")
Dialog.resize(466, 417)
self.centralwidget = QtWidgets.QWidget(Dialog)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(160, 260, 75, 23))
self.pushButton.setObjectName("pushButton")
self.menubar = QtWidgets.QMenuBar(Dialog)
self.menubar.setGeometry(QtCore.QRect(0, 0, 466, 21))
self.menubar.setObjectName("menubar")
self.statusbar = QtWidgets.QStatusBar(Dialog)
self.statusbar.setObjectName("statusbar")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
self.pushButton.setText(_translate("MainWindow", "PushButton"))
I have a PyQt5 app that need to write input for the subprocess to stop.
However it also kills my PyQt5 Mainwindow, if input button is used without using subprocess button first.
If i use subprocess button first, and then use input button , with self.bob.stdin.write("b") app stays open, but if i press input button first without pressing subprocess button self.bob.stdin.write("b") it kills my app and Mainwindow.
So why do self.bob.stdin.write("b") kill app ,and how do i stop it from killing my MainWindow, if i press input button first ?
The dilemma can be seen in this test code.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(308, 156)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.Button_2 = QtWidgets.QPushButton(self.centralwidget)
self.Button_2.setGeometry(QtCore.QRect(20, 40, 121, 71))
self.Button_2.setObjectName("subprocessButton_2")
self.Button_1 = QtWidgets.QPushButton(self.centralwidget)
self.Button_1.setGeometry(QtCore.QRect(170, 40, 121, 71))
self.Button_1.setObjectName("inputbutton")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
self.Button_2.setText(_translate("MainWindow", "subprocess"))
self.Button_1.setText(_translate("MainWindow", "input"))
self.Button_2.clicked.connect(self.sub)
self.Button_1.clicked.connect(self.input)
def sub(self):
import subprocess
from subprocess import Popen, PIPE
from subprocess import Popen, PIPE
self.bob = subprocess.Popen('cmd ',stdin=PIPE, shell=True)
def input(self):
self.bob.stdin.write("q")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Without understanding all your code, here's what I think your code converted into a script does:
import subprocess
from subprocess import Popen, PIPE
# sub button handler
bob = subprocess.Popen('echo',stdin=PIPE, shell=True)
# input button handler
bob.stdin.write(b"text")
I'll let you think about what hapens when you don't do step 1.
Last week I started to learn Python and I developed some command line apps. Now I would like to develop apps with GUI. I searched in internet and I found a project that fits my needs: Qt Project (http://qt-project.org) and PyQt (http://www.riverbankcomputing.com/software/pyqt/intro). I installed Qt 5.3.2 Open Source, SIP 4.16.4, PyQt5 5.3.2 on Mac OS X 10.10 and python 2.7.6. After some troubles on installing Qt and PyQt, finally I managed to make them work. If I open example projects from PyQt example folder the gui appears without any problems. So I created my GUI with Qt Creator and then I used pyuic5 to generate python code. This is what pyuic5 created (file name "myapp_auto.py"):
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/Users/andrea/Developer/Qt/mainwindow.ui'
#
# Created: Mon Nov 17 14:20:14 2014
# by: PyQt5 UI code generator 5.3.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(400, 300)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.ok = QtWidgets.QPushButton(self.centralWidget)
self.ok.setGeometry(QtCore.QRect(140, 120, 115, 32))
self.ok.setAccessibleName("")
self.ok.setObjectName("ok")
self.text = QtWidgets.QLabel(self.centralWidget)
self.text.setGeometry(QtCore.QRect(100, 70, 181, 16))
self.text.setAccessibleName("")
self.text.setAlignment(QtCore.Qt.AlignCenter)
self.text.setObjectName("text")
self.time = QtWidgets.QDateTimeEdit(self.centralWidget)
self.time.setGeometry(QtCore.QRect(100, 180, 194, 24))
self.time.setAccessibleName("")
self.time.setObjectName("time")
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 22))
self.menuBar.setObjectName("menuBar")
self.menuMyApp = QtWidgets.QMenu(self.menuBar)
self.menuMyApp.setObjectName("menuMyApp")
self.menuEdit = QtWidgets.QMenu(self.menuBar)
self.menuEdit.setObjectName("menuEdit")
MainWindow.setMenuBar(self.menuBar)
self.mainToolBar = QtWidgets.QToolBar(MainWindow)
self.mainToolBar.setObjectName("mainToolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
self.statusBar = QtWidgets.QStatusBar(MainWindow)
self.statusBar.setObjectName("statusBar")
MainWindow.setStatusBar(self.statusBar)
self.menuBar.addAction(self.menuMyApp.menuAction())
self.menuBar.addAction(self.menuEdit.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.ok.setText(_translate("MainWindow", "Ok"))
self.text.setText(_translate("MainWindow", "I\'m a GUI"))
self.menuMyApp.setTitle(_translate("MainWindow", "MyApp"))
self.menuEdit.setTitle(_translate("MainWindow", "Edit"))
After that I added a new python file where I put my code; this is what i wrote (file name "myapp.py"):
#!/usr/bin/env python
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from myapp_auto import Ui_MainWindow
import sys
import time
class MyApp(Ui_MainWindow):
parse_triggered = pyqtSignal()
def __init__(self, parent=None, name=None):
Ui_MainWindow.__init__(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = MyApp()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Then, I run myapp.py and I verified that all GUI elements appeared to be where they should be. Well...now arrive my issue: I tried to access with code the "time" element in MainWindow modifying init def thus:
def __init__(self, parent=None, name=None):
Ui_MainWindow.__init__(self)
# Set the date to now
now = QDateTime()
now.setTime_t(int(time.time()))
self.time.setDateTime(now)
But the compiler shows alway this error:
AttributeError: 'MyApp' object has no attribute 'time'
This happen even if I try to access any other element ("ok", "text").
Will surely be a stupid mistake but I just can not figure out where I went wrong.
Thank you all guys!
Have a good day,
Andrea
You're not far off.
The MyApp class needs to inherit QMainWindow, and you don't need to use the time module. Try something like this:
class MyApp(QMainWindow, Ui_MainWindow):
parse_triggered = pyqtSignal()
def __init__(self, parent=None, name=None):
super(MyApp, self).__init__(parent)
self.setupUi(self)
# Set the date to now
self.time.setDateTime(QDateTime.currentDateTime())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())