I am working with python plugins for QGIS.I have my main form as DlgQueryBuilder.py and another form as DlgDberror.py,which displays the error in the query.My DlgDberror.py
contains following:
class DlgDbError(QtGui.QWidget, Ui_DlgDbError):
def __init__(self,e,parent):
QtGui.QWidget.__init__(self)
Ui_DlgDbError.__init__(self)
self.setupUi(self)
msg = "<pre>" + e.msg.replace('<','<') + "</pre>"
self.txtMessage.setHtml(msg)
#staticmethod
def showError(e, parent):
dlg = DlgDbError(e,parent)
dlg.show()
The call to this from DlgQueryBuilder.py is "DlgDbError.showError(e, self)"
Everything goes smooth but when i try to run my main form DlgQueryBuilder.py,*DlgDberror.py* form is not shown.It dissapears within a second.
dlg.show() should work rite??
When showError exits, dlg is garbage collected and goes away which also destroys the underlying Qt objects and the dialog. I suspect you need to pass your dialog back to QGIS in some way so it can handle whatever is necessary with the dialog. So yes, show() works, but your program is destroying the dialog before it can do anything useful.
Perhaps you wanted exec_() instead? It will pop up the dialog and then block waiting for the user to close the dialog. This is known as a modal dialog. See http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qdialog.html
Related
I have a Windows-10 PyGTK3 app (python 3.8 + GTK3.24) structured as follows: it has a non-modal main window, which opens a modal dialog (A), which in turn opens another modal dialog (B).
I use glade for the layout and properties of all three windows.
On the app startup I retain the instance of the GtkWindow A as follows:
def __init__(self):
self._main_window = builder.get_object("main_window")
...
def on_clicked_open_dialog_a(self, _widget):
dlg_a = DialogA(self._main_window)
dlg_a.run()
In the dialog A, I do a similar thing:
def __init__(self, parent):
self._dialog = builder.get_object("dialog_a")
self._dialog.set_transient_for(parent)
...
def on_clicked_open_dialog_b(self, _widget):
dlg_b = DialogB(self._dialog)
dlg_b.run()
And the same thing in the dialog B:
def __init__(self, parent):
self._dialog = builder.get_object("dialog_b")
self._dialog.set_transient_for(parent)
This is what it is supposed to look like
The problem that I am running into is that once I open the Dialog B from the Dialog A, the Dialog A ceases to behave like a proper modal dialog: if I click it, its window becomes active, even though the Dialog B is still displayed on top of it (and it can be moved and maximized/minimized, but not closed),
like so
and if I cover the app with another window and use Alt-Tab to bring the app back, the Dialog A comes on top, and I can only access the Dialog B by clicking on the Dialog A
as appears on this diagram
What am I doing wrong, and how do I fix it, please?
I have a QDialog with 3 buttons - Apply, OK and Cancel. In the __init__ method of the Dialogbox, I am connecting the OK and Cancel using the following:
buttonBox.accepted.connect( self.accept )
buttonBox.rejected.connect( self.reject )
In my main form, I am able to run a method (addNameToSandbox) for the OK signal using
self.__nameDialog.accepted.connect(self.__addNameToSandbox)
However, I want the Apply button to do the same but keep the child Dialog box open (as opposed to the OK button which closes it). How can I get that signal on the main window?
I have a method within the child dialog that I am able to run when Apply is clicked, but how to trigger an action in the main form with that, I have no idea.
buttonBox.button( QtGui.QDialogButtonBox.Apply ).clicked.connect( self.add )
I've tried using some of the other signals like finished, but I can't figure that one out either.
Create a signal in the dialog and connect it to the clicked of the apply button, and then use a signal to connect it in your main form:
class YourDialog(QtGui.QDialog):
applyClicked = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(YourDialog, self).__init__(parent):
# ...
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
apply_button = buttonBox.button(QtGui.QDialogButtonBox.Apply)
apply_button.clicked.connect(self.applyClicked)
# ...
self.__nameDialog.accepted.connect(self.__addNameToSandbox)
self.__nameDialog.applyClicked.connect(self.__applyfunc)
You need to declare QtCore.pyqtSignal applied as a class variable and then fire it up with self.applied.emit()
Then you'll be able to use it:
self.__nameDialog.applied.connect(self.__applyPressed)
Context:
I'm creating a GUI using QtDesigner and PyCharm. The issue I am having occurs when the main window (win1) calls a second windows (win2) through a QPushButton, and then opens an error message due to lack of user input. I am using an external file (main.py) to handle the logic and events of all the windows, rather than editing code generated by QtDesigner.
Issue:
The way I have things set up, the user needs to pick an option from a comboBox, based on the user's selection, the application launches either win2 or win3
User picks no option from comboBox --> Error message
User picks option 1 --> win2 opens, win1 closes
User picks option 2 --> win3 opens, win1 closes
The problem occurs after user exits win 2/3 and goes back to win1
User picks option 1/2 --> correct window opens, no issues
User picks no option --> error message --> user closes error
message --> crash (or sudden application exit)
Note: The crash produces no errors/warnings
Here's the code I'm using to handle the opening/closing:
class win1(QtGui.QMainWindow, pg1.Ui_MainWindow):
def __init__(self, parent=None):
super(win1, self).__init__(parent)
self.setupUi(self)
self.btn1.clicked.connect(self.checkuserselection)
self.addnew = win2(self)
def checkuserselection(self):
user_str = str(self.comboBox.currentText())
if user_str == "Select Option":
self.errMsg()
elif user_str == "Option 1":
self.runaddnew("Window title for option 1")
else:
self.runaddnew("window title for option 2")
def runaddnew(self, title):
self.addnew.setWindowTitle(title)
self.addnew.show()
self.hide()
#staticmethod
def errMsg():
w = QtGui.QMessageBox()
QtGui.QMessageBox.critical(w, "No Option Selected!")
The code for win2:
class win2(QtGui.QMainWindow, pg2.Ui_MainWindow):
def __init__(self, parent=None):
super(win2, self).__init__(parent)
self.setupUi(self)
self.actionExit.triggered.connect(self.closeWindow)
def closeWindow(self):
self.close()
w = win1(self)
w.show()
The user can get the error message many times without the application suddenly quitting, as long as they haven't opened one of the other windows. This leads me to think that the issue is with the closeWindow method in win2. My knowledge in python/pyQt is limited, so I don't know if that's indeed where the issue is.
Thanks to #ekhumoro, I'm posting the response that worked here in case anyone else has this issue.
In the code for win2:
def closeWindow(self):
self.parent().show()
self.close()
This works because win1 passed self to win2 when win2 was created
I'm trying to launch a dialog window inside of an active application window. The difficulty I am facing is being able to interact with the active application window once the dialog window is launched.
Here is an example of my python script:
class select_output_UI(QtGui.QDialog):
def __init__(self, *args, **kwargs):
super(select_output_UI, self).__init__(*args, **kwargs)
# BUILD UI FROM FILE
ui_file = QtCore.QFile("./select_output.ui")
ui_file.open(QtCore.QFile.ReadOnly)
self.myWidget = QtUiTools.QUiLoader().load(ui_file, self)
ui_file.close()
# SIGNALS
self.myWidget.cancel_button.clicked.connect(self.cancel_button_pressed)
def cancel_button_pressed(self):
self.button_pressed = "CANCEL"
self.close()
dialog = select_output_UI(QtGui.QApplication.activeWindow())
There are 2 options I am familiar with to launch this dialog window:
dialog.show()
This option allow's me to interact with the active application window, but this option will not wait for the dialog window to close before continuing to run whatever code is underneath.
dialog.exec_()
This option does not allow me to interact with the active application window. But what it does do is wait for the dialog window to close before continuing with the rest of the code.
Is there a way to interact with the application window while the dialog window has launch and have python wait till the dialog window is closed before continuing to read the rest of my code?
Sounds like you want to connect your dialog's "OK" (or "proceed", "continue", etc.) button to a method or function containing the rest of the code you want to run. Chances are you'll want it to be a method, since I imagine the rest of the code will need access to some of the widget values on the dialog.
For example:
class select_output_UI(QtGui.QDialog):
def __init__(self, *args, **kwargs):
super(select_output_UI, self).__init__(*args, **kwargs)
# Load .ui file, etc...
self.myWidget.ok_button.clicked.connect(self.do_work)
self.myWidget.cancel_button.clicked.connect(self.reject)
def do_work(self):
print "I'm doing work!"
# Do the work...
self.accept()
dialog = select_output_UI(QtGui.QApplication.activeWindow())
dialog.show()
Alternatively, you could hook your "OK" and "Cancel" buttons up to .accept() and .reject(), respectively, and then attach your do_work() function/method to the dialog's accepted signal. However, if you approach it that way, your code will execute after the dialog is closed, rather than allowing you to close it when you see fit (or, say, leave it open if something goes wrong in the rest of your code).
I'm (trying to) make a small program that resides in the system tray and checks a list of Twitch channels to see if they're online every once in a while.
I'm currently doing the GUI (in PyQt4), but it's exiting for no reason.
Here's my code so far:
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class TwitchWatchTray(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
super(TwitchWatchTray, self).__init__(icon, parent)
self.menu = QtGui.QMenu(parent)
settings_action = self.menu.addAction("Settings")
settings_action.triggered.connect(self.open_settings)
self.menu.addSeparator()
exit_action = self.menu.addAction("Exit")
exit_action.triggered.connect(QtCore.QCoreApplication.instance().quit)
self.setContextMenu(self.menu)
self.show()
def open_settings(self):
settings = SettingsDialog()
settings.show()
class SettingsDialog(QtGui.QWidget):
def __init__(self):
super(SettingsDialog, self).__init__()
self.resize(300, 300)
self.setWindowTitle('TwitchWatch Settings')
vbox = QtGui.QHBoxLayout()
self.channels_list = QtGui.QListView(self)
vbox.addWidget(self.channels_list)
self.add_box = QtGui.QLineEdit(self)
vbox.addWidget(self.add_box)
self.setLayout(vbox)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
tw = TwitchWatchTray(QtGui.QIcon("icon.png"), widget)
app.exec_()
print("Done!")
if __name__ == '__main__':
main()
When I right click the tray icon and click "Settings", it flashes a white box (my dialog), then immediately exits and prints "Done!".
Why is this, and how do I fix it?
There are two reasons why your code exits immediately after you open the settings dialog.
The first problem is with your open_settings method:
def open_settings(self):
settings = SettingsDialog()
settings.show()
This creates a dialog and makes it visible. show() returns immediately after showing the window; it doesn't wait for the window to be closed. The settings variable goes out of scope at the end of the method, and this causes the reference count of your SettingsDialog to drop to zero and hence become eligible for garbage collection. When Python deletes the SettingsDialog object, PyQt will delete the underlying C++ object, and this is what causes the dialog to close again.
I would recommend having your settings dialog subclass QDialog rather than QWidget (it is a dialog, after all). Instead of calling settings.show() you can then call settings.exec_(). settings.exec_() does wait for the dialog to be closed before it returns. It also returns QDialog.Accepted or QDialog.Rejected depending on whether the user clicked OK or Cancel. I'd also recommend getting rid of the call to self.show() in your SettingsDialog constructor.
The second problem is that your QApplication is set to quit when the last window is closed. This is the default behaviour, which is what a lot of applications need, but not yours. Even if your dialog stayed open and you could close it, you wouldn't want your application to exit immediately after you close the settings dialog. Call app.setQuitOnLastWindowClosed(False) to fix this.