How to get custom signals from a Qt dialog box - python

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)

Related

PyQt5: How do I "collect" or "receive" an emitted signal?

In my code, I have 2 classes in 2 separate files. I have a signal testSignal, a button pushButton and I connected the button like this:
testSignal = QtCore.pyqtSignal()
pushButton.pressed.connect(buttonPressed)
def buttonPressed(self):
testSignal.emit()
Now what I want to do is "receive" the emitted signal in the class/file, but I'm somewhat in the dark on how emit() actually works. Does anyone have any links for guides for the emit() function or can help somehow?
Thanks
[Py]Qt signals must be declared at the class level, as they become "active" only whenever a new class instance is created. Also, they can only be used for classes that inherit from QObject (including QWidget subclasses), and using them with standard python object classes will not work.
class SomeWindow(QtWidgets.QWidget):
testSignal = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
# ...
pushButton.pressed.connect(self.buttonPressed)
# connect the custom signal with an *instance method*
self.testSignal.connect(self.someInstanceFunction)
def buttonPressed(self):
# emit the signal
self.testSignal.emit()
def someInstanceFunction(self):
print('hello from the instance!')
def someAnonymousFunction():
print('hello from outside!')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = SomeWindow()
# connect the signal with an anonymous function
window.testSignal.connect(someAnonymousFunction)
sys.exit(app.exec_())
Obviously the example above doesn't make a lot of sense, as custom signals are normally used to communicate between instances of (possibly different) classes, but that's just for explanation purposes. Also, you could "concatenate" signals (as long as their signature is compatible):
class SomeWindow(QtWidgets.QWidget):
testSignal = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
# ...
# emit the testSignal when the button emits its pressed signal
pushButton.pressed.connect(self.testSignal)
self.testSignal.connect(self.someInstanceFunction)
# ...
Note that if you want to do something when the user clicks a button, you should use clicked(), not the pressed() one and the difference is very important: it's common convention that a button is considered clicked only when the user presses and releases the mouse button while still in the button area, so that the "click" can be avoided if the mouse button is released outside it. If you connect to pressed(), the signal will be emitted as soon as the mouse button is just pressed down on the button, which is not considered a standard behavior.

launching a pyside dialog inside of an active application window

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).

What event should I bind to before enabling a button in wxPython?

I have a simple app that requires 4 file inputs before proceeding to generate an output.
Consider btnFile1 btnFile2 btnFile3 and btnFile4 and btnOutput.
btnOutput is disabled on init.
btnFile1...4 all link to four similar, yet different, methods, call them OnBtn1..4 that assign a variable (call them file1..file4) to the file selected using the wx.FileDialog method.
I have a helper method, EnableButton() with a simple test:
if self.file1 and self.file2 and self.file3 and self.file4:
self.btnGenerate.Enable()
e.Skip()
What is the best way to call this method when all four variables are assigned? The four buttons can be clicked in any order, so we can't assign it only to the 4th button.
One obvious option is to add a call to EnableButton in every OnBtn method, but is this the ideal solution? What if there were 50 buttons?
Another option was to add a new event to each button like so:
for btn in (btn1, btn2, btn3, btn4):
btn.Bind(wx.EVT_BUTTON, self.EnableButton)
However, this doesn't work with the method above because when the 4th button is clicked, the variable is not yet assigned. It is only assigned after the file is selected from wx.FileDialog.
I am sure there's a third, more ideal, solution that I am not seeing.
The event EVT_UPDATE_UI is an event handler to look at the state of the application and change UI elements accordingly. You can use it to enable or disable your button depending on the state of the files, heres an example using check boxes to represent the file states, the button will only be enabled while all four check boxes are ticked.
import wx
from wx.lib import sized_controls
class TestFrame(sized_controls.SizedFrame):
def __init__(self, *args, **kwargs):
super(TestFrame, self).__init__(*args, **kwargs)
contents_pane = self.GetContentsPane()
self.files = {}
for number in range(1, 5):
label = 'file{}'.format(number)
ctrl = wx.CheckBox(contents_pane, label=label)
self.files[label] = ctrl
btn_output = wx.Button(contents_pane, label='btn_output')
btn_output.Bind(wx.EVT_UPDATE_UI, self.btn_update)
def btn_update(self, event):
enable = all(ctrl.IsChecked() for ctrl in self.files.values())
event.Enable(enable)
app = wx.App(False)
test_frame = TestFrame(None)
test_frame.Show()
app.MainLoop()
I would use a wx.Timer and set the timer to fire once a second. Every second it fires and calls your EnableButton event handler. The code would look something like this:
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.EnableButton, self.timer)
self.timer.Start(1000)
In the event handler, I would add the following inside the if statement:
self.timer.Stop()
You may also want to add some logic to stop the timer when the user closes the application as timer objects can cause the application to not close correctly if they are still active. You can read more about timers here:
http://www.blog.pythonlibrary.org/2009/08/25/wxpython-using-wx-timers/

python-form not displayed using show method

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

PyQt4: Adding QtMessageBox.information functionality to custom window

what I need is something very alike QtMessageBox.information method, but I need it form my custom window.
I need a one window with few labels, one QtTreeViewWidget, one QButtonGroup … This window will be called from main window. If we call class that implements called window as SelectionWindow, than what I need is:
class MainWindow(QtGui.QMainWindow):
...
def method2(self):
selWin = SelectionWindow()
tempSelectionValue = selWin.getSelection()
# Blocked until return from getSelection
self.method1(tempSelectionValue)
...
class SelectionWindow(QtGui.QMainWindow):
...
def getSelection(self):
...
return selectedRow
...
Method getSelection from SelectionWindow should pop up selection window and at the end return row selected in QTreeViewWidget. I want that main window remains blocked until user selects one row in selection window and confirms it by button. I hope that you will understand what I need.
I will appreciate any help!
Thanks,
Tiho
I would do something like this:
dialog window with buttonbox ->
events connected to accept() and
reject() slots of the dialog itself
set the dialog modality to something like application modal
call the exec_() method of the dialog to keep it blocking until the user chooses ok/cancel
after the execution of the exec_() method terminates, you can read what you need from the dialog widgets.
Something like this should fit your needs:
class SelectionWindow(QtGui.QMainWindow):
...
def getSelection(self):
result = self.exec_()
if result:
# User clicked Ok - read currentRow
selectedRow = self.ui.myQtTreeViewWidget.currentIndex()
else:
# User clicked Cancel
selectedRow = None
return selectedRow
...

Categories

Resources