PyQt: setCentralWidget clashes with QPushButton function - python

So I deduced down to this problem from my project that the setCentralWidget from the Slides Widget class I injected in the Main Window class causes the function of the buttons (i.e. opening a new Widget window) from the Main Window to not work at all.
If I remove the setCentralWidget the buttons work fine so without compromising any of the functions, what approach should I use for this? Should I use a different form of calling the Slides Widget class?
Any help would be appreciated, as always! Thanks!
from PyQt4 import QtCore, QtGui
import sys
from functools import partial
class MainWindow(QtGui.QMainWindow):
def __init__(self, image_files, parent=None):
super(MainWindow, self).__init__()
self.setupUi(self)
self.slides_widget = Slides(image_files, self)
#If you enable this down below, pushButton will not function
#and instead the slideshow will pop up and function correctly
#self.setCentralWidget(self.slides_widget)
def setupUi(self, MainWindow):
MainWindow.resize(1278, 688)
#MainWindow.setStyleSheet(self.styledata)
self.groupBox = QtGui.QGroupBox(MainWindow)
self.groupBox.setGeometry(QtCore.QRect(490, 220, 120, 371))
self.groupBox.setAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop)
self.groupBox.setFlat(False)
self.groupBox.setCheckable(False)
self.pushButton_Desc = QtGui.QPushButton(self.groupBox)
self.pushButton_Desc.setGeometry(QtCore.QRect(20, 30, 71, 61))
self.pushButton_Desc.clicked.connect(partial(self.DescWindow))
self.groupBox.raise_()
self.NewWindow = QtGui.QWidget()
def DescWindow(self):
self.NewWindow.show();
class Slides(QtGui.QWidget):
def __init__(self, image_files, parent=None):
super(Slides, self).__init__(parent)
self.image_files = image_files
self.label = QtGui.QLabel("", self)
self.label.setGeometry(65, 225, 423, 363)
#buttons to rewind and forward
self.button = QtGui.QPushButton(". . .", self)
self.button.setGeometry(200, 100, 140, 30)
self.button.clicked.connect(self.timerEvent)
self.timer = QtCore.QBasicTimer()
self.step = 0
self.delay = 3000 #ms
def timerEvent(self, e=None):
if self.step >= len(self.image_files):
self.timer.start(self.delay, self)
self.step = 0
return
self.timer.start(self.delay, self)
file = self.image_files[self.step]
image = QtGui.QPixmap(file)
self.label.setPixmap(image)
#self.setWindowTitle("{} --> {}".format(str(self.step), file))
self.step += 1
image_files = ["images\slide1.jpg", "images\slide2.jpg", "images\slide3.jpg",
"images\slide4.jpg", "images\slide5.jpg"]
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Form = MainWindow(image_files)
Form.show()
sys.exit(app.exec_())

The problem arises because first place the button and on this is placed the CentralWidget, the solution is to place the button in the CentralWidget, ie in Slides.
You must change:
def __init__(self, image_files, parent=None):
super(MainWindow, self).__init__()
self.setupUi(self)
self.slides_widget = Slides(image_files, self)
to:
def __init__(self, image_files, parent=None):
super(MainWindow, self).__init__()
self.slides_widget = Slides(image_files, self)
self.setupUi(self)
and
self.groupBox = QtGui.QGroupBox(MainWindow)
to:
self.groupBox = QtGui.QGroupBox(MainWindow.slides_widget)
Complete code:
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, image_files, parent=None):
super(MainWindow, self).__init__()
self.slides_widget = Slides(image_files, self)
self.setupUi(self)
def setupUi(self, MainWindow):
MainWindow.resize(1278, 688)
#MainWindow.setStyleSheet(self.styledata)
self.groupBox = QtGui.QGroupBox(MainWindow.slides_widget)
self.groupBox.setGeometry(QtCore.QRect(490, 220, 120, 371))
self.groupBox.setAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop)
self.groupBox.setFlat(False)
self.groupBox.setCheckable(False)
self.pushButton_Desc = QtGui.QPushButton(self.groupBox)
self.pushButton_Desc.setGeometry(QtCore.QRect(20, 30, 71, 61))
self.pushButton_Desc.clicked.connect(self.DescWindow)
self.setCentralWidget(self.slides_widget)
self.groupBox.raise_()
self.NewWindow = QtGui.QWidget()
def DescWindow(self):
self.NewWindow.show();
class Slides(QtGui.QWidget):
def __init__(self, image_files, parent=None):
super(Slides, self).__init__(parent)
self.image_files = image_files
self.label = QtGui.QLabel("", self)
self.label.setGeometry(65, 225, 423, 363)
#buttons to rewind and forward
self.button = QtGui.QPushButton(". . .", self)
self.button.setGeometry(200, 100, 140, 30)
self.button.clicked.connect(self.timerEvent)
self.timer = QtCore.QBasicTimer()
self.step = 0
self.delay = 3000 #ms
def timerEvent(self, e=None):
if self.step >= len(self.image_files):
self.timer.start(self.delay, self)
self.step = 0
return
self.timer.start(self.delay, self)
file = self.image_files[self.step]
image = QtGui.QPixmap(file)
self.label.setPixmap(image)
#self.setWindowTitle("{} --> {}".format(str(self.step), file))
self.step += 1
image_files = ["images\slide1.jpg", "images\slide2.jpg", "images\slide3.jpg",
"images\slide4.jpg", "images\slide5.jpg"]
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Form = MainWindow(image_files)
Form.show()
sys.exit(app.exec_())

