17

Very small issue:

I've written a small IDE with a text editing widget based on a QPlainTextEdit. When you move the mouse over it the cursor becomes a caret/text cursor as expected. If you hit F5 the window is disabled and a small script runs after which the window is re-enabled and the text area is given focus.

Somehow, this changes the cursor from a text cursor to a pointer. If you move the cursor off the text area and then back onto it, it turns into a text cursor again.

Is there some way to trigger this refresh action programmatically?


Update: It seems to be something to do with having a progress bar:

#!/usr/bin/env python
import sys
import time
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt

class TinyIDE(QtGui.QMainWindow):

    def __init__(self, filename=None):
        super(TinyIDE, self).__init__()
        self.setWindowTitle('Tiny IDE test')

        # Add menu item
        menu = self.menuBar()
        menu_run = menu.addMenu('&Run')
        tool_run = QtGui.QAction('&Run', self)
        tool_run.setShortcut('F5')
        tool_run.triggered.connect(self.action_run)
        menu_run.addAction(tool_run)

        # Add editor
        self._editor = QtGui.QPlainTextEdit()
        self._editor.setPlainText('Press F5 to run')
        self.setCentralWidget(self._editor)
        self._editor.setFocus()

    def action_run(self):
        pbar = None
        try:
            self.setEnabled(False)

            pbar = QtGui.QProgressDialog('Running script', 'Cancel', 0, 10)
            pbar.setWindowModality(Qt.WindowModal)
            pbar.setAutoClose(False)
            pbar.setAutoReset(False)
            pbar.show()

            for i in xrange(10):
                time.sleep(0.2)
                pbar.setValue(1 + i)
                QtGui.QApplication.processEvents()

        finally:

            QtGui.QApplication.processEvents()
            pbar.close()
            pbar.deleteLater()
            self.setEnabled(True)
            self._editor.setFocus()

if __name__ == '__main__':
    a = QtGui.QApplication([])
    a.connect(a, QtCore.SIGNAL('lastWindowClosed()'), a, QtCore.SLOT('quit()'))
    w = TinyIDE()
    w.show()
    sys.exit(a.exec_())

I've tested it on Linux (Fedora 21) with Python 2.7.8 and PyQt4 version 4.8.6

Steps to reproduce:

  1. Run the script
  2. Place the mouse cursor over the text area, it should turn into a text cursor
  3. Press F5, wait for the progress bar to go away, leave the mouse hovering over the text area, it should turn into a pointer

Expected result: Once the progress bar goes away the cursor, still hovering over the text area, should revert to a text cursor

Actual result: The cursor stays a pointer, until it's moved off and back on to the text area

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Michael Clerx
  • 2,928
  • 2
  • 33
  • 47
  • 1
    Can you provide a small example script that demonstrates the behaviour? I cannot reproduce it by simply disabling/enabling the widget. – ekhumoro Apr 06 '15 at 23:32
  • 1
    Could you state what platform (windows, linux, OS X) you are on as well. Cursor behaviour is likely to be platform specific. – three_pineapples Apr 07 '15 at 02:21
  • 1
    I can't reproduce on PyQt4 v4.11.1, Python 2.7.7 (Anaconda 2.0.1 32-bit), Window 8.1. – three_pineapples Apr 07 '15 at 07:25
  • 1
    I tried many changes and workarounds but I can't fix this. Any modality or widget disabling causes the bug. – Pavel Strakhov Apr 27 '15 at 16:49
  • 1
    Can't reproduce the bug on Qt 4.8.6, PyQt 4.11.3, Python 2.7, Windows 7. Seems to be Linux specific. Here is some things you can try to bypass this bug. First, try changing the entire app cursor with `QtGui.QApplication.setOverrideCursor(QtGui.QCursor(Qt.WaitCursor))` when you call `action_run()`. When it's over, restore to your original mouse cursor with `QtGui.QApplication.restoreOverrideCursor()`. – DrHaze Apr 28 '15 at 08:35
  • 1
    Secondly, when creating your UI, does `self._editor.setCursor(Qt.UpArrowCursor)` changes anything? On my computer, this is not working as I expect, the cursor is only changed for the "borders" of the widget. I suspect `QPlainTextEdit` to overwrite the mouse cursor on the editor. However, when I create the same UI in Qt Designer and change the cursor in it, the mouse cursor is properly overwrited, weird. You can restore the original cursor with `self._editor.unsetCursor()`. – DrHaze Apr 28 '15 at 08:38
  • Thanks for the feedback. Using an override cursor works fine, but it then "restores" the cursor back to the wrong cursor shape (an arrow not a caret). – Michael Clerx Apr 28 '15 at 08:55
  • self._editor.setCursor() works as you describe: only at the very edges of the editor field. – Michael Clerx Apr 28 '15 at 08:57
  • Mhhh, I'll look at it a bit more to see if there is no other way to fix this. – DrHaze Apr 28 '15 at 09:01
  • Found this in PySide doc (PySide doc is far superior than PyQt's one): `The shape of the mouse cursor on a PySide.QtGui.QPlainTextEdit is Qt.IBeamCursor by default. It can be changed through the PySide.QtGui.QAbstractScrollArea.viewport() ‘s cursor property.` Maybe an interesting track to follow. – DrHaze Apr 28 '15 at 09:05
  • Ok, what about: `self._editor.viewport().setCursor(Qt.UpArrowCursor)` ? First try using it in your UI creation without the global QApplication mouse cursor overwrite. If it's not working as expected, try mixing everything. – DrHaze Apr 28 '15 at 09:07
  • Yup, that changes the cursor properly. The original bug still stands though :-) – Michael Clerx Apr 28 '15 at 09:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76423/discussion-between-drhaze-and-michael-clerx). – DrHaze Apr 28 '15 at 09:11

1 Answers1

1

I was only able to work around this (what is obviously a bug):

        pos = QtGui.QCursor.pos()
        QtGui.QCursor.setPos(0, 0)
        QtGui.QCursor.setPos(pos)

The funny thing is, setPos(0, 0) on my system (some Ubuntu) doesn't even move the mouse, so if I just call it, the mouse stays where it is and the cursor changes back immediately after even a slightest move (no need to move it away from the editor any more). But the additional setPos() that reverts the position back does the trick, and the cursor updates instantly. This has the added bonus that if you move it away while the computation is in progress, the workaround above still resets the cursor to whatever shape is correct for the place the mouse cursor is actually at.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73