PyQt progress bar with QProgressDialog unable to reset - python

I have a simple progress bar that I want to pop up inside loops, like in the example below.
I have to instantiate it outside of the app, then adjust values for the loop. Why? I would've thought it'd be more ideal to instantiate a new progress bar class inside the MainWindow, but that doesn't show up. I assume it has to do with the app.exec. However, if I add a self.exec for the progress bar, it doesn't update, so what's the correct way of implementing it?
from PyQt5.Qt import *
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.centralwidget = QWidget(MainWindow)
self.pushButton = QPushButton(self.centralwidget)
self.pushButton.setText("CLICK ME")
MainWindow.setCentralWidget(self.centralwidget)
self.pushButton.clicked.connect(self.testloop)
def testloop(self):
n = 200
print(progressbar.wasCanceled())
progressbar.setMinimum(0)
progressbar.setValue(0)
progressbar.setMaximum(n)
progressbar.show()
for i in range(n):
progressbar.setValue(i)
if progressbar.wasCanceled(): # How can I un-cancel and reset the progress bar again?
break
class ProgressBar(QProgressDialog):
def __init__(self):
super().__init__()
self.setValue(0)
self.setModal(True)
def step(self, i):
self.setValue(i)
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(mainwindow)
mainwindow.show()
progressbar = ProgressBar() # Do I need to instantiate a progressbar here,
# or can I instantiate and kill it inside the app code?
sys.exit(app.exec_())

Simple implementation of progress bar for a loop:
from PyQt5.Qt import *
import sys
import time
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.centralwidget = QWidget(MainWindow)
self.pushButton = QPushButton(self.centralwidget)
self.pushButton.setText("CLICK ME")
MainWindow.setCentralWidget(self.centralwidget)
self.pushButton.clicked.connect(self.testloop)
def testloop(self):
n = 10
progressbar = ProgressBar(n, title = "Copying files...")
for i in range(n):
time.sleep(0.01)
progressbar.setValue(i)
if progressbar.wasCanceled():
break
class ProgressBar(QProgressDialog):
def __init__(self, max, title):
super().__init__()
self.setMinimumDuration(0) # Sets how long the loop should last before progress bar is shown (in miliseconds)
self.setWindowTitle(title)
self.setModal(True)
self.setValue(0)
self.setMinimum(0)
self.setMaximum(max)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(mainwindow)
mainwindow.show()
sys.exit(app.exec_())

Related

Need help understanding inheritance in PyQt5

