I'm using pyside and sqlalchemy for a database of contact information.
The setup of sqlalchemy is pretty standard, except for the fact that when I created the session using sessionmaker(), I set expire_on_commit = False.
In order to be able to create a new session within a sitting I'm using a sort of refresh button. The problem is every time I use the refresh button, when I try to add a new line/entry, I get 2 or 4 additional entries (with one click), whereas if one gets deleted, all the "copies" are deleted too (they aren't actual copies though because each of them has their own unique id).
def refresh(self):
self.session.close_all()
self.session = CreateSession(self.username, self.password) #custom method defined elsewhere for creating a session
self.TableViews() #initializing the table views for pyside
self.WidgetsToAttributes() #connect widgets of the gui to the sqlalchemy scheme attributes
self.connectSignals() #connects the qt signals to the appropriate functions in code
pass
(there are also some additional initializations but they are irrelevant here because they only initialize some widgets)
All the above functions are used in other places, for example for the first initialization of the GUI, so what doesn't work here is the combination of the methods. From what I gather, it's the connectSignals() method that causes all the trouble. Signals are apparently doubled (sometimes quadrupled) which results in several entries (execution of code) from one click.
What causes that and how can I avoid it?
If you call several time a connect, your signal will be connected several times to the same slot. One signal will then trigger multiple calls to the slot. I think that's why you get multiple entries in your database.
As a simple example, the code bellow would print two "1" every time the button is clicked:
self.button=QtGui.QPushButton("my button")
self.button.clicked.connect(self.on_click)
self.button.clicked.connect(self.on_click)
def on_click(self):
print("1")
Signals are automatically disconnected when an object is destroyed. In your case, you destroy self.session to create a new one. So every signals directly linked to self.session should be destroyed.
But if you have something like this:
class myWidget(QtGui.QWidget):
def __init__( self, parent=None):
super(myWidget, self ).__init__( parent )
self.button=QtGui.QPushButton("add entry")
self.button.clicked.connect(self.on_click)
def on_click(self):
self.session.add_an_entry()
Then destroying self.session is not going to disconnect the signal between the button and the on_click function.
So, you can either not call this kind of connect multiple time, or you can manually delete the connection with disconnect.
Related
I have created a little pyqt5 project. Here is a printscreen of the application while running:
When the user clicks on the QPushButton from the main window, the dialog window appears and the user writes something in the QlineEdit. Then while clicking on the QPushButton of the dialog window, the dialog window sends a signal to the main window and is deleted. The signal contains the text typed by the user.
Here are the descriptions of my two classes which are very simple:
The MainWindow class.
The DialogWindow class (I want to make my own Dialog Class without using the pre existing Dialog windows).
My main script
I have several questions:
Is it the right way of using signals in order to communicate between windows? I do not think that I violate the class encapsulation. However I do not like to connect the signal on the child class by writing:
self.mySignal.connect(parent.updatelabelAnswer)
In this line I use the attribute parent - is it okay? It seems to me that it is not a good way to use signals.
My second question is:
Am I right to call self.deleteLater() in the on_pushButton_clicked slot of DialogWindow? It seems not, as I have checked with the python interactive shell and the object myDialogWindow is still accessible.
Generally, the parent should always be the one performing the signal connecting. Having the child widget make connections on the parent is problematic because it places limitations on the parent and causes side effects, and completely breaks in cases where parent ownership is transfrerred for the child widget.
In your example, there are two options I would consider "correct". If the dialog was meant to be at least somewhat persistent, and not meant to be run modally, then it should define a signal that the parent class connects to. The dialog should not delete itself, that should be the responsibility of the parent class after the signal is received.
MainWindow
def on_pushbutton_clicked(self):
if not self.dlg:
self.dlg = DialogWindow(self)
self.dlg.mySignal.connect(self.on_mySignal)
self.dlg.show()
def on_mySignal(value):
self.dlg.mySignal.disconnect()
self.dlg.close()
self.dlg.deleteLater()
self.dlg = None
self.updateLabelAnswer(value)
Your dialog seems to be a temporary dialog that exists just to gather input and should probably be run modally. In that case, you don't even have to define any signals. Just create the class and provide an API to get the value of the text box.
DialogWindow
class DialogWindow(...)
...
def on_pushbutton_clicked(self):
self.accept()
def getValue(self):
return self.lineEdit.text()
In MainWindow
def on_pushbutton_clicked(self):
dlg = DialogWindow(self)
if dlg.exec_():
value = dlg.getValue()
Okay so I guess I should post an answer instead of writing bloated comments :P
About the deletion I will quote the Qt documentation:
As with QWidget::close(), done() deletes the dialog if the
Qt::WA_DeleteOnClose flag is set. If the dialog is the application's
main widget, the application terminates. If the dialog is the last
window closed, the QApplication::lastWindowClosed() signal is emitted.
However if you want to handle the closing (and deletion) of the dialog window from your other widget that opens it, slots and signals should be used. Simply connect a button or whatever from your main widget and its clicked() signal to the done() slot of your dialog and you are good to go.
At this point I would also like to point out that deleting a dialog may not be necessary. Based on the memory footprint of the dialog (how much memory is used to create and run it) you may wish to consider creating the dialog at the beginning and leaving it in your memory until the main application is closed. In addition to that you can use hide() and show() to display it on the screen. This is actually a generally good practice for things that are small enough since the deletion and then creation of a window takes more time compared to simply hiding and showing it.
Now about the signals and slots these have pretty straight-forward semantics. As I've posted in the comments and my other answer in order to connect a slot to a signal you need to have them present in the same scope. If that's not the case pass one (or both) to a place where the situation is fixed. In your case you have to have a common place for both. If both are top-level widgets you have to do the connections inside your main(). I would rather add the dialog as an extension to your MainWindow class (as a class member) and to the instantiation plus the connections there - for example in the constructor of your MainWindow:
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.dialog = DialogWindow(self)
# Connect mainwindow's signals to dialog's slots
# Connect dialog's signals to mainwindow's slots
# And even connect dialog's signals to dialog's slots
I have developed a user interface in tkinter/ttk that uses the standard ttk.Button widget to call various functions using the 'command' argument. The problem is that if a user double-clicks a button the function is called twice. This causes all sorts of problems. The double-click event is not bound to anything so is there a way to disable the second single-click? I've tried time.sleep() and after() at the start of the function definition but I haven't found a way to make it work. Do I need to manually bind each function to a click for each button and reassign the event handler? Any straightforward way to do ignore double-clicks across the board???
Just tell the button's callback function (its command) to disable the button, then put it back to normal after a brief period (200ms here).
def callback(self):
self.my_button.config(state=tk.DISABLED)
self.root.after(200, lambda: self.my_button.config(state=tk.NORMAL))
# other actions
As Bryan points out, it's better and simpler to just wait until the function is finished (unless you just wanted to guard against accidental double-clicks and are okay with the function being called again before it's necessarily finished):
def callback(self):
self.my_button.config(state=tk.DISABLED)
# other actions
self.my_button.config(state=tk.NORMAL)
This sample code assumes an import of import tkinter as tk, an OO app structure, a button called self.my_button, a callback function called self.callback, and a root object called self.root.
I am creating a PyQt4 gui that allows the user in a qmainwindow to input some initial parameters and then click a start button to begin a program. When they click the start button, a new window that displays the parameters appears, and the program begins. I would like the user to be able to initiate multiple instances of the program. However, when the parameters are changed in the qmainwindow and the start button is clicked a second time, the first program window disappears.
Is there a way to have the start button call a second window that runs concurrently with the first window? I imagine this would be something like threading, but from what I have read, PyQt4 doesn't seem to have a method for threading within an application.
Any help would be much appreciated.
I am guessing that you are saving the reference to newly created window in the same variable. If you want to create multiple windows, try to save the reference to that window in a separate variable, i.e each window should have it's own reference variable.
def showWindow(self):
self.child = Window(self)
self.child.show()
If this is your situation, the first window will loose it's reference as soon as the second time showWindow() executes. Because the self.child will contain the reference to second window, resulting in closing the first window, because the first window has no reference reference. As soon as the widget looses reference in Qt, the widget will be destroyed. To overcome this problem maintain a list of variables:
# declare a list in __init__ as self.widgetList = []
def showWindow(self):
win = Window(self):
win.show()
self.widgetList.append(win)
My problem is the following. Coming from a web background, I did not problems to do this, but in a Python desktop application I can´t really see what is the best way to organize the code according to a MVC pattern.
I want to create a window that according to user input, when a button is pressed, it shows similar entries that are available in the database. The window is my view.
So basically these are the relations:
1) Communication controller --> view
The controller has an instance of the view, and can use its exposed methods, as for example view.show_data(). I think this is the way to go.
# Controller
my_view = View()
...
my_view.show_data(whatever_data)
2) Communication view --> controller
When the user inserts some text, a method in the controller has to be fired so that it can ask the model for the necessary data in the database. The problem is that I don't know what is the best way for the view to tell the controller that it has to fire that very method.
My first idea is to pass a reference of the controller to the view, and bind the events on the view, something like this:
# Controller
my_view = View(self)
my_model = Model()
...
def on_user_input(self):
# process the input
user_input = ...
self.my_model.method_to_get_info(user_input)
And the view:
# View
def __init__(self, controller):
self.controller_reference = controller
self.launch_gui()
self.config_binds()
def launch_gui(self):
# ... configure all the GUI itself
self.button = ...
def config_binds(self):
self.button.Bind(wx.EVT_BUTTON, self.controller_reference.on_user_input())
But I think this "closed circle" relation is not a very clean solution. The view is referenced in the controller, and the controller in the view. I think it creates a tight relationship between the view and the controller.
What´s the way to do this?
The view is supposed to fire events. Normally you are not sending events directly to the controller, but the the GUI's frameworks generic event handler. The events in question are typically such event like "a key has been pressed", and "somebody moved the mouse".
A more "higher-level" MVC as in your case, can very well let the parts know of each other. That you tell the view what object is it's controller is perfectly fine. The important part is that all parts are using a well-defined interface, so that the parts are exchangeable.
As you see, this is quite different from the common web concept of model-view-controller which really isn't model-view-controller at all, but a misnomer and should be called model-view-template.
I have a Qt program with many buttons, user-interactable widgets, etc.
At one stage in the program, I would like all the widgets to temporarily 'stop working'; stop behaving to mouse clicks and instead pass the event on to one function.
(This is so the User can select a widget to perform meta operations. Part explanation here: Get variable name of Qt Widget (for use in Stylesheet)? )
The User would pick a widget (to do stuff with) by clicking it, and of course clicking a button must not cause the button's bound function to run.
What is the correct (most abstracted, sensible) method of doing this?
(which doesn't involve too much new code. ie; not subclassing every widget)
Is there anything in Qt designed for this?
So far, I am able to retrieve a list of all the widgets in the program (by calling
QObject.findChildren(QtGui.QWidget)
so the solution can incorporate this.
My current horrible ideas are;
Some how dealing with all the applications events all the time in one
function and not letting through the events when I need the
application to be dormant.
When I need dormancy, make a new transparent widget which recieves
mouse clicks and stretch it over the entire window. Take coordinates
of click and figure out the widget underneath.
Somehow create a new 'shell' instance of the window.
THANKS!
(Sorry for the terrible write-up; in a slight rush)
python 2.7.2
PyQt4
Windows 7
You can intercept events send to specific widgets with QObject::installEventFilter.
graphite answered this one first so give credit where credit is due.
For an actual example in PySide, here's an example you might draw some useful code from:
my_app.py
from KeyPressEater import KeyPressEater
if __name__ == "__main__":
app = QApplication(sys.argv)
eater = KeyPressEater()
app.installEventFilter(eater)
KeyPressEater.py
class KeyPressEater(QObject):
# subclassing for eventFilter
def eventFilter(self, obj, event):
if self.ignore_input:
# swallow events
pass
else:
# bubble events
return QObject.eventFilter(self,obj,event)