Related

PyQT: Exit/Complete Second Window Before First Window

I have the following code below:
from PyQt4 import QtGui
import sys
class Second(QtGui.QWidget):
def __init__(self, parent=None):
super(Second, self).__init__(parent)
self.grid = QtGui.QGridLayout(self)
self.setGeometry(650,400,400,200)
self.widget = QtGui.QWidget()
class First(QtGui.QMainWindow):
def __init__(self, parent=None):
super(First, self).__init__(parent)
self.grid = QtGui.QGridLayout(self)
self.setGeometry(350, 200, 1000, 700)
self.widget = QtGui.QWidget()
Button1 = QtGui.QPushButton('...', self)
Button1.clicked.connect(self.on_pushButton_clicked)
self.grid.addWidget(Button1, 0, 0, 1, 1)
def on_pushButton_clicked(self):
self.Second = Second()
self.Second.setWindowTitle('Window')
self.Second.show()
def main():
app = QtGui.QApplication(sys.argv)
main = First()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
When I click the button, I want to be able to finish up my action in the second window before continuing on the first. Right now, I can exit out of my first window and the second window remains open. How do you keep on the second window but keep the first window unselectable?
There are 2 possible solutions:
- Second must inherit from QDialog, pass it as parent to the first window and use exec_() instead of show:
class Second(QtGui.QDialog):
def __init__(self, parent=None):
super(Second, self).__init__(parent)
class First(QtGui.QMainWindow):
def __init__(self, parent=None):
super(First, self).__init__(parent)
self.setGeometry(350, 200, 1000, 700)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
grid = QtGui.QGridLayout(self.widget)
Button1 = QtGui.QPushButton('...', self)
Button1.clicked.connect(self.on_pushButton_clicked)
grid.addWidget(Button1, 0, 0, 1, 1)
#QtCore.pyqtSlot()
def on_pushButton_clicked(self):
self.Second = Second(self)
self.Second.setWindowTitle('Window')
self.Second.exec_()
- Change the windowModality to Qt::WindowModal, activate the flag Qt::Dialog and pass it the first window as parent.
class Second(QtGui.QWidget):
def __init__(self, parent=None):
super(Second, self).__init__(parent)
self.setWindowModality(QtCore.Qt.WindowModal)
self.setWindowFlags(self.windowFlags() | QtCore.Qt.Dialog)
class First(QtGui.QMainWindow):
def __init__(self, parent=None):
super(First, self).__init__(parent)
self.setGeometry(350, 200, 1000, 700)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
grid = QtGui.QGridLayout(self.widget)
Button1 = QtGui.QPushButton('...', self)
Button1.clicked.connect(self.on_pushButton_clicked)
grid.addWidget(Button1, 0, 0, 1, 1)
#QtCore.pyqtSlot()
def on_pushButton_clicked(self):
self.Second = Second(self)
self.Second.setWindowTitle('Window')
self.Second.show()

PyQt5 Automatic drawing from input value

