[PyQt] Strangeness with signals and slots in PyQt4 QLineEdit

David Boddie david at boddie.org.uk
Thu Aug 16 15:20:30 BST 2007


On Thu Aug 16 14:57:37 BST 2007, Johannes Lochmann wrote:

> I have written lots of code in Qt3/C++ and am evaluating PyQt4 for a new 
> project right now. However, I encountered a strange behaviour, that I can
> not explain (code at the bottom of this post): It seems to me, that the
> signals and slots are acting rather unpedictable:
> 
> I have a simple widget with three QLineEdits and connect the signal 
> textEdited(QString) to my slots. In the slots I set the new text of the 
> lineedit to a data class (and print them).
> 
> If I enter 'a', 'b' and 'c' in the fields labelled 'a', 'b', 'c', I would
> have expected to see something like this:
> 
> a:None:None
> a:b:None
> a:b:c
> 
> But I get:
> 
> a:None:None
> b:b:None
> c:c:c

OK, so your Data class looks like this:

  class Data:
      def __init__(self, a=None, b=None, c=None):
          self.a = a
          self.b = b
          self.c = c
      def __str__(self):
          return "%s:%s:%s" % (self.a, self.b, self.c)

In the slots in your Widget class, you're just assigning the "txt" values
to the attributes in the instance of the Data class:

      def onAChange(self, txt):
          self.data.a = txt
          print "onAChange: %s" % self.data
      def onBChange(self, txt):
          self.data.b = txt
          print "onBChange: %s" % self.data
      def onCChange(self, txt):
          self.data.c = txt
          print "onCChange: %s" % self.data

In C++, this would work perfectly because QString objects are copied when
they are modified, so your copies in the "data" object will be safe.
In Python, each QString is simply bound to the "a", "b" and "c" attributes.
When each slot returns, the QString is still referenced, but could be
changed elsewhere, and the "copy on write" semantics of Qt don't apply.

Basically, you have to explicitly copy the "txt" values passed to the slots:

      def onAChange(self, txt):
          self.data.a = QString(txt)        # or use unicode(txt)
          print "onAChange: %s" % self.data
      def onBChange(self, txt):
          self.data.b = QString(txt)        # or use unicode(txt)
          print "onBChange: %s" % self.data
      def onCChange(self, txt):
          self.data.c = QString(txt)        # or use unicode(txt)
          print "onCChange: %s" % self.data

Aside: This can be a problem when reimplementing event handlers, such as
QWidget.mouseMoveEvent(), if you want to record the current cursor position.
Just assigning the return value of QMouseEvent.pos() to an attribute in an
object won't work as you might expect.

This explicit copying of values might seem like a lot of work, but the
opposite approach - automatic copying of values everywhere - might severely
impact the performance of applications that handle large amounts of data.

David


More information about the PyQt mailing list