PyQt - Hide MainWindow and show QDialog without the taskbar icon disappearing - python

I've been using the code from this example PyQt: How to hide QMainWindow:
class Dialog_02(QtGui.QMainWindow):
def __init__(self, parent):
super(Dialog_02, self).__init__(parent)
# ensure this window gets garbage-collected when closed
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
...
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtGui.QMainWindow):
...
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
It works, however when opening a second window, the window's task bar icon doesn't show. I've tried using QtGui.QDialog for the Dialog_02 as well but that gives me the same result.
How do I go about solving this?
Edit: I'm on Windows 10

Just guessing (because I don't know what platform you're on, and I don't use a task-bar myself, so I cant really test it), but try getting rid of the parent:
class Dialog_02(QtGui.QMainWindow):
def __init__(self, other_window):
super(Dialog_02, self).__init__()
# ensure this window gets garbage-collected when closed
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self._other_window = other_window
...
def closeAndReturn(self):
self.close()
self._other_window.show()

Related

Opening a QDialog and saving last state

I am trying to open a QDialog from a QMainWindow, and after closing the `QDialog, if I need to open it again, it has to open and show the same information that had when I close it.
Here is the code of the QMainWindow:
class A (QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
#I create a QPushButton to open the QDialog
self.axes1 = self.figure_canvas.figure.add_axes ([0.8, 0.01, 0.19, 0.05])
self.button = QPushButton(self.axes1,"Open Dialog")
self.button.on_clicked(self.OpenDialog)
#This is the method to open the QDialog which is in another module
def OpenDialog(self, event):
text = configurePort.ConfigurePort.retrieve_data(self)
print text
What this code does is create a button in my QMainWindow and when I click it, it opens a QDialog, which is created in another module. And this is the code of the QDialog:
class ConfigurePort(QDialog):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
uic.loadUi("configurePort.ui", self)
#I create a button to check active ports and show them
self.connect(self.btn_checkconn, SIGNAL("clicked()"), self.check_ports)
#This method calls another class which opens another QDialog
#and I select the port that I want
def check_ports(self):
self.check_serial = CheckPorts(self)
self.check_serial.exec_()
#After selecting the port, when I close the QDialog of the class named above
#the port´s name appears in the first QDialog
#classmethod
def retrieve_data(cls, parent = None):
dlg = cls(parent)
dlg.exec_()
text = dlg.getPortText()
return text
def closeEvent(self, event):
#Here is where I need to write the code to close the QDialog
#and it does not has to be an event
In the method, closeEvent, I need to write the necessary code, so I can close the window, and using the same button that I use to open it, open it again with the last information that it showed when I closed it.
I have tried to use QSettings but it did not worked (maybe I used it wrong). And I tried the show() and hide() classes of PyQt too, but it did not work. Hope you can help me.
----- EDIT -----
I edited the code of above. and I added some methods for a better understanding. So, i open the QDialog called ConfigurePort and it shows this:
The red circle, surrounds the port´s name. It is shown in a QLabel,and I take this text from the QDialog and then print it when I close the QDialog. I acomplish this thanks to a question I asked before, wich is in this link:
Getting data from child using PyQt
The check_port method shown in the code above, opens another QDialog that works great. With this I can select the ports that I need in my pc. So, this does not matter.
So, after closing the QDialog(and selecting for example "COM3", as you can see in the picture), I need to open it again, and see the same information that was shown before I closed it.
I tried to add this lines, using QSettings :
self.settings = QSettings("MyCompany", "MyApp")
if not self.settings.value("windowsState") == None:
self.restoreState(self.settings.value("windowState"))
But as I said before, I think that I did not use it right, but I hope that I solve this using something simpler.
----- EDIT 2 -----
Thank to the help of #Brendan Abel I have this code:
class ConfigurePort(QDialog):
def __init__(self, parent):
super(ConfigurePort, self).__init__(parent)
uic.loadUi("configurePort.ui", self)
self.myValue = 10
self.restoreSettings()
self.connect(self.btn_checkconn, SIGNAL("clicked()"), self.check_ports)
self.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect(self.close)
self.buttonBox.button(QDialogButtonBox.Ok).clicked.connect(self.closeEvent)
self.iniUi()
def check_ports(self):
pass
def iniUi(self):
pass #I just create some QLabels in here
#classmethod
def retrieve_data(cls, parent = None):
dlg = cls(parent)
dlg.exec_()
text = dlg.getPortText()
return text
def closeEvent(self, event):
self.saveSettings()
super(QDialog,self).closeEvent(event)
def saveSettings(self):
settings = QSettings("MyOrg", "MyApp")
settings.setValue("myValue", self.myValue)
def restoreSettings(self):
settings = QSettings("MyOrg", "MyApp")
self.myValue = settings.value("myValue", self.myValue)
This gives me this error: TypeError: QWidget.closeEvent(QCloseEvent): argument 1 has unexpected type 'bool'
I know that I am missing something, but I can not see it.
There are a couple ways you could persist this data Generally, to persist data across sessions, you use QSettings and load the data in the __init__ and save it in the closeEvent method
Generally it looks something like this. This also assumes your using the v2 version of the QVariant api; otherwise, the results returned from QSettings.value is going to be a QVariant and you'll need to cast it to the appropriate python type. If you're using a recent version of PyQt then you should be on v2, but if not you can force it by sticking this at the top of your file
import sip
sip.setapi('QVariant', 2)
sip.setapi('QString', 2)
class MyDialog(QDialog):
def __init__(self, parent):
super(MyDialog, self).__init__(parent)
self.myvalue = 10
self.restoreSettings()
def closeEvent(self, event):
self.saveSettings()
super(MyDialog, self).closeEvent(event)
def saveSettings(self):
settings = QSettings('myorg', 'myapp')
settings.setValue('myvalue', self.myvalue)
def restoreSettings(self):
settings = QSettings('myorg', 'myapp')
self.myvalue = settings.value('myvalue', self.myvalue)
EDIT:
The error in your code is caused by this:
self.buttonBox.button(QDialogButtonBox.Ok).clicked.connect(self.closeEvent)
You shouldn't be calling or connecting to closeEvent directly. Instead, you should connect to .close or .accept
self.buttonBox.button(QDialogButtonBox.Ok).clicked.connect(self.accept)
You need to instantiate the ConfigurePort class then the self.configurePortDialog object should keep consistent. You will need to make sure if you have the user enter data that a cancel does not store the data and that an "ok" stores the data, but I not sure what you are putting in your dialog.
class A (QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
#I create a QPushButton to open the QDialog
self.button = QPushButton("Open Dialog")
self.button.on_clicked(self.OpenDialog)
self.configurePortDialog = configurePort.ConfigurePort(parent=self)
self.configurePortDialog.accepted.connect(self.get_data)
#This is the method to open the QDialog which is in another module
def OpenDialog(self, event):
self.configurePortDialog.show()
#QtCore.Slot()
def get_data(self)
text = self.configurePortDialog.retrieve_data()
print text

Python program works, but it shows an error. Why?

I am new in programming and I have done my research on this website and others, but I can't find anything helpful for my problem. I am writing a Python program with several PyQt windows opening when different buttons are pressed. This is my program:
import sys, os,
from PyQt4 import QtCore, QtGui, uic
Ui_IntroWindow = uic.loadUiType('introduction.ui')[0]
Ui_ElmWindow = uic.loadUiType('elm.ui')[0]
Ui_ClueWindow = uic.loadUiType('pistaelm.ui') [0]
Ui_ButtonWindow = uic.loadUiType('firtsguibutton.ui')[0]
class IntroWindow(QtGui.QMainWindow, Ui_IntroWindow):
def __init__ (self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.continuar.clicked.connect(self.continuar_clicked)
def continuar_clicked(self):
window = ElmWindow(self)
window.show()
window.exec_()
self.close()
class ElmWindow(QtGui.QMainWindow, Ui_ElmWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.bpista.clicked.connect(self.pista)
self.bcontinuar.clicked.connect(self.continuar)
def pista(self):
pistaelm = ClueWindow(self)
pistaelm.show()
pistaelm.exec_()
def continuar(self):
elemento = str(self.elemento.text())
main = ButtonWindow(self)
if elemento == 'cobalto':
main.show()
main.exec_()
self.close()
class ClueWindow(QtGui.QMainWindow, Ui_ClueWindow):
def __init__ (self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
class ButtonWindow(QtGui.QMainWindow, Ui_ButtonWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
app = QtGui.QApplication(sys.argv)
myWindow = IntroWindow()
myWindow.show()
app.exec_()
I have several sub windows created, for example in:
def continuar_clicked(self):
window = ElmWindow(self)
window.show()
window.exec_()
self.close()
If i don't write
window.exec_()
the window will open, but the buttons won't work. But when i write it I get an error:
Traceback (most recent call last):
File "C:\Users\Work\Desktop\Project\project.py", line 19, in continuar_clicked
window.exec_()
AttributeError: 'ElmWindow' object has no attribute 'exec_'
How can I stop the error?
Objects derived from QMainWindow do not have a method exec_(). This is why you see the exception.
The reason this exception is modifying the behaviour of your program is because when an exception is raised, the rest of the slot currently being executed is not run. So your continuar_clicked method runs until it hits the line with the missing method, and stops.
This points to the fact that not calling self.close() in continuar_clicked keeps your GUI working. Calling self.close() is apparently breaking the program.
SO what does this mean? Well it points to a bad object hierarchy. You are spawning new windows, that are children of an existing window, and then closing the parent window. Quite possibly the parent window is being deleted, depending on whether the Qt.WA_DeleteOnClose attribute is set to true for your windows.
So I would suggest redesigning your program. Perhaps have a parent window that is always open, or write a window managing class which handles the creation/closing of all windows (eg a window object calls a method from your window managing object to close the current window and open a new window).
Ultimately how you structure your code will be up to you as it is difficult to gauge the details of your program from a minimal example

How to connect to parent SIGNAL in PyQt?

I have a MainWindow that looks like this:
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
self.setupUi(self)
self.showMaximized()
menu=mainMenu.MainMenu()
classification=classificationMain.ClassificationMain()
self.stackedWidget.addWidget(menu)
self.stackedWidget.addWidget(classification)
self.stackedWidget.setCurrentWidget(menu)
self.stackedWidget.showFullScreen()
#connections
menu.pushButton.clicked.connect(self.showClassification)
classification.backButton.clicked.connect(self.showMainWindow)
def showClassification(self ):
self.stackedWidget.setCurrentIndex(3)
def showMainWindow(self):
self.stackedWidget.setCurrentIndex(2)
The MainWindows waits for signal from the rest of the dialogs. Now, the Classification dialog has another StackedWidget in it, since it works as a main window for an important part of the application. It looks like:
class ClassificationMain(QDialog, Ui_Dialog):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
self.setupUi(self)
choose=choosePatient.ChoosePatient()
self.stackedWidget.addWidget(choose)
self.stackedWidget.setCurrentWidget(choose)
Now, I want to reload the data inside ChoosePatient every time the button "Show Classification" from MainMenu is clicked, but now the data is loaded only once in the line classification=classificationMain.ClassificationMain() of MainWindow.
I was thinking I had to connect a slot inside ChoosePatient with the click of "Show Classification" button inside MainMenu, but I would need an instance of MainMenu, which is not possible.
How can a method of ChoosePatient can be execute every time the button in the "parent" window is clicked? (also, please tell me if this is not the right way to work with pyqt windows)
You need to save references to your composed widgets, and also to expose some public methods to the parents:
class ClassificationMain(QDialog, Ui_Dialog):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
self.setupUi(self)
self.chooseWidget=choosePatient.ChoosePatient()
self.stackedWidget.addWidget(self.chooseWidget)
self.stackedWidget.setCurrentWidget(self.chooseWidget)
def reloadPatients(self):
# whatever your operation should be on the ChoosePatient
self.chooseWidget.reload()
# MAIN WINDOW
def __init__(self, parent = None):
...
self.classification=classificationMain.ClassificationMain()
self.stackedWidget.addWidget(self.classification)
...
#connections
menu.pushButton.clicked.connect(self.showClassification)
def showClassification(self ):
self.stackedWidget.setCurrentIndex(3)
self.classification.reloadPatients()
You could also just skip the reloadPatients method and connect to the ChoosePatient directly if you want:
def showClassification(self ):
self.stackedWidget.setCurrentIndex(3)
self.classification.chooseWidget.reload()
My personal opinion is to make your custom classes wrap up the internal functionality nicely so that you only need to interface with it over the custom class, and not dig into its internals. That way you can change how it works inside without breaking the main window.

Open a second window in PyQt

I'm trying to use pyqt to show a custom QDialog window when a button on a QMainWindow is clicked. I keep getting the following error:
$ python main.py
DEBUG: Launch edit window
Traceback (most recent call last):
File "/home/james/Dropbox/Database/qt/ui_med.py", line 23, in launchEditWindow
dialog = Ui_Dialog(c)
File "/home/james/Dropbox/Database/qt/ui_edit.py", line 15, in __init__
QtGui.QDialog.__init__(self)
TypeError: descriptor '__init__' requires a 'sip.simplewrapper' object but received a 'Ui_Dialog'
I've gone over several online tutorials, but most of them stop just short of showing how to use a non built-in dialog window. I generated the code for both the main window and the dialog using pyuic4. What I think should be the relevant code is below. What am I missing here?
class Ui_Dialog(object):
def __init__(self, dbConnection):
QtGui.QDialog.__init__(self)
global c
c = dbConnection
class Ui_MainWindow(object):
def __init__(self, dbConnection):
global c
c = dbConnection
def launchEditWindow(self):
print "DEBUG: Launch edit window"
dialog = QtGui.QDialog()
dialogui = Ui_Dialog(c)
dialogui = setupUi(dialog)
dialogui.show()
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
conn = sqlite3.connect('meds.sqlite')
c = conn.cursor()
self.ui = Ui_MainWindow(c)
self.ui.setupUi(self)
def main():
app = QtGui.QApplication(sys.argv)
program = StartQT4()
program.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Bonus question: since it looks like you can't pass arguments in pyqt function callbacks, is setting something which would otherwise be passed as an argument (the poorly named "c") to be global the best way to get information into those functions?
I've done like this in the past, and i can tell it works.
assuming your button is called "Button"
class Main(QtGui.QMainWindow):
''' some stuff '''
def on_Button_clicked(self, checked=None):
if checked==None: return
dialog = QDialog()
dialog.ui = Ui_MyDialog()
dialog.ui.setupUi(dialog)
dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
dialog.exec_()
This works for my application, and I believe it should work with yours as well. hope it'll help, it should be pretty straight forward to do the few changes needed to apply it to your case.
have a good day everybody.
Ui_Dialog should inherent from QtGui.QDialog, not object.
class Ui_Dialog(QtGui.QDialog):
def __init__(self, dbConnection):
QtGui.QDialog.__init__(self)
global c
c = dbConnection
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
Why QtGui.QWidget.__init___ ???
Use insted:
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
You must call __init__ methon from base class (name in parenthesis '()')
QDialog have two useful routins:
exec_()
show()
First wait for closing dialog and then you can access any field form dialog. Second show dialog but don't wait, so to work properly you must set some slot/signals connections to respond for dialog actions.
eg. for exec_():
class Dialog(QDialog):
def __init__(self, parent):
QDialog.__init__(parent)
line_edit = QLineEdit()
...
dialog = Dialog()
if dialog.exec_(): # here dialog will be shown and main script will wait for its closing (with no errors)
data = dialog.line_edit.text()
Small tip: can you change your ui classes into widgets (with layouts). And perhaps problem is that your __init__ should be __init__(self, parent=None, dbConnection)
Because when you create new widget in existing one PyQt may try to set it as children of existing one. (So change all init to have additional parent param (must be on second position)).

Stealing wheelEvents from a QScrollArea

I want to put my custom widget in a QScrollArea, but in my custom widget, I reimplemented wheelEvent(e) and it never gets called.
I'm fine with the scroll area not having its mouse wheel scrolling functionality. I just need those wheelEvents to call my handler. I tried handling the events out at the level of the main window but I only got them when the scroll widget was at one of its extremes and couldn't have moved any further anyways, I need all of them.
Heres a simplified version of my code:
class custom(QWidget):
def __init__(self, parent=None):
super(custom, self).__init__(parent)
self.parent = parent
def wheelEvent(self,event):
print "Custom Widget's wheelEvent Handler"
class mainw(QMainWindow):
def __init__(self, parent=None):
super(mainw, self).__init__(parent)
scroll = QScrollArea()
self.tw = thread_widget(scroll)
scroll.setWidget(self.tw)
self.setCentralWidget(scroll)
def wheelEvent(self,event):
print "Main Window's wheelEvent Handler"
Can someone explain to me how it is determined which event handler gets the events in this situation?
I figured out that its got something to do with the installEventFilter method of QObject, but I couldn't get the example to work so I said to hell with this and changed my plan completely.
problem solved
You can install a eventFilter in your custom class
class custom(QWidget):
def __init__(self, parent=None):
super(custom, self).__init__(parent)
self.parent = parent
self.installEventFilter(self)
def eventFilter(self, qobject, qevent):
qtype = qevent.type()
if qtype == QEvent.Wheel:
... wheel event logic
return True
# parents event handler for all other events
return super(custom,self).eventFilter(qobject, qevent)

Categories