I have recently created a widget with Qpaint, which I want to pass value to it, at the same time force the Qpaint Widget to draw from input values. The idea is to define a data value from a Qdialog and pass it to main widget, and pass the value to Qpaint Widget class. I would like to have, when user clicks on the button 'Getting values' a dialog widget would appear and insert some int values, then pass it to main Widget. from there pass value to correct class Paint. Which would draw and display the result. I tried with Qlabel, to assign value first to Qlabel or QlineEdit,
class Button(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
---------
self.value = QtWidgets.QLabel()
--------
Then inside the paint class call the value or text of those. then assign it to Qpaint event. But seems does not work.'
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.button = Button()
self.Value = self.button.value
---------
painter.drawRect(100,100,250,250) <----- instead of value 250 having self.Value
The code Main.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from datainput import *
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Paint()
self.lay = QtWidgets.QVBoxLayout()
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
self.setLayout(self.lay)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.button = Button()
self.Value = self.button.value
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
def paintEvent(self, event):
self.pen = QtGui.QPen()
self.brush = QtGui.QBrush( QtCore.Qt.gray, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(self.pen)
painter.setBrush(self.brush)
painter.drawRect(100,100,250,250)
painter.setBrush(QtGui.QBrush())
class Button(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Button, self).__init__(parent)
getbutton = QtWidgets.QPushButton('Getting values')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(getbutton)
self.value = QtWidgets.QLabel()
getbutton.clicked.connect(self.getbuttonfunc)
def getbuttonfunc(self):
subwindow=Dinput()
subwindow.setWindowModality(QtCore.Qt.ApplicationModal)
if subwindow.exec_() == QtWidgets.QDialog.Accepted:
self._output = subwindow.valueEdit.text()
return self.value.setText(self._output)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
Input Qdialog code, datainput.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Dinput(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dinput, self).__init__(parent)
valuelabel = QtWidgets.QLabel('Input: ')
self.valueEdit = QtWidgets.QLineEdit()
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.close)
self.Alay = QtWidgets.QHBoxLayout()
self.Alay.addWidget(valuelabel)
self.Alay.addWidget(self.valueEdit)
self.Blay = QtWidgets.QVBoxLayout()
self.Blay.addLayout(self.Alay)
self.Blay.addWidget(buttonBox)
self.setLayout(self.Blay)
def closeEvent(self, event):
super(Dinput, self).closeEvent(event)
def accept(self):
super(Dinput, self).accept()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Dinput()
w.show()
sys.exit(app.exec_())
Visualization
I appreciate any help. Thankssss
datainput is irrelevant, your task is only to obtain a number so for space question I will not use it and instead I will use QInputDialog::getInt(). Going to the problem, the strategy in these cases where the value can be obtained at any time is to notify the change to the other view through a signal, in the slot that receives the value is to update a variable that stores the value and call update so that it calls when necessary to paintEvent, and in the paintEvent use the variable that stores the value.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Paint()
self.button.valueChanged.connect(self.paint.set_size_square)
self.lay = QtWidgets.QVBoxLayout(self)
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self._size_square = 250
#QtCore.pyqtSlot(int)
def set_size_square(self, v):
self._size_square = v
self.update()
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.gray, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(pen)
painter.setBrush(brush)
r = QtCore.QRect(QtCore.QPoint(100, 100), self._size_square*QtCore.QSize(1, 1))
painter.drawRect(r)
class Button(QtWidgets.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(Button, self).__init__(parent)
getbutton = QtWidgets.QPushButton('Getting values')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(getbutton)
self.value = QtWidgets.QLabel()
getbutton.clicked.connect(self.getbuttonfunc)
#QtCore.pyqtSlot()
def getbuttonfunc(self):
number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
self.tr("Input:"), 1, 1)
if ok:
self.valueChanged.emit(number)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())

How to update QWidget's content?

There is a QWidget, which is called with the button "Start" in the main widget.
It shows some text, which should be updated every minute during the work of the QWidget.
How can I make this infinity updating within this code?
class ExampleWidget(QWidget):
def __init__(self, parent=None):
super().__init__()
self.setWindowTitle('Example Widget ScrollArea')
self.initUi()
def initUi(self):
area = QScrollArea(self)
area.setWidgetResizable(True)
self.scrollAreaWidgetContents = QLabel(some_text, self)
area.setWidget(self.scrollAreaWidgetContents)
button = QPushButton("Close")
button.clicked.connect(self.goMainWindow)
layoutV = QVBoxLayout()
layoutV.addWidget(area)
layoutV.addWidget(button)
self.setLayout(layoutV)
def goMainWindow(self):
self.hide()
def sizeHint(self):
return QSize(400, 200)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
start_main_button = QPushButton('Start', self)
start_main_button.move(40, 40)
start_main_button.clicked.connect(self.start)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Test')
def start(self):
global some_text
some_text = 'some text'
self.result_widget = ExampleWidget()
self.result_widget.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
One of my tries:
def start(self):
global some_text
some_text = 'some text'
self.result_widget = ExampleWidget()
self.result_widget.show()
i = 0
while True:
i+=1
some_text+=str(i)
self.result_widget = ExampleWidget()
self.result_widget.show()
Forget the global variables because they are considered bad practice, on the other hand in a GUI you should avoid having loops that consume a lot of time, and in your case a while True blocks the GUI. In Qt if you want to do periodic tasks you must use a QTimer:
from PyQt5 import QtCore, QtWidgets
class ExampleWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__()
self.setWindowTitle('Example Widget ScrollArea')
self.initUi()
def initUi(self):
area = QtWidgets.QScrollArea()
area.setWidgetResizable(True)
self.scrollAreaWidgetContents = QtWidgets.QLabel("some_text")
area.setWidget(self.scrollAreaWidgetContents)
button = QtWidgets.QPushButton("Close")
button.clicked.connect(self.hide)
layoutV = QtWidgets.QVBoxLayout(self)
layoutV.addWidget(area)
layoutV.addWidget(button)
def update_text(self, text):
# update the text
self.scrollAreaWidgetContents.setText(text)
def sizeHint(self):
return QtCore.QSize(400, 200)
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
start_main_button = QtWidgets.QPushButton('Start', self)
start_main_button.move(40, 40)
start_main_button.clicked.connect(self.start)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Test')
self.result_widget = ExampleWidget()
self.timer = QtCore.QTimer(self, interval=60*1000)
self.timer.timeout.connect(self.on_timeout)
self.counter = 0
self.initial_text = "some_text"
def on_timeout(self):
# this method will be called every 60 * 1000 ms
self.initial_text += str(self.counter)
self.result_widget.update_text(self.initial_text)
self.counter += 1
#QtCore.pyqtSlot()
def start(self):
self.result_widget.show()
# start timer
self.timer.start()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

