PyQt5) When I try to open Second window, it is closed without any error messages - pyqt5

I'm trying to open second window on main window.
I made 2 classes for each windows, and run the code. when I click the button on main window, the main window is closed and second windows show. but after 1 seconds the second window is closed and the process ends.
Really I don't know why it does...
from PyQt5 import QtWidgets
import sys
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 300)
# Button
self.button = QtWidgets.QPushButton(self)
self.button.setGeometry(0, 0, 400, 300)
self.button.setText('Open Sub Window')
self.button.setStyleSheet('font-size:40px')
self.button.clicked.connect(self.sub_show)
def sub_show(self):
self.hide()
self.sub_window = SubWindow()
self.sub_window.exec()
self.show()
class SubWindow(QtWidgets.QWidget):
def __init__(self):
super(SubWindow, self).__init__()
self.resize(400, 300)
self.button2 = QtWidgets.QPushButton(self)
self.button2.setGeometry(0, 0, 400, 300)
self.button2.setText('Back to Main window')
self.button2.setStyleSheet('font-size:40px')
self.button2.clicked.connect(self.home)
self.show()
def home(self):
self.close()
if __name__ == "__main__" :
app = QtWidgets.QApplication([])
mw = MainWindow()
mw.show()
sys.exit(app.exec())

You are calling show a few to many times. Also QWidget doesn't have an exec method. You will also want the first window to reappear when you close the second I assume.
I made some changes in the example below. Give it a try:
from PyQt5 import QtWidgets
import sys
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 300)
self.button = QtWidgets.QPushButton(self)
self.button.setGeometry(0, 0, 400, 300)
self.button.setText('Open Sub Window')
self.button.setStyleSheet('font-size:40px')
self.button.clicked.connect(self.sub_show)
def sub_show(self):
self.hide()
self.sub_window = SubWindow()
# connect signal to show again when button2 is pressed
self.sub_window.button2.clicked.connect(self.show)
self.sub_window.show()
class SubWindow(QtWidgets.QWidget):
def __init__(self):
super(SubWindow, self).__init__()
self.resize(400, 300)
self.button2 = QtWidgets.QPushButton(self)
self.button2.setGeometry(0, 0, 400, 300)
self.button2.setText('Back to Main window')
self.button2.setStyleSheet('font-size:40px')
self.button2.clicked.connect(self.close)
if __name__ == "__main__" :
app = QtWidgets.QApplication([])
mw = MainWindow()
mw.show()
sys.exit(app.exec())

Related

cannot be associated with QComboBox

button of class Main don't connect with class Qcombobox of Signals
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import *
import sys
from PyQt5 import QtGui
class Signals(QWidget):
asignal = pyqtSignal(str)
def __init__(self):
super(Signals, self).__init__()
self.setGeometry(300, 250, 400, 300)
self.ii()
self.show()
def ii(self):
vbox = QVBoxLayout()
self.combo = QComboBox()
self.combo.addItem("Python")
self.combo.addItem("Java")
self.combo.addItem("C++")
self.combo.addItem("C#")
self.combo.addItem("Ruby")
self.buttom = QPushButton("Click")
self.buttom.clicked.connect(self.windown2)
vbox.addWidget(self.combo)
vbox.addWidget(self.buttom)
self.setLayout(vbox)
def do_something(self):
self.asignal.emit(self.combo.currentText())
def windown2(self):
self.ggpp = Main()
self.ggpp.show()
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.coso1)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self):
s = Signals()
s.asignal.connect(lambda sig: print("self.combo.currentText()>>>>>" + sig))
s.do_something()
if __name__ == '__main__':
app = QApplication(sys.argv)
nals = Signals()
nals.show()
sys.exit(app.exec())
What you see happens because you're not using the existing instance of Signals, but you're creating a new one each time the button is clicked.
In your case, you could add a reference to the instance as an argument when you create the new window, so that you can correctly connect to its signal.
class Signals(QWidget):
# ...
def windown2(self):
self.ggpp = Main(self)
self.ggpp.show()
class Main(QWidget):
def __init__(self, signals):
super(Main, self).__init__()
self.signals = signals
self.signals.asignal.connect(self.coso1)
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.signals.do_something)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self, sig):
print("self.combo.currentText()>>>>>" + sig)