I'm trying to organize my code into classes. In the example below, B and C need to inherit the names of the push buttons from A so that the buttons echo a statement when pushed. In order for me to get this to work properly, I had to create Dummy and inherit B and C and then created in instance of Dummy. It doesn't seem like this is the way I should be doing it. Is there a "better" or "more accepted" way of using multiple classes in PyQt5?
inheritance.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'inheritance.ui'
#
# Created by: PyQt5 UI code generator 5.9.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(452, 246)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.b_pushButton = QtWidgets.QPushButton(self.centralwidget)
self.b_pushButton.setGeometry(QtCore.QRect(40, 50, 151, 81))
self.b_pushButton.setObjectName("b_pushButton")
self.c_pushButton = QtWidgets.QPushButton(self.centralwidget)
self.c_pushButton.setGeometry(QtCore.QRect(230, 50, 151, 81))
self.c_pushButton.setObjectName("c_pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 452, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.b_pushButton.setText(_translate("MainWindow", "B"))
self.c_pushButton.setText(_translate("MainWindow", "C"))
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_())
Main script:
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from inheritance import Ui_MainWindow
class A(QMainWindow):
def __init__(self):
super(A, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
class B(A):
def __init__(self):
super(B, self).__init__()
self.ui.b_pushButton.clicked.connect(self.pushB)
def pushB(self):
print('You pushed the "B" button')
print()
class C(A):
def __init__(self):
super(C, self).__init__()
self.ui.c_pushButton.clicked.connect(self.pushC)
def pushC(self):
print('You pushed the "C" button')
print()
class Dummy(B, C):
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
main = Dummy()
main.show()
sys.exit(app.exec_())
I ended up doing what eyllanesc suggested. I was initially trying to keep everything related to B and C in their own class because my actual code is really long, but this will do.
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from inheritance import Ui_MainWindow
class A(QMainWindow):
def __init__(self):
super(A, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.B = B()
self.C = C()
self.ui.b_pushButton.clicked.connect(self.B.pushB)
self.ui.c_pushButton.clicked.connect(self.C.pushC)
class B:
def pushB(self):
print('You pushed the "B" button')
print()
class C:
def pushC(self):
print('You pushed the "C" button')
print()
if __name__ == "__main__":
app = QApplication(sys.argv)
main = A()
main.show()
sys.exit(app.exec_())
Here is the final code that does what I was trying to do - access the attributes from ui so I could access them in other classes.
I ended up passing the ui object from A into the __init__ of B and C. Not sure if this proper coding, but it works.
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from inheritance import Ui_MainWindow
class A(QMainWindow):
def __init__(self):
super(A, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.B = B(self.ui)
self.C = C(self.ui)
self.ui.b_pushButton.clicked.connect(self.B.pushB)
self.ui.c_pushButton.clicked.connect(self.C.pushC)
class B():
def __init__(self, ui):
self.ui = ui
def pushB(self):
if self.ui.b_checkBox.isChecked():
print('You pushed the "B" button')
print()
else:
print('Check the check box to print the correct statement.')
class C():
def __init__(self, ui):
self.ui = ui
def pushC(self):
if self.ui.c_checkBox.isChecked():
print('You pushed the "C" button')
print()
else:
print('Check the check box to print the correct statement.')
if __name__ == "__main__":
app = QApplication(sys.argv)
main = A()
main.show()
sys.exit(app.exec_())

Master PyQt5 GUI with pushbuttons that open additional GUI

Using Python3 and PyQt5, I want to have a GUI that has a button on it that, when pressed, opens another widget which also has buttons and other controls. In the future, the master GUI will have many buttons that will open many additional widgets. I'm looking at having one widget with an embedded matplotlib.pyplot. At the moment, I am struggling to open a second widget.
Main Program
import sys
from PyQt5 import QtWidgets
from gui import Ui_MainWindow as Ui_MainWindow1
from gui2 import Ui_MainWindow as Ui_MainWindow2
class Window(QtWidgets.QMainWindow, Ui_MainWindow1):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.ui_gui = Ui_MainWindow1()
self.ui_gui.setupUi(self)
self.pb.clicked.connect(self.on_pb_clicked)
self.graph = Graph(self)
def on_pb_clicked(self):
print('pb clicked')
self.graph.show()
class Graph(QtWidgets.QMainWindow, Ui_MainWindow2):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.ui_graph = Ui_MainWindow2()
self.ui_graph.setupUi(self)
if __name__ == "__main__":
#import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow1()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
gui = gui2. Both look like the below.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(282, 219)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.pb = QtWidgets.QPushButton(self.centralWidget)
self.pb.setGeometry(QtCore.QRect(100, 60, 75, 23))
self.pb.setObjectName("pb")
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QtWidgets.QMenuBar(MainWindow)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 282, 21))
self.menuBar.setObjectName("menuBar")
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.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pb.setText(_translate("MainWindow", "pb"))
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 want to click the button on gui and have the gui2 appear. NB gui will not equal gui2 in the future.
Since Window and Graph inherit from Ui_Window1 and Ui_Window2, respectively, you should call self.setupUi(self) in Window.__init__ and Graph.__init__ instead of creating separate instances of Ui_Window1 and Ui_Window2, i.e.
class Window(QtWidgets.QMainWindow, Ui_MainWindow1):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.setupUi(self)
self.pb.clicked.connect(self.on_pb_clicked)
self.graph = Graph(self)
# just to see the two windows side-by-side
self.move(500, 400)
self.graph.move(self.x()+self.width()+20, self.y())
def on_pb_clicked(self):
print('pb clicked')
self.graph.show()
class Graph(QtWidgets.QMainWindow, Ui_MainWindow2):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.setupUi(self)
The main part of the program should then be something like
if __name__ == "__main__":
#import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = Window()
MainWindow.show()
app.exec()
You use QMetaObject::connectSlotsByName() to enable the automatic calling of the on_pb_clicked() slot.
You do not need to use self.pb.clicked.connect(self.on_pb_clicked)
You need #QtCore.pyqtSlot()
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from gui_1 import Ui_MainWindow as Ui_MainWindow1
from gui_2 import Ui_MainWindow as Ui_MainWindow2
class Graph(QtWidgets.QMainWindow, Ui_MainWindow2):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.setupUi(self)
self.setWindowTitle("window Graph")
self.setGeometry(QtCore.QRect(850, 260, 282, 219))
# self.pb.clicked.connect(self.on_pb_clicked) # ---
#QtCore.pyqtSlot() # +++
def on_pb_clicked(self):
print('pb clicked -> Graph')
class Window(QtWidgets.QMainWindow, Ui_MainWindow1):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowTitle("main Window")
# self.pb.clicked.connect(self.on_pb_clicked) # ---
self.graph = Graph(self)
#QtCore.pyqtSlot() # +++
def on_pb_clicked(self):
print('pb clicked -> "main Window')
self.graph.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())