How can I pass data in run method (thread) (python, Pyqt)

I want to make the thread, in the window, using PyQt(Python). For example
class window(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(100, 100, 500, 200)
self.setWindowTitle('Hello')
self.label = QtGui.QLabel(u'WoRLd', self)
self.Layout = QtGui.QVBoxLayout()
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
self.introduced = 123
self.request = Requests()
self.request.start()
self.connect(self.request, QtCore.SIGNAL("mysignal(QString)"),
self.on_change, QtCore.Qt.QueuedConnection)
def on_change(self, s):
self.label.setText(s)
class Requests():
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
def run(self):
self.emit(QtCore.SIGNAL("mysignal(QString)"), "143242" )
app = QtGui.QApplication(sys.argv)
main = window()
main.show()
sys.exit(app.exec_())
But, in method "run", I need some dates from "class window" (variable: introduced).
How can I pass this variable to "def run"?
you just have to make arguments for it and pass it into the class instance. then assign it to self.date, this makes it accesible across the class instance:
class window(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(100, 100, 500, 200)
self.setWindowTitle('Hello')
self.label = QtGui.QLabel(u'WoRLd', self)
self.Layout = QtGui.QVBoxLayout()
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
self.introduced = 123
date = # get some date whatever you need to pass here
self.request = Requests(date)
self.request.start()
self.connect(self.request, QtCore.SIGNAL("mysignal(QString)"),
self.on_change, QtCore.Qt.QueuedConnection)
def on_change(self, s):
self.label.setText(s)
class Requests():
def __init__(self, date, parent=None):
self.date = date
QtCore.QThread.__init__(self, parent)
def run(self):
# do something with self.date here
self.emit(QtCore.SIGNAL("mysignal(QString)"), "143242" )
app = QtGui.QApplication(sys.argv)
main = window()
main.show()
sys.exit(app.exec_())

Qt and python - how to refer to another class

I have main window which contains scene and button in widget from where I need to call scene:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Widget(QWidget):
def __init__(self, scene):
super(Widget, self).__init__()
self.refreshButton = QPushButton("Refresh", self)
self.refreshButton.clicked.connect(self.Refresh)
# THIS ACTION SHOULD PROCEED ARGUMENTS
# TO FUNCION "Refresh"
layout = QHBoxLayout()
layout.addWidget(self.refreshButton)
self.setLayout(layout)
self.show()
def Refresh(self, scene):
mainWinScene = scene
print "On Refresh! - ", mainWinScene.items()
class MainScene(QGraphicsScene):
def __init__(self):
super(MainScene, self).__init__()
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.scene = MainScene()
self.scene.setSceneRect(0,0,200,100)
self.scene.addLine(20,10,150,80)
self.view = QGraphicsView()
self.view.setScene(self.scene)
drawRectAct = QAction('&Add Rectangle', self)
drawRectAct.triggered.connect(self.drawRect)
shapeInspectorAct = QAction('&Show Inspector', self)
shapeInspectorAct.triggered.connect(self.showInspector)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&Shapes')
fileMenu.addAction(drawRectAct)
fileMenu.addAction(shapeInspectorAct)
self.setCentralWidget(self.view)
def drawRect(self):
self.scene.addRect(50,50,20,30)
def showInspector(self):
self.I = Widget(self.scene)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
How to proceed "scene" argument with action - to "Refresh" function?
You can pass a scene in Widget's constructor:
class Widget(QWidget):
def __init__(self, scene):
...
self.scene = scene
...
def Refresh(self):
print "On Refresh! - ", self.scene.items()
class MainWindow(QMainWindow):
...
def showInspector(self):
self.I = Widget(self.scene)
...

Categories