How to execute code after threads qthreadpool?

I have a GUI that implements 3 buttons
One of the buttons has the "start" function tied, which contains 2 processes, in fact, these are two cycles that should work multithreaded, but I need to add more code to the "start" function that, for example, changes some widgets.
The problem is that if I add some code after self.threadpool.start(thread2) self.threadpool.start(thread3), then it is not executed upon completion of these threads, but before that, or not at all.
I wrote a toy example that fully describes my requirements from the program
import sys
import time
from PySide2.QtCore import QThreadPool, QRunnable, Slot
from PySide2.QtWidgets import QApplication, QWidget, QPushButton
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.threadpool = QThreadPool()
self.btn.clicked.connect(lambda: self.start())
def start(self):
t1 = Worker(th1)
t2 = Worker(th2)
self.btn.setEnabled(False)
self.threadpool.start(t1)
self.threadpool.start(t2)
self.btn.setEnabled(True)
def initUI(self):
self.btn = QPushButton('Start', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('HELP')
self.show()
class Worker(QRunnable):
def __init__(self, fn):
super(Worker, self).__init__()
self.fn = fn
#Slot() # QtCore.Slot
def run(self):
self.fn()
def th1():
time.sleep(5)
print("th1")
def th2():
time.sleep(10)
print("th2")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I need the button to turn off before the streams start and turn on only after their completion.
The solution should be fair for any code I want to use after streams.
First, using the Slot decorator in the methods of a QRunnable is useless since it is not a QObject.
On the other hand, the idea of implementing a multithreading logic is that the main thread does not block, so you should not expect that after invoking start() it will be executed after it finishes executing the threads.
The solution is to create a QObject that emits a signal before and after the execution of the thread, and with that logic implement the GUI update:
import sys
import time
from PySide2.QtCore import QObject, QThreadPool, QRunnable, Signal, Slot
from PySide2.QtWidgets import QApplication, QWidget, QPushButton
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.active_workers = 0
self.threadpool = QThreadPool()
self.btn.clicked.connect(self.start)
#Slot()
def start(self):
t1 = Worker(th1)
t1.signaller.started.connect(self.on_started)
t1.signaller.finished.connect(self.on_finished)
t2 = Worker(th2)
t2.signaller.started.connect(self.on_started)
t2.signaller.finished.connect(self.on_finished)
self.btn.setEnabled(False)
self.threadpool.start(t1)
self.threadpool.start(t2)
def initUI(self):
self.btn = QPushButton("Start", self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle("HELP")
self.show()
#Slot()
def on_started(self):
self.active_workers += 1
#Slot()
def on_finished(self):
self.active_workers -= 1
self.btn.setEnabled(self.active_workers == 0)
class Signaller(QObject):
started = Signal()
finished = Signal()
class Worker(QRunnable):
def __init__(self, fn):
super(Worker, self).__init__()
self.signaller = Signaller()
self.fn = fn
def run(self):
self.signaller.started.emit()
self.fn()
self.signaller.finished.emit()
def th1():
time.sleep(5)
print("th1")
def th2():
time.sleep(10)
print("th2")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

Calling method from other class

I'm very new to PyQt5, and I'm trying to make an interactive gui for plotting of data. However, this problem may well be completely unrelated to PyQt5 and more a problem with my understanding of object oriented programming in general.
I have a MainWindow class, a SupportClass1 and a SupportClass2. When I make an instance of SupportClass1, I want to call the method DoSomething in the MainWindow class by referring to the object window, but I get the error message NameError: name 'window' is not defined.
I have no problems creating a method in the SupportClass2 and calling that from the MainWindow class so I get the impression that I have not instantiated the MainWindow class correctly which I don't understand as I thought I had defined window as an instace of the MainWindow class.
Can anyone help me understand what is wrong in my logic and how to solve this problem?
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys
import os
from random import randint
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.graphWidget = pg.PlotWidget()
self.x = list(range(100))
self.y = [randint(0,100) for _ in range(100)]
self.graphWidget.setBackground('w')
pen = pg.mkPen(color=(255, 0, 0))
self.data_line = self.graphWidget.plot(self.x, self.y, pen=pen)
self.button = QPushButton('Test')
self.button.clicked.connect(self.InstantiateSupportClasses)
self.gui_box = QVBoxLayout()
self.gui_box.addWidget(self.graphWidget)
self.gui_box.addWidget(self.button)
self.setLayout(self.gui_box)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test application')
self.show()
def InstantiateSupportClasses(self):
supp_class2 = SupportClass2()
print(supp_class2.GetVariable())
supp_class1 = SupportClass1()
def DoSomething(self):
print('I did something!')
class SupportClass1():
def __init__(self):
window.DoSomething
class SupportClass2():
def __init__(self):
self.some_variable = 5
def GetVariable(self):
return self.some_variable
def main():
app = QApplication(sys.argv)
app.setStyle('Fusion')
window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()```
I see that you are using the "window" object from the "SupportClass1" class.
but that class does not recognize this object one solution is to insert that object to the "SupportClass1()"
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys
import os
from random import randint
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.graphWidget = pg.PlotWidget()
self.x = list(range(100))
self.y = [randint(0,100) for _ in range(100)]
self.graphWidget.setBackground('w')
pen = pg.mkPen(color=(255, 0, 0))
self.data_line = self.graphWidget.plot(self.x, self.y, pen=pen)
self.button = QPushButton('Test')
self.button.clicked.connect(self.InstantiateSupportClasses)
self.gui_box = QVBoxLayout()
self.gui_box.addWidget(self.graphWidget)
self.gui_box.addWidget(self.button)
self.setLayout(self.gui_box)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test application')
self.show()
def InstantiateSupportClasses(self):
supp_class2 = SupportClass2()
print(supp_class2.GetVariable())
supp_class1 = SupportClass1(self)
def DoSomething(self):
print('I did something!')
class SupportClass1():
def __init__(self, window):
window.DoSomething()
class SupportClass2():
def __init__(self):
self.some_variable = 5
def GetVariable(self):
return self.some_variable
def main():
app = QApplication(sys.argv)
app.setStyle('Fusion')
window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Resizing a QWindow to fit contents

The main window of my PyQt5 application is set up with a text label along the top above a custom canvas widget which displays an image:
from PyQt5 import QtCore, QtGui, QtWidgets
class Canvas(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.image = None
def paintEvent(self, event):
qp = QtGui.QPainter(self)
if self.image:
qp.drawImage(0, 0, self.image)
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.canvas = Canvas()
self.label = QtWidgets.QLabel()
self.label.setText('foobar')
self.label.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Fixed)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.canvas)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
content = QtWidgets.QWidget()
content.setLayout(layout)
self.setCentralWidget(content)
self.load_image('a.jpg')
def load_image(self, filename):
image = QtGui.QImage(filename)
self.canvas.image = image
self.canvas.setFixedSize(image.width(), image.height())
self.update()
def keyPressEvent(self, event):
self.load_image('b.jpg')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
This looks like this, which is what I want:
When the canvas changes to display a smaller image, I want to shrink the window to fit accordingly. However, it looks like this:
It seems that the minimum size that I can give the window if I manually drag to resize it is the size that fits the contents, but why isn't it resizing to this automatically?
When a fixed size is set, it is used as sizeHint, and the latter is used by layouts to set the widget size. So the size of the canvas depends on the size of the widget, but you want the opposite. You must scale the image size to the window size:
from PyQt5 import QtCore, QtGui, QtWidgets
class Canvas(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.image = QtGui.QImage()
#property
def image(self):
return self._image
#image.setter
def image(self, image):
self._image = image
self.update()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
if not self.image.isNull():
image = self.image.scaled(
self.size(), QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation
)
qp.drawImage(0, 0, image)
class Window(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.canvas = Canvas()
self.label = QtWidgets.QLabel("foobar")
self.label.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.canvas)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
content = QtWidgets.QWidget()
content.setLayout(layout)
self.setCentralWidget(content)
self.load_image("a.jpg")
def load_image(self, filename):
image = QtGui.QImage(filename)
self.canvas.image = image
def keyPressEvent(self, event):
self.load_image('b.jpg')
super().keyPressEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

How can I open and close a new window from main window in PyQt?

I searched in Google usually many posts are talking about how to open a new window and not indicating how to close the window. I found several posts in this website but most of them are using dialog window which is not considered in my software.
I make a UI, which contains a spin box and a button, to demonstrate my problem. I can type in a number equal or less than 5 in spin box. When I click the button, a number of new windows will show up and how many windows will show up is depending on the number in the spin box. If I change to the number in spin box then click the button, the original windows will be closed and new windows will be shown.
Fox example, first I type in "2" in spin box then click the button. Then 2 new windows will show up. If I change the number in spin box to 3 then click the button, the original 2 windows will be closed and 3 new windows will show up.
Here is my main program code:
from PyQt5.QtWidgets import QApplication, QMainWindow
from uitest_review import Ui_MainWindow # import the UI module
# set up a class for main window
class window(QMainWindow):
def __init__(self, parent=None):
super(window, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.Open.clicked.connect(self.openwindow)
def openwindow(self):
windownum = self.ui.windownum.value()
print("open window num:", windownum)
opennewwindow = newwindow(self)
opennewwindow.show()
class newwindow(QMainWindow):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
if __name__ == "__main__":
app = QApplication([])
gui = window()
gui.show()
app.exec_()
Here is my UI code:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(816, 577)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 796, 537))
self.scrollAreaWidgetContents.setObjectName(\
"scrollAreaWidgetContents")
self.verticalLayout = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
spacerItem = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
self.verticalLayout.addItem(spacerItem)
self.windownum = QtWidgets.QSpinBox(self.scrollAreaWidgetContents)
self.windownum.setMaximum(5)
self.windownum.setObjectName("windownum")
self.verticalLayout.addWidget(self.windownum)
self.groupBox = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
self.groupBox.setTitle("")
self.groupBox.setObjectName("groupBox")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox)
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.Open = QtWidgets.QPushButton(self.groupBox)
font = QtGui.QFont()
font.setPointSize(14)
font.setBold(True)
font.setWeight(75)
self.Open.setFont(font)
self.Open.setObjectName("Open")
self.horizontalLayout.addWidget(self.Open)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem2)
self.verticalLayout.addWidget(self.groupBox)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.gridLayout.addWidget(self.scrollArea, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
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.Open.setText(_translate("MainWindow", "Open"))
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 create a new class (newwindow) in my main program and I can call this class to show a new window. But I cannot figure out how to detect how many windows is opened and how to close them. Does anyone can help me? Thank you so much.
I figured out by myself.
class window(QMainWindow):
def __init__(self, parent=None):
super(window, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.Open.clicked.connect(self.openwindow)
self.openedwin = []
def openwindow(self):
windownum = self.ui.windownum.value()
if windownum != 0:
if self.openedwin != []:
for window in self.openedwin:
window.close()
for repeat in range(windownum):
opennewwindow = newwindow(self)
# print("opennewwindow:", opennewwindow)
self.openedwin.append(opennewwindow)
opennewwindow.show()
# print("self.openedwin:", self.openedwin)
class newwindow(QMainWindow):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
if __name__ == "__main__":
app = QApplication([])
gui = window()
gui.show()
app.exec_()
I add a list self.openedwin = [] to save all window objects. I can use "window object".close() command to close the window before opening new one.