How to access a widget in a QMainWindow from a QDialog

Before posting my question I searched a lot about it and I found some questions that might be similar but they do not solve my problem. I believe it is quite easy but I don't know how:
below is a minimal example of the problem:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
#MainWindow.setObjectName("MainWindow")
MainWindow.setEnabled(True)
MainWindow.resize(574, 521)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.firstPushButton = QtWidgets.QPushButton(self.centralwidget)
self.firstLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.firstPushButton.clicked.connect(self.showDialog)
# the other stuff related to layout setup is ommited
def showDialog(self):
dialog = MyDialog(MainWindow)
dialog.exec()
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedSize(400, 200)
self.myButton = QtWidgets.QPushButton("Write something")
# When I click the myButton, I want it to change the text of MainWindow lineEdit
self.myButton.clicked.connect(self.writeHello)
def writeHello(self):
# How can I access firstLineEdit from MainWindow? I want to write "Hello" to the firstLineEdit
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.setWindowTitle("BEM Analysis for propellers")
MainWindow.show()
sys.exit(app.exec())
Could you please tell me how can I implement writeHello() method in order to write something in firstLineEdit in the MainWindow
Thanks
First of all you should not modify the code generated by Qt Designer since it is not a GUI, it is just a class that fills a GUI, and that brings several inconveniences such as not being able to overwrite the methods of the widget, or some you want to use methods of the widget in that class. Instead it inherits from a suitable widget and uses the other class as an interface.
Going to the point you should not mix the classes since there will be a lot of dependency between them and in the future if you modify one class you will have to modify the other which is unbeatable, instead you use the signals to notify any change or action.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
#MainWindow.setObjectName("MainWindow")
MainWindow.setEnabled(True)
MainWindow.resize(574, 521)
MainWindow.setWindowIcon(QtGui.QIcon(':/icons/drone.ico'))
MainWindow.setIconSize(QtCore.QSize(32, 32))
self.centralwidget = QtWidgets.QWidget(MainWindow)
MainWindow.setCentralWidget(self.centralwidget)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.firstPushButton = QtWidgets.QPushButton(self.centralwidget)
self.firstLineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.verticalLayout_2.addWidget(self.firstPushButton)
self.verticalLayout_2.addWidget(self.firstLineEdit)
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.firstPushButton.clicked.connect(self.showDialog)
def showDialog(self):
dialog = MyDialog()
dialog.clicked.connect(self.writeHello)
dialog.exec()
#QtCore.pyqtSlot()
def writeHello(self):
self.firstLineEdit.setText('Hello')
class MyDialog(QtWidgets.QDialog):
clicked = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedSize(400, 200)
self.myButton = QtWidgets.QPushButton("Write something")
# When I click the myButton, I want it to change the text of MainWindow lineEdit
self.myButton.clicked.connect(self.clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.myButton)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.setWindowTitle("BEM Analysis for propellers")
w.show()
sys.exit(app.exec())

