How to receive close event in following code?
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.view = QUiLoader().load("sample.ui", self)
self.view.show()
def closeEvent(self, e):
print "close event recieved"
def main():
app = QApplication(sys.argv)
a=Main()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
If I convert sample.ui to sample.py using pyside-uic and importing this into main.py then I was able to receive close event.
from sample import Ui_MainWindow
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
def closeEvent(self, e):
print "close event recieved"
app = QApplication(sys.argv)
a=Main()
a.show()
sys.exit(app.exec_())
The second example works because it effectively becomes a subclass of the top-level class from Qt Designer. By contrast, the first example uses composition rather than subclassing, which puts all the gui elements inside an internal namespace. The Main class is just a container that acts as the parent of the view widget, and is never actually shown (which in turn means it doesn't receive any close events).
In PyQt, the uic module has several funtions which allow you to work around these issues, but there is currently nothing like that in PySide. Instead, you have to roll your own function. See this answer for an explanation of how to do that.
Alternatively, you could change the top-level class in Qt Designer to a QWidget, and then make view the central widget of your Main class. This is a lot less flexible than the above method, though.
Related
Id like to ask how to connect 2 different classes. I have the following codes in 2 classes (2 classes because i have created 2 different interface. 1 is the QMainwindow and the other 1 is QWidget).
Here's the code:
class MainWindow(QMainWindow, Ui_MainWindow):
def open_inv_search_form(self):
self.window = QtWidgets.QWidget()
self.ui = Ui_Inv_Search()
self.ui.setupUi(self.window)
self.window.show()
MainWindow.setEnabled(False)
class Inv_Search(QWidget, Ui_Inv_Search):
def __init__(self,):
super(Inv_Search, self).__init__()
self.btn_close_search.clicked.connect(self.close_inv_search_form)
def close_inv_search_form(self):
Inv_Search.hide()
MainWindow.setEnabled(True)
The idea is when SEARCH button is clicked in MainWindow, Inv_Search will pop up while MainWindow will be disabled. I have done this part correctly. When CLOSE button is clicked, Inv_Search will be hide and MainWindow will be enabled. However, when CLOSE button is clicked, nothing happened. And there is no error at all.
UPDATE
I was able to successfully do what I wanted. Here's the changes I did. Let me know if this is fine or can be better. Nevertheless, this code works.
class MainWindow(QMainWindow, Ui_MainWindow):
Inv_Search.show() #called to connect to the new window. This is the key since what i previously did only call the ui, but not connect it to the class itself.
MainWindow.setEnabled(False)
class Inv_Search(QWidget, Ui_Inv_Search):
def __init__(self,):
super(Inv_Search, self).__init__()
self.setupUi(self)
self.btn_close_search.clicked.connect(self.close_inv_search_form)
def close_inv_search_form(self):
Inv_Search.close() # I cant figure out how can I use the self but this works fine
MainWindow.setEnabled(True)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
MainWindow = MainWindow()
Inv_Search = Inv_Search() #added this one
#ui = Ui_MainWindow()
#ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
When you call Inv_Search.hide() and MainWindow.setEnabled(True) in close_inv_search_form, you call the methods on the class itself not on the instance, which is what you actually have to.
from PyQt5.QtCore import qApp
class MainWindow(QMainWindow, Ui_MainWindow):
def open_inv_search_form(self):
self.window = QtWidgets.QWidget()
self.window.ui = Ui_Inv_Search() # You have to set the attributes of the
self.window.ui.setupUi(self.window) # "window" not the instance of MainWindow
self.window.show()
self.setEnabled(False)
class Inv_Search(QWidget, Ui_Inv_Search):
def __init__(self,):
super(Inv_Search, self).__init__()
self.btn_close_search.clicked.connect(self.close_inv_search_form)
def close_inv_search_form(self):
self.hide() # Use the method "hide" on the instance
qApp.setEnabled(True) # Use the method "setEnabled" on the instance
if __name__ == "__main__" :
app = QApplication()
main = MainWindow() # This is the instance that can be referred to by qApp
main.exec_()
I am assuming that there is more code and a .ui file somewhere. It looks like this line
Inv_Search.hide()
should be changed to
self.hide()
Also, I think that because you need to call the method on the instance, not the class.
self.ui = Ui_Inv_Search()
should probably be
self.ui = Inv_Search()
You are doing a similar thing with MainWindow. It's a little more difficult here, because you will need to have an instance of MainWindow stored somewhere accessible. Although you may be able to access the MainWindow instance through QtWidget.parentWidget, in Python I prefer to just pass the instance to the constructor. So
def __init__(self, mainWindow):
self.mainWindow = mainWindow
# ... all the other stuff too
as your Inv_Search constructor, and
self.ui = Inv_Search(self)
# notice the new ^ argument
in your MainWindow constructor. And then
self.mainWindow.setEnabled(True)
in your class method. Also, your argument signature is wrong for the clicked signal. Use
def close_inv_search_form(self, checked=None):
# Need this ^ argument, even if you don't use it.
In reality, it seems like the functionality you are trying to accomplish is best suited for a modal dialog, such as that provided by QDialog, which will natively handle many of the effect I think you are looking for.
I've created an app which has an main window and the possibility to open an dialog (question, error and so on). I'm not using QMessageBox.warning() or QMessageBox.question() and so on because I wanted to customize the dialogs a bit.
But every time I open a new Dialog, in the Windows task bar (I'm working on Windows 10) a new 'tab' is opened, which is a little bit annoying.
My code (shortened):
from PySide import QtCore, QtGui
import sys
class MessageBox:
def __init__(self, title, message):
msg = QtGui.QMessageBox()
flags = QtCore.Qt.Dialog
flags |= QtCore.Qt.CustomizeWindowHint
flags |= QtCore.Qt.WindowTitleHint
msg.setWindowFlags(flags)
msg.setWindowTitle(title)
msg.setText(message)
msg.exec_()
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.show()
MessageBox("Title", "My message here")
if __name__ == "__main__":
app = QtGui.QApplication([])
window = MainWindow()
sys.exit(app.exec_())
Note: Normally, the dialog is called from an menu or button.
Question: How can I make the dialog appear in the main window without creating a new 'task bar tab'?
The solution was quite simple: Passing an reference of QMainWindow to the constructor of QDialog will do the job, e.g:
class MessageBox(QtGui.QDialog):
def __init__(self, parent, title, message, icon="info"):
super(MessageBox, self).__init__(parent)
...
and then calling the dialog from an class that inherits from QMainWindow:
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
#connect button with function, e.g.:
mybutton.clicked.connect(self.open_dialog)
def open_dialog(self):
MessageBox(self)
Maybe this helps anyone!
If you set the parent of the QDialog to the window, it will only show as one item on the task bar. This is generally the first argument to QMessageBox.
class MessageBox:
def __init__(self, parent, title, message):
msg = QtGui.QMessageBox(parent)
Also, if you really want to create a custom dialog, you might as well just subclass from QDialog.
Can anyone tell me why this code is not working? The Test4 class is my converted simple UI:
import sys
import Test4
from PyQt4 import QtGui, QtCore
class UiViewer(QtGui.QApplication, Test4.Ui_Dialog):
def __init__(self, parent=None):
return super(UiViewer, self).__init__(parent)
self.setupUi(self)
def main(self):
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
uiViewer = UiViewer()
uiViewer.main()
app.exec_()
first of all
you need to use
if __name__ == '__main__'
not
if name == 'main':
and also adding the Error Message and describing the behavior of the application when you run it will help trace the problem.
from your question, it can be any number of problems.
Your UiViewer class needs to inherit from the same class as the top-level widget in Qt Designer (presumably QDialog, in your case, but it could also be a QMainWindow or a QWidget):
class UiViewer(QtGui.QDialog, Test4.Ui_Dialog):
def __init__(self, parent=None):
super(UiViewer, self).__init__(parent)
self.setupUi(self)
And note that you must not put return before the super call, otherwise the __init__ function will exit at that point, meaning the rest of its code won't be executed (in particular, setupUi would not be called).
I have a QMainWindow that launches a QDialog everytime I click on a button and I can't figure out why the python binary crashes when I close the QMainWindow while one or more dialogs are open.
It's not a complex Qt app and I'm really struggling trying to understand what happens.
Here's the code:
# dependency modules
from PyQt4 import QtGui
import sys
# custom modules
from ui import SingleOrderUI, DashBoardUI
class SingleOrder(QtGui.QDialog, SingleOrderUI.Ui_SingleOrder):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
class DashBoard(QtGui.QMainWindow, DashBoardUI.Ui_DashBoard):
def __init__(self):
QtGui.QMainWindow.__init__(self)
super(DashBoard, self).__init__()
# setup UI
self.setupUi(self)
self.newOrderBtn.clicked.connect(self.newOrder)
def newOrder(self):
print 'New order clicked'
so = SingleOrder(self)
so.show()
app = QtGui.QApplication(sys.argv)
window = DashBoard()
window.show()
sys.exit(app.exec_())
Any help would be appreciated.
EDIT: When launched using ipython, the dialogs are still showing after I close the QMainWindow, so that's maybe where the issue comes from.
I give the QMainWindow as a parent argument to the QDialog, I thought that was enough to have them killed when the QMainWindow is closed.
Okay, I've found a workaround for that but I'm not sure if it's the right way to do it.
On my DashBoard init method, I've added a python list that will store all the opened Dialogs:
def __init__(self):
QtGui.QMainWindow.__init__(self)
super(DashBoard, self).__init__()
# setup UI
self.setupUi(self)
self.newOrderBtn.clicked.connect(self.newOrder)
self.soTab = []
Then, in the same class, I defined a method to handle the closeEvent and close all the dialogs.
def closeEvent(self, event):
for so in self.soTab:
if so:
so.close()
event.accept()
I have a QTreeWidget which needs to be populated with a large sum of information. So that I can style it and set it up the way I really wanted, I decided I'd create a QWidget which was styled and dressed all pretty-like. I would then populate the TreeWidget with generic TreeWidgetItems and then use setItemWidget to stick the custom QWidgets in the tree. This works when the QWidgets are called inside the main PyQt thread, but since there is a vast sum of information, I'd like to create and populate the QWidgets in the thread, and then emit them later on to be added in the main thread once they're all filled out. However, when I do this, the QWidgets appear not to be getting their parents set properly as they all open in their own little window. Below is some sample code recreating this issue:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class ItemWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
btn = QPushButton(self)
class populateWidgets(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
widget = ItemWidget()
for x in range(5):
self.emit(SIGNAL("widget"), widget)
class MyMainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.tree = QTreeWidget(self)
self.tree.setColumnCount(2)
self.setCentralWidget(self.tree)
self.pop = populateWidgets()
self.connect(self.pop, SIGNAL("widget"), self.addItems)
self.pop.start()
itemWidget = QTreeWidgetItem()
itemWidget.setText(0, "This Works")
self.tree.addTopLevelItem(itemWidget)
self.tree.setItemWidget(itemWidget, 1, ItemWidget(self))
def addItems(self, widget):
itemWidget = QTreeWidgetItem()
itemWidget.setText(0, "These Do Not")
self.tree.addTopLevelItem(itemWidget)
self.tree.setItemWidget(itemWidget, 1, widget)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
ui = MyMainWindow()
ui.show()
sys.exit(app.exec_())
As you can see, doing it inside MyMainWindow is fine, but somehow things go awry once it gets processed in the thread and returns. Is this possible to do? If so, how do I properly parent the ItemWidget class inside the QTreeWidgetItem? Thanks in advance.
AFAICT Qt doesn't support the creation of QWidgets in a thread other than the thread where the QApplication object was instantiated (i.e. usually the main() thread). Here are some posts on the subject with responses from Qt developers:
http://www.qtcentre.org/archive/index.php/t-27012.html
http://www.archivum.info/qt-interest#trolltech.com/2009-07/00506/Re-(Qt-interest)-QObject-moveToThread-Widgets-cannot-be-moved-to-a-new-thread.html
http://www.archivum.info/qt-interest#trolltech.com/2009-07/00055/Re-(Qt-interest)-QObject-moveToThread-Widgets-cannot-be-moved-to-a-new-thread.html
http://www.archivum.info/qt-interest#trolltech.com/2009-07/00712/Re-(Qt-interest)-QObject-moveToThread-Widgets-cannot-be-moved-toa-new-thread.html
(if it were possible, the way to do it would be to call moveToThread() on the QWidgets from within the main thread to move them to the main thread -- but apparently that technique doesn't work reliably, to the extent that QtCore has a check for people trying to do that prints a warning to stdout to tell them not to do it)