I use QT to build my GUI, and decorator to record the log. When I use the decorator on a slot, the GUI will crush.
The code is:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def LogInfos(func):
def wrapper(*s, **gs):
print('log')
ret = func(*s, **gs)
return ret
return wrapper
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout(self)
btn = QPushButton('test')
layout.addWidget(btn)
btn.clicked.connect(self.testSlot)
#LogInfos
def testSlot(self):
print('test slot')
#LogInfos
def testLog():
print('test log')
if __name__ == '__main__':
testLog()
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I has tested that the decorator function is OK, and the GUI is ok after remove the decorator.
See #ekhumoro's explanation of why, but using the decorated method in the slot is causing your problem due to an incorrect signature.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def LogInfos(func):
def wrapper(*s, **gs):
print('log')
ret = func(*s, **gs)
return ret
return wrapper
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout(self)
btn = QPushButton('test')
layout.addWidget(btn)
btn.clicked.connect(self.testSlot)
#LogInfos
def testSlot(self, checked=False):
print('test slot')
#LogInfos
def testLog():
print('test log')
if __name__ == '__main__':
testLog()
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Try it: btn.clicked.connect(lambda: self.testSlot())
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def LogInfos(func):
def wrapper(*s, **gs):
print('log')
ret = func(*s, **gs)
return ret
return wrapper
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout(self)
btn = QPushButton('test')
layout.addWidget(btn)
#btn.clicked.connect(self.testSlot)
btn.clicked.connect(lambda: self.testSlot())
#LogInfos
def testSlot(self):
print('test slot')
#LogInfos
def testLog():
print('test log')
if __name__ == '__main__':
testLog()
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Related
I need to execute a method whenever the user clicks the down arrow on the combo box. I've tried the signals listed in the documentations but none of the worked.
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.combo = QComboBox(self)
self.combo.signal.connect(self.mymethod)
self.show()
def mymethod(self):
print('hello world')
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
There is no signal that is emitted when the down arrow is pressed but you can create override the mousePressEvent method and verify that this element was pressed:
import sys
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QStyle,
QStyleOptionComboBox,
QVBoxLayout,
QWidget,
)
class ComboBox(QComboBox):
arrowClicked = pyqtSignal()
def mousePressEvent(self, event):
super().mousePressEvent(event)
opt = QStyleOptionComboBox()
self.initStyleOption(opt)
sc = self.style().hitTestComplexControl(
QStyle.CC_ComboBox, opt, event.pos(), self
)
if sc == QStyle.SC_ComboBoxArrow:
self.arrowClicked.emit()
class Window(QWidget):
def __init__(self):
super().__init__()
self.combo = ComboBox()
self.combo.arrowClicked.connect(self.mymethod)
lay = QVBoxLayout(self)
lay.addWidget(self.combo)
lay.setAlignment(Qt.AlignTop)
def mymethod(self):
print("hello world")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
I want to create a QApplication which is then exited using a keyboard shortcut. Then the python script should call another QApplication.
My issues currently is that I get this error when the second QApplication is about to run:
app2 = QApplication()
RuntimeError: Please destroy the QApplication singleton before creating a new QApplication instance.
I have the following structure:
| main.py
| Q1.py
| Q2.py
This is main.py:
import Q1 as record
import Q2 as display
def main():
record.main()
display.main()
if __name__ == "__main__":
main()
This is Q1 which creates the problem:
import sys
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
from PySide2 import QtMultimedia as qtmm
class MainWindow(qtw.QMainWindow):
def __init__(self):
super().__init__()
#Create Window layout with a sound widget
soundboard = qtw.QWidget()
soundboard.setLayout(qtw.QGridLayout())
self.setCentralWidget(soundboard)
sw = SoundWidget()
soundboard.layout().addWidget(sw)
#Window Dimensions
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.MinimumExpanding)
# Code ends here
self.show()
class SendOrderButton(qtw.QPushButton):
button_stylesheet = 'background-color: blue; color: white;'
def __init__(self):
super().__init__('Send Order')
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
self.setStyleSheet(self.button_stylesheet)
#self.clicked.connect(qtc.QCoreApplication.instance().quit)
def press_button(self):
if self.isEnabled():
self.setEnabled(False)
self.setText('Send Order')
else:
self.setEnabled(True)
self.setText('Sent')
class SoundWidget(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QGridLayout())
#Send Order Button
self.sendorder_button = SendOrderButton()
self.sendorder_button.setShortcut(qtg.QKeySequence('Tab'))
self.layout().addWidget(self.sendorder_button, 5, 0, 1, 2)
self.sendorder_button.clicked.connect(qtc.QCoreApplication.instance().quit)
def main():
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
This is Q2.py which has the second QApplication:
import sys
from PySide2.QtCore import (QAbstractTableModel, Slot)
from PySide2.QtWidgets import (QAction, QApplication, QMainWindow,QWidget)
class MainWindow(QMainWindow):
def __init__(self, widget):
QMainWindow.__init__(self)
# Exit QAction
exit_action = QAction("Exit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.exit_app)
#Slot()
def exit_app(self, checked):
sys.exit()
class CustomTableModel(QAbstractTableModel):
def __init__(self, data=None):
QAbstractTableModel.__init__(self)
class Widget(QWidget):
def __init__(self):
QWidget.__init__(self)
# Getting the Model
self.model = CustomTableModel()
def main():
app2 = QApplication()
widget = Widget()
window2 = MainWindow(widget)
window2.show()
sys.exit(app2.exec_())
if __name__ == "__main__":
app = QApplication()
widget = Widget()
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())
As noted in the comments a Qt application can only and should have a QApplication (you might not follow this rule but nothing guarantees that it works correctly) so you will have to restructure your code.
Assuming that you want the Q1 window to be first and when that window is closed then the Q2 window opens that does not imply at any time that you have to use several QApplication. The idea is to know when a window is closed and to be notified of it, to know when a window is closed then you must override the closeEvent method of the window and to make the notification you must send a signal.
Considering the above, the solution is:
├── main.py
├── Q1.py
└── Q2.py
main.py
import sys
from PySide2 import QtWidgets as qtw
import Q1 as record
import Q2 as display
def main():
app = qtw.QApplication(sys.argv)
w1 = record.get_mainwindow()
w2 = display.get_mainwindow()
w1.closed.connect(w2.show)
w1.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Q1.py
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
class MainWindow(qtw.QMainWindow):
closed = qtc.Signal()
def __init__(self):
super().__init__()
# Create Window layout with a sound widget
soundboard = qtw.QWidget()
soundboard.setLayout(qtw.QGridLayout())
self.setCentralWidget(soundboard)
sw = SoundWidget()
soundboard.layout().addWidget(sw)
# Window Dimensions
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.MinimumExpanding)
sw.sendorder_button.clicked.connect(self.close)
def closeEvent(self, event):
self.closed.emit()
super().closeEvent(event)
class SendOrderButton(qtw.QPushButton):
button_stylesheet = "background-color: blue; color: white;"
def __init__(self):
super().__init__("Send Order")
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
self.setStyleSheet(self.button_stylesheet)
def press_button(self):
if self.isEnabled():
self.setEnabled(False)
self.setText("Send Order")
else:
self.setEnabled(True)
self.setText("Sent")
class SoundWidget(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QGridLayout())
# Send Order Button
self.sendorder_button = SendOrderButton()
self.sendorder_button.setShortcut(qtg.QKeySequence("Tab"))
self.layout().addWidget(self.sendorder_button, 5, 0, 1, 2)
def get_mainwindow():
window = MainWindow()
return window
if __name__ == "__main__":
import sys
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Q2.py
from PySide2 import QtCore as qtc
from PySide2 import QtWidgets as qtw
class MainWindow(qtw.QMainWindow):
def __init__(self, widget):
super().__init__()
file_menu = self.menuBar().addMenu("&File")
# Exit QAction
exit_action = qtw.QAction("Exit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
class CustomTableModel(qtc.QAbstractTableModel):
pass
class Widget(qtw.QWidget):
def __init__(self):
super().__init__()
# Getting the Model
self.model = CustomTableModel()
def get_mainwindow():
widget = Widget()
window2 = MainWindow(widget)
return window2
if __name__ == "__main__":
import sys
app = qtw.QApplication()
widget = Widget()
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())
I am stack trying to include MenuBar from separate file and trying to connect with function
I include some code that I have the same problem
foo.py
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUiType
import os
import sys
from foomenu import menu
FROM_MAIN, _ = loadUiType(os.path.join(os.path.dirname(__file__), "SalesGui.ui"))
class Main(QMainWindow, FROM_MAIN):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
self.MyMenu = menu(self)
self.MyMenu.NewProduct.triggered.connect(self.NewProduct())
def NewProduct(self):
print("foo")
def main():
app = QApplication(sys.argv)
window = Main()
window.show()
app.exec_()
if __name__ == '__main__':
try:
main()
except Exception as why:
print(why)
and foomenu.py
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QMenu
def menu(self):
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('FooMenu')
NewProduct = QAction(QIcon('icons/exit.png'), 'Foo', self)
NewProduct.setShortcut('Ctrl+Q')
NewProduct.setStatusTip('FooAction')
fileMenu.addAction(NewProduct)
When Trying to connect the "NewProduct" button with "New Product" function I get the following error
'NoneType' object has no attribute 'NewProduct'
Try it:
import os
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QMenu
#from PyQt5.uic import loadUiType
#from foomenu import menu
#FROM_MAIN, _ = loadUiType(os.path.join(os.path.dirname(__file__), "SalesGui.ui"))
def menu(self):
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('FooMenu')
self.NewProduct = QAction(QIcon('exit.png'), 'Foo', self) # 'icons/exit.png' # + self
self.NewProduct.setShortcut('Ctrl+Q')
self.NewProduct.setStatusTip('FooAction')
fileMenu.addAction(self.NewProduct)
return self.NewProduct # +++
class Main(QMainWindow):#, FROM_MAIN):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
# self.setupUi(self)
self.MyMenu = menu(self)
# self.MyMenu.NewProduct.triggered.connect(self.funcNewProduct) # - ()
self.MyMenu.triggered.connect(self.funcNewProduct) # +
def funcNewProduct(self):
print("foo")
qApp.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Main()
window.show()
app.exec_()
It is much more elegant to subclass and setup the menubar in this another python file then import and instance this class in your file that holds the Main class.
In the MyMenu class you can binding the signal a socket locally that can provide from here the sender object for the parent class.
BTW you can iterate over the action object of the menubar/menus but it is much more harder to maintain and control then the explicit describe where to connect the potential different actions.
Menu.py:
class MyMenu(QMenuBar):
new_product_clicked = Signal(object)
def __init__(self, parent=None):
super(MyMenu, self).__init__(parent)
file_menu = QMenu("File menu", self)
new_product_action = QAction('Foo', self)
new_product_action.setShortcut('Ctrl+Q')
new_product_action.setStatusTip('FooAction')
new_product_action.triggered.connect(self.new_product_clicked)
file_menu.addAction(new_product_action)
self.addMenu(file_menu)
def new_product_clicked(self):
""" Call a method from the parent class. """
self.new_product_clicked.emit(self.sender())
Main.py:
from Menu import MyMenu
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
my_menu = MyMenu(self)
self.setMenuBar(my_menu)
self.my_menu.new_product_clicked.connect(self.product_clicked)
def product_clicked(self, action):
""" Socket for the clicked action """
clicked_action = action
print clicked_action.text()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Main()
window.show()
app.exec_()
I have an application on pyqt5. And if I click on button time lcd display start to countdown with thread I created. After the countdown finished I cant use the same thread for the same action. How can I terminate that thread and start it again with clicking button
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window,self).__init__()
self.button1=QPushButton(self)
self.lcd=QLCDNumber(self)
self.tbutton1=threading.Thread(target=self.timing)
def initUI(self):
self.lcd.setVisible(False)
self.button1.clicked.connect(self.timing)
def timing(self):
self.tbutton1.start()
self.lcd.setVisible(True)
timing=self.spin.value()
for i in range(timing,-1,-1):
time.sleep(1)
if(i<10):
self.lcd.display("00:0{}".format(i))
else:
self.lcd.display("00:{}".format(i))
self.lcd.setVisible(False)
if __name__=='__main__':
app=QApplication(sys.argv)
win=window()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import threading
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window,self).__init__()
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.button1 = QPushButton("Start to countdown", self)
self.lcd = QLCDNumber(self)
self.spin = QSpinBox(self)
grid = QGridLayout(centralWidget)
grid.addWidget(self.lcd)
grid.addWidget(self.spin)
grid.addWidget(self.button1)
self.initUI()
def initUI(self):
# self.lcd.setVisible(False)
self.button1.clicked.connect(self.timing)
def timing(self):
self.tbutton1 = threading.Thread(target=self.timingThread)
self.tbutton1.start()
# self.lcd.setVisible(True)
def timingThread(self):
timing=self.spin.value()
for i in range(timing, -1, -1):
# time.sleep(1)
QtCore.QThread.msleep(1000)
if(i<10):
self.lcd.display("00:0{}".format(i))
else:
self.lcd.display("00:{}".format(i))
# self.lcd.setVisible(False)
if __name__=='__main__':
app = QApplication(sys.argv)
win = window()
win.show()
sys.exit(app.exec_())
I have a bit when try change new window UI with effect fade. I added effect on closeEvent of mainwindow but it doen't work.
This is my code:
library used:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import uic
load ui
uifile_1 = 'home.ui'
form_1, base_1 = uic.loadUiType(uifile_1)
uifile_2 = 'plate.ui'
form_2, base_2 = uic.loadUiType(uifile_2)
Class Home page:
class HomePage(base_1, form_1):
def __init__(self):
super(base_1,self).__init__()
self.setupUi(self)
#add button for click next page
self.btn_start = QPushButton(self)
self.btn_start.clicked.connect(self.change)
self._heightMask = self.height()
self.animation = QPropertyAnimation(self, b"heightPercentage")
self.animation.setDuration(1000)
self.animation.setStartValue(self.height())
self.animation.setEndValue(-1)
self.animation.finished.connect(self.close)
self.isStarted = False
def change(self):
self.plate = PlatePage()
self.plate.show()
self.close()
#pyqtProperty(int)
def heightMask(self):
return self._heightMask
#heightMask.setter
def heightPercentage(self, value):
self._heightMask = value
rect = QRect(0, 0, self.width(), self.heightMask)
self.setMask(QRegion(rect))
def closeEvent(self, event):
if not self.isStarted:
self.animation.start()
self.isStarted = True
event.ignore()
else:
self.closeEvent(self, event)
Class Plate Page
class PlatePage(base_2, form_2):
def __init__(self):
super(base_2, self).__init__()
self.setupUi(self)
self.show()
Please have a look and give me some solution.
Thank You
Try it:
from PyQt5.QtCore import QPropertyAnimation, QThread
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
class Window(QWidget):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.resize(400, 400)
layout = QVBoxLayout(self)
layout.addWidget(QPushButton('Button', self))
self.animation = QPropertyAnimation(self, b'windowOpacity')
self.animation.setDuration(1000)
self.isStarted = False
self.doShow()
def doShow(self):
try:
self.animation.finished.disconnect(self.close)
except:
pass
self.animation.stop()
self.animation.setStartValue(0)
self.animation.setEndValue(1)
self.animation.start()
def closeEvent(self, event):
if not self.isStarted:
self.animation.stop()
self.animation.finished.connect(self.close)
self.animation.setStartValue(1)
self.animation.setEndValue(0)
self.animation.start()
self.isStarted = True
event.ignore()
else:
event.accept()
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())