SEGV_MAPERR for PyQt5 object

Why does the following demo code produce a SEGV_MAPERR? How would one fix it?
Hint: Once one deletes the line annotated with "# Ref1", no error is produced. This is not easily done in the production code the problem is abstracted from.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebChannel
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.webView = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
self.webView.setHtml("")
self.gridLayout.addWidget(self.webView, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
page = self.webView.page() # REF1
for i in range(2):
self.init_page()
def init_page(self):
class EditObject(QtCore.QObject):
#QtCore.pyqtSlot(str)
def edit(self, s):
print("test")
editObject = EditObject(self.webView.page())
poChannel = self.webView.page().webChannel()
print(1)
if poChannel is None:
poChannel = QtWebChannel.QWebChannel()
self.webView.page().setWebChannel(poChannel)
print(2)
objects = poChannel.registeredObjects()
print(objects)
poChannel.registerObject("editObject", editObject)
self.webView.setHtml("")
from PyQt5 import QtWebEngineWidgets
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_())
This is similar to Issues with PyQt5==5.10 on Ubuntu 18, but with example code. For the example code, https://gist.github.com/gioditalia/03c9fd5d793aeccbe065fea45d842431 is adapted.
The problem is that poChannel is a local variable that will be deleted after executing init_page, so in the second iteration the poChannel reference will point to an incorrect memory address. So the solution is to extend its cycle to that of the view, so we take advantage of the Qt property and pass it as a parent to self.webView.
poChannel = QtWebChannel.QWebChannel(self.webView)
Although as PyQt points out in the docs and the generated file: # WARNING! All changes made in this file will be lost!, it is not convenient to modify the class generated by the .ui, instead you must create another class that inherits from the appropriate widget and use the interface provided by Qt Designer.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets, QtWebChannel
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.webView = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
self.gridLayout.addWidget(self.webView, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
class EditObject(QtCore.QObject):
#QtCore.pyqtSlot(str)
def edit(self, s):
print("test")
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
for i in range(2):
self.init_page()
def init_page(self):
editObject = EditObject(self.webView.page())
poChannel = self.webView.page().webChannel()
if poChannel is None:
poChannel = QtWebChannel.QWebChannel(self)
self.webView.page().setWebChannel(poChannel)
objects = poChannel.registeredObjects()
poChannel.registerObject("editObject", editObject)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

printing from main page in pyqt5

i write some code in pyqt5 that create a table in main main:
class Ui_MainWindow(object):
def setupUi(self, MainWindow): ...
def retranslateUi(self, MainWindow):...
self.pushButton.setText(_translate("MainWindow", "print"))
self.pushButton.clicked.connect(self.printer)
def printer(self):...
and use this class by:
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
i wanna know how can i print from main page of my program?
This can be done rather easy using QPrinter class.
Below is well-commented example of how to do it.
import sys
from PyQt5 import QtGui, QtWidgets, QtPrintSupport
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Create some widgets
self.setGeometry(500, 500, 300, 300)
self.button = QtWidgets.QPushButton(
'Print QTextEdit widget (the one below)', self)
self.button.setGeometry(20, 20, 260, 30)
self.editor = QtWidgets.QTextEdit(
'Wow such text why not change me?', self)
self.editor.setGeometry(20, 60, 260, 200)
self.button.clicked.connect(self.print_widget)
def print_widget(self):
# Create printer
printer = QtPrintSupport.QPrinter()
# Create painter
painter = QtGui.QPainter()
# Start painter
painter.begin(printer)
# Grab a widget you want to print
screen = self.editor.grab()
# Draw grabbed pixmap
painter.drawPixmap(10, 10, screen)
# End painting
painter.end()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
gui = App()
gui.show()
app.exec_()
To print whole window just replace screen = self.editor.grab() with screen = self.grab()

Categories