There has been a small problem with a little project of mine using PyQt5. I tried to add a random QWidget (in this example a QPushbutton) to a custom QWidget. However, I don't understand the behavior of the "setParent" function. When I use it outside of the custom QWidget, the QPushButton is displayed. When I use it in a declared function of the custom Widget, the QPushButton is occluded and I have no chance of displaying it outside of adding a layout (which I don't want). Here an example of the source code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class customWidget(QWidget):
def __init__(self):
super().__init__()
self.addButton()
def addButton(self):
button = QPushButton('not_showing')
button.setParent(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
button = QPushButton('showing')
button.setParent(w)
button.move(50,50)
w.resize(600,600)
w.move(1000,300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
There is no change, when adding the parent during the initialization of the QPushButton.
When the function addButton exits, the button is removed.
If you want to see the button, try this:
class customWidget(QWidget):
def __init__(self):
super().__init__()
self.addButton()
self.button = None
def addButton(self):
if self.button is None:
self.button = QPushButton('not_showing')
self.button.setParent(self)
You do not have this problem in the main function because this function does not return until the application is stopped.
EDIT: The comment was right, but you also missed some arguments. This will work
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class customWidget(QWidget):
def __init__(self, parent=None):
super(customWidget, self).__init__(parent)
self.addButton()
def addButton(self):
button = QPushButton('not_showing')
button.setParent(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = customWidget()
button = QPushButton('showing')
button.setParent(w)
button.move(50,50)
w.resize(600,600)
w.move(1000,300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
Related
I am using this method to have multiple windows GUI in a python script.
With a button press, I open one of the two forms, that have multiple textEdits and comboBoxes, used to fill a word teamplate easier.
Now, I want to reset all the inputs after I finished filling the form, and save the document.
What behaviour I want:
From main window, open one of the forms depending on 2 ComboBoxes index (type of document, and the client's gender).
After I fill the form and exit it, I go back to the main window, and when I open again a form, the text edits are empty and the combo boxes are on index 0.
But i can't make it to work, either the inputs are not resetted, either I kill the popup window and the main window.
I can't only kill the popup, I can only hide it and show it again.
# import some PyQt5 modules
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QImage
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QTimer
from lib.eco_barbat_ui import *
from lib.eco_femeie_ui import *
from lib.eco_main_ui import *
class eco_abd_barbat(QWidget):
def __init__(self):
# call QWidget constructor
super().__init__()
# self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.ui = Ui_EcoAbdBarbat()
self.ui.setupUi(self)
def display(self):
self.show()
class eco_abd_femeie(QWidget):
def __init__(self):
# call QWidget constructor
super().__init__()
# self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.ui = Ui_EcoAbdFemeie()
self.ui.setupUi(self)
def display(self):
self.show()
class MainWindow(QWidget):
# class constructor
def __init__(self):
# call QWidget constructor
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.eco_abd_barbat = eco_abd_barbat()
self.eco_abd_femeie = eco_abd_femeie()
self.ui.pushButton.clicked.connect(self.open)
def open(self):
if self.ui.tip.currentIndex() == 0 and self.ui.sex.currentIndex()==0:
self.eco_abd_femeie.ui.qnume = self.ui.nume.toPlainText()
self.eco_abd_femeie.ui.qprenume = self.ui.prenume.toPlainText()
self.eco_abd_femeie.ui.qvarsta = self.ui.varsta.toPlainText()
self.eco_abd_femeie.ui.qdomiciliu = self.ui.domiciliu.toPlainText()
self.eco_abd_femeie.show()
elif self.ui.tip.currentIndex() == 0 and self.ui.sex.currentIndex()==1:
self.eco_abd_barbat.ui.qnume=self.ui.nume.toPlainText()
self.eco_abd_barbat.ui.qprenume = self.ui.prenume.toPlainText()
self.eco_abd_barbat.ui.qvarsta = self.ui.varsta.toPlainText()
self.eco_abd_barbat.ui.qdomiciliu = self.ui.domiciliu.toPlainText()
self.eco_abd_barbat.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
# create and show mainWindow
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
This is the code that make it work for me, I cant explain 100% why it works, but i think before I reloaded the same variable over and over, as now I am forcing it to reload the ui from the file containing it every time the variable is accesed.
For some reason, without the self in front of variable (on self.formWindow, in open function), the second window would just close a split second after it opened.
This works for me, I mostly understand the reason why is performing like this, but if someone can make things clear about the self part, I'm happy to learn.
# import some PyQt5 modules
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QImage
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QTimer
from lib.eco_barbat_ui import *
from lib.eco_femeie_ui import *
from lib.eco_main_ui import *
class eco_abd_barbat(QWidget):
def __init__(self):
# call QWidget constructor
super().__init__()
# self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.ui = Ui_EcoAbdBarbat()
self.ui.setupUi(self)
class eco_abd_femeie(QWidget):
def __init__(self):
# call QWidget constructor
super().__init__()
# self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
self.ui = Ui_EcoAbdFemeie()
self.ui.setupUi(self)
class MainWindow(QWidget):
# class constructor
def __init__(self):
# call QWidget constructor
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.open)
def select(self):
if self.ui.tip.currentIndex() == 0 and self.ui.sex.currentIndex() == 0:
return eco_abd_femeie()
else:
return eco_abd_barbat()
def open(self):
self.formWindow=self.select()
self.formWindow.ui.qnume = self.ui.nume.toPlainText()
self.formWindow.ui.qprenume = self.ui.prenume.toPlainText()
self.formWindow.ui.qvarsta = self.ui.varsta.toPlainText()
self.formWindow.ui.qdomiciliu = self.ui.domiciliu.toPlainText()
self.formWindow.show()
I'm trying to create a layout with pyqt5, but i cannot do it. There are so many error. Why i am getting so many errors.
autoFillBackground() takes no arguments, and MainWindow will not assume the palette of it's central widget unless you explicitly set it. Also, this has nothing to do with a layout.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.autoFillBackground()
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
widget = Color('Blue')
self.setCentralWidget(widget)
self.setPalette(widget.palette())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
I would like to know how to implement QProgressBar, which shows the progress of calculation in main thread.
Please refer to below codes.
import sys
from PySide2.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QProgressBar
from PySide2.QtCore import QThread
class BarThread(QThread):
# Progress Bar UI Definition
def __init__(self):
QThread.__init__(self)
self.window = QWidget()
self.pgsb = QProgressBar()
self.lay = QVBoxLayout()
self.lay.addWidget(self.pgsb)
self.window.setLayout(self.lay)
self.isRun = False
# Thread Function Definition
def run(self):
self.window.show()
while self.isRun:
self.pgsb.setValue(self.percent)
print(self.percent)
if self.percent == 100:
self.isRun = False
class Tool(QWidget):
# Main UI Definition
def __init__(self):
windowWidth = 300
windowHeight = 300
QWidget.__init__(self)
self.setWindowTitle("Example")
self.resize(windowWidth, windowHeight)
self.bt = QPushButton('Numbering')
self.layout = QVBoxLayout()
self.layout.addWidget(self.bt)
self.setLayout(self.layout)
# Main Function Link Definition
self.bt.clicked.connect(self.numbering)
# Main Function Definition
def numbering(self):
bth = BarThread()
bth.start()
bth.isRun = True
for x in range(0,100000):
bth.percent = x/1000
print(x)
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = Tool()
widget.show()
sys.exit(app.exec_())
You can copy and paste directly onto your python IDE.
(it needs PySide2. It can be installed with 'pip install pyside2' in your prompt).
This code executes simple numbering, however, this doesn't show numbering progress.
How can I solve this problem? Thank you in advance.
P.S. I'm using Windows 10 with PyCharm.
You have at least the following errors:
You must not modify the GUI from another thread, in your case the run method is executed in another thread but you try to modify the value of the QProgressBar, in addition to displaying a widget which is not allowed. If you want to modify the GUI with the information provided in the execution in the secondary thread you must do it through signals since they are thread-safe
The for loop is the blocking task so it must be executed in another thread.
Considering the above, the solution is:
import sys
from PySide2.QtWidgets import (
QApplication,
QWidget,
QPushButton,
QVBoxLayout,
QProgressBar,
)
from PySide2.QtCore import QThread, Signal
class ProgressWidget(QWidget):
def __init__(self, parent=None):
super(ProgressWidget, self).__init__(parent)
self.pgsb = QProgressBar()
lay = QVBoxLayout(self)
lay.addWidget(self.pgsb)
class BarThread(QThread):
progressChanged = Signal(int)
def run(self):
percent = 0
for x in range(0, 100000):
percent = x / 100
self.progressChanged.emit(percent)
class Tool(QWidget):
"""Main UI Definition"""
def __init__(self, parent=None):
super(Tool, self).__init__(parent)
self.setWindowTitle("Example")
self.resize(300, 300)
self.bt = QPushButton("Numbering")
layout = QVBoxLayout(self)
layout.addWidget(self.bt)
# Main Function Link Definition
self.bt.clicked.connect(self.numbering)
self.bar_thread = BarThread(self)
self.progress_widget = ProgressWidget()
self.bar_thread.progressChanged.connect(self.progress_widget.pgsb.setValue)
# Main Function Definition
def numbering(self):
self.bar_thread.start()
self.progress_widget.show()
def closeEvent(self, event):
super(Tool, self).closeEvent(event)
self.bar_thread.quit()
self.bar_thread.wait()
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = Tool()
widget.show()
sys.exit(app.exec_())
Is there a way in Qt5 to apply a QGraphicsEffect to a widget, even when one of its parent widgets already has a QGraphicsEffect applied to it?
When i try the following:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class ParentWidget(QWidget):
def __init__(self,parent):
super().__init__(parent)
effect = QGraphicsBlurEffect(self)
self.setGraphicsEffect(effect)
effect.setBlurRadius(0)
class ChildWidget(ParentWidget):
def __init__(self,parent):
super().__init__(parent)
self.layout = QGridLayout(self)
widget = QWidget()
widget.setObjectName('reviewControlArea')
effect = QGraphicsOpacityEffect(widget)
widget.setGraphicsEffect(effect)
self.layout.addWidget(widget)
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
cw = ChildWidget(MainWindow)
MainWindow.setCentralWidget(cw)
MainWindow.show()
sys.exit(app.exec_())
The stdout says:
QPainter::begin: A paint device can only be painted by one painter at a time.
I want use the autoconnection feature. I am using this example:
http://www.eurion.net/python-snippets/snippet/Connecting%20signals%20and%20slots.html
it works, but I want to create my own signals and own slots, the example using built in signals.
for example, here are a custom signal with a custom slot, but don't works:
import sys
from PyQt4 import QtGui, QtCore
class SignalsAndSlots(QtGui.QWidget):
testSignal = QtCore.pyqtSignal(str,name='testSignal')
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setObjectName('testObject')
self.label = QtGui.QLabel(self)
QtCore.QMetaObject.connectSlotsByName(self)
self.emitSignal()
def emitSignal(self):
self.testSignal.emit('message')
#QtCore.pyqtSlot(str,name='on_testObject_testSignal')
def autoSlot(self,msg):
self.label.setText(msg)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
gui = SignalsAndSlots()
gui.show()
app.exec_()
Thanks a lot
Ber is right. This what the pyqt documentation says:
QMetaObject.connectSlotsByName searches recursively for all child objects of the given object [...]
Here is a simple example with custom signals :
import sys
from PyQt4 import QtGui, QtCore
class CustomButton(QtGui.QPushButton):
custom_clicked = QtCore.pyqtSignal(str, name='customClicked')
def mousePressEvent(self, event):
self.custom_clicked.emit("Clicked!")
class SignalsAndSlots(QtGui.QWidget):
def __init__(self):
QtGui.QMainWindow.__init__(self)
layout = QtGui.QHBoxLayout(self)
self.custom_button = CustomButton("Press Me", self)
self.custom_button.setObjectName('customButton')
self.label = QtGui.QLabel("Nothing...", parent=self)
layout.addWidget(self.custom_button)
layout.addWidget(self.label)
QtCore.QMetaObject.connectSlotsByName(self)
#QtCore.pyqtSlot(str, name='on_customButton_customClicked')
def autoSlot(self, msg):
self.label.setText(msg)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
gui = SignalsAndSlots()
gui.show()
app.exec_()
But I think you should consider not using the object names. New-style signal connection is way neater. Here is the same application :
import sys
from PyQt4 import QtGui, QtCore
class CustomButton(QtGui.QPushButton):
custom_clicked = QtCore.pyqtSignal(str)
def mousePressEvent(self, event):
self.custom_clicked.emit("Clicked!")
class SignalsAndSlots(QtGui.QWidget):
def __init__(self):
QtGui.QMainWindow.__init__(self)
layout = QtGui.QHBoxLayout(self)
self.custom_button = CustomButton("Press Me", self)
self.custom_button.setObjectName('customButton')
self.label = QtGui.QLabel("Nothing...", parent=self)
layout.addWidget(self.custom_button)
layout.addWidget(self.label)
self.custom_button.custom_clicked.connect(self.on_clicked)
def on_clicked(self, msg):
self.label.setText(msg)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
gui = SignalsAndSlots()
gui.show()
app.exec_()
I investigated a bit into the doc. of QtCore.QMetaObject.connectSlotsByName().
For this, I made an MCVE to collect things not working vs. things working:
#!/usr/bin/python3
import sys
from PyQt5.QtCore import QT_VERSION_STR
from PyQt5.QtCore import QMetaObject, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QWidget
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
# build GUI
qMain = QWidget()
qVBox = QVBoxLayout()
qBtnPreview1 = QPushButton("Preview 1")
qBtnPreview1.setObjectName("preview1")
qVBox.addWidget(qBtnPreview1)
qBtnPreview2 = QPushButton("Preview 2")
qBtnPreview2.setObjectName("preview2")
qVBox.addWidget(qBtnPreview2)
qBtnPreview3 = QPushButton("Preview 3")
qBtnPreview3.setObjectName("preview3")
qVBox.addWidget(qBtnPreview3)
qBtnPreview4 = QPushButton("Preview 4")
qBtnPreview4.setObjectName("preview4")
qVBox.addWidget(qBtnPreview4)
qBtnPreview5 = QPushButton("Preview 5")
qBtnPreview5.setObjectName("preview5")
qVBox.addWidget(qBtnPreview5)
qBtnPreview6 = QPushButton("Preview 6")
qBtnPreview6.setObjectName("preview6")
qVBox.addWidget(qBtnPreview6)
qMain.setLayout(qVBox)
self.setCentralWidget(qMain)
# install signal handlers
qBtnPreview1.clicked.connect(lambda: print("preview1 clicked."))
qBtnPreview2.clicked.connect(lambda: print("preview2 clicked."))
qBtnPreview3.clicked.connect(lambda: print("preview3 clicked."))
qBtnPreview4.clicked.connect(lambda: print("preview4 clicked."))
qBtnPreview5.clicked.connect(lambda: print("preview5 clicked."))
qBtnPreview6.clicked.connect(lambda: print("preview6 clicked."))
QMetaObject.connectSlotsByName(self)
#pyqtSlot()
def preview1(self):
print("MainWindow.preview1() called.")
#pyqtSlot()
def preview2_clicked(self):
print("MainWindow.preview2_clicked() called.")
#pyqtSlot()
def on_preview3(self):
print("MainWindow.on_preview3() called.")
#pyqtSlot()
def on_preview4_clicked(self):
print("MainWindow.on_preview4_clicked() called.")
#pyqtSlot(name='on_preview5_clicked')
def preview5_clicked(self):
print("MainWindow.preview5_clicked() called.")
def on_preview6_clicked(self):
print("MainWindow.on_preview6_clicked() called.")
if __name__ == '__main__':
print("Qt Version: {}".format(QT_VERSION_STR))
app = QApplication(sys.argv)
# build GUI
qWinMain = MainWindow()
qWinMain.show()
# runtime loop
sys.exit(app.exec_())
Output:
$ ./testQMetaObjectConnectSlotsByName.py
Qt Version: 5.9.3
After clicking each of the six buttons once, I got:
preview1 clicked.
preview2 clicked.
preview3 clicked.
preview4 clicked.
MainWindow.on_preview4_clicked() called.
preview5 clicked.
MainWindow.preview5_clicked() called.
preview6 clicked.
MainWindow.on_preview6_clicked() called.
MainWindow.on_preview6_clicked() called.
Observations:
MainWindow.preview1(), MainWindow.preview2_clicked(), MainWindow.on_preview3() were not connected. They don't follow the required convention of QtCore.QMetaObject.connectSlotsByName().
MainWindow.on_preview4_clicked() is connected by name – properly following the required name convention.
MainWindow.preview5_clicked() is connected as well – by giving the required name by #pyqtSlot(name='on_preview5_clicked').
MainWindow.on_preview6_clicked() is connected as well – even without marking it as slot explicitly. (However, it is called twice for any reason which might be undesirable.)
Explicit connections work in any case and appear more robust to me.
Further readings:
QMetaObject::connectSlotsByName() (Qt5 C++ doc.)
Connecting Slots By Name (PyQt5 doc.)
PyQt5.QtCore.pyqtSlot() (PyQt5 doc.).
Actually, this was my answer to another question (SO: How to display two images in each Qlabel PyQt5) until I realized that QtCore.QMetaObject.connectSlotsByName() unlikely plays a role in that question.
So, I moved it here where it may be more appropriate even although the question is a bit aged.