1

I'm using PyQt to build a simple IDE and getting weird errors if you load an empty file. A small example script is posted below:

#!/usr/bin/env python
import sys
from PyQt4 import QtGui
class TestApp(QtGui.QMainWindow):
    def __init__(self, filename=None):
        super(TestApp, self).__init__()
        self._editor = QtGui.QPlainTextEdit()
        self._editor.modificationChanged.connect(self._change_modified)
        self.setCentralWidget(self._editor)
        self._editor.setPlainText('a')
    def _change_modified(self, have_change):
        print(have_change)
if __name__ == '__main__':
    a = QtGui.QApplication([])
    app = TestApp()
    app.show()
    sys.exit(a.exec_())

As expected, this shows a window with a plain text editor. As soon as the setPlainText method is called, the editor emits two events: One modificationChanged event with changes=True, a second with changes=False. A bit weird, but fine. However, if you change setPlainText('a') to setPlainText(''), only a single event is emitted, this time with changes=True. Even worse, after telling the editor it's not modified with setModified(False), it insists it's been changed somehow.

Does anyone know what's causing this and how I can work around this issue?


Update: It seems to be a bug & also affects QPlainTextEdit.clear().

The workaround below places a wrapper around the QPlainTextEdit to fix clear() and setPlainText('').

#!/usr/bin/env python
import sys
from PyQt4 import QtGui
class TestApp(QtGui.QMainWindow):
    def __init__(self, filename=None):
        super(TestApp, self).__init__()
        self._editor = PlainTextEdit()
        self._editor.modificationChanged.connect(self._change_modified)
        self.setCentralWidget(self._editor)
        self._editor.setPlainText('')
    def _change_modified(self, have_change):
        print(have_change)
class PlainTextEdit(QtGui.QPlainTextEdit):
    def clear(self):
        self.selectAll()
        cursor = self.textCursor()
        cursor.removeSelectedText()
        doc = self.document()
        doc.clearUndoRedoStacks()
        doc.setModified(False)
        self.modificationChanged.emit(False)
    def setPlainText(self, text):
        if text:
            super(PlainTextEdit, self).setPlainText(text)
        else:
            self.clear()
if __name__ == '__main__':
    a = QtGui.QApplication([])
    app = TestApp()
    app.show()
    sys.exit(a.exec_())
Michael Clerx
  • 2,928
  • 2
  • 33
  • 47

1 Answers1

1

It's a Qt bug, and the straightforward workaround is to check for empty contents if modifications are indicated.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I found a report for it here: https://bugreports.qt.io/browse/QTBUG-42318 Not so sure about your workaround though: what happens if a user clears the field (with delete or backspace)? – Michael Clerx Jul 24 '15 at 13:38
  • 1
    A slightly bizarre workaround is to replace setPlainText('') with selectAll() and then use a cursor to delete it and document().setModified(False)... – Michael Clerx Jul 24 '15 at 13:49