PyQt: How to reset the cursor to whatever it's hovering over
Asked Answered
I

1

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

Impanation answered 6/4, 2015 at 22:23 Comment(13)
Can you provide a small example script that demonstrates the behaviour? I cannot reproduce it by simply disabling/enabling the widget.Oliana
Could you state what platform (windows, linux, OS X) you are on as well. Cursor behaviour is likely to be platform specific.Wooley
I can't reproduce on PyQt4 v4.11.1, Python 2.7.7 (Anaconda 2.0.1 32-bit), Window 8.1.Wooley
I tried many changes and workarounds but I can't fix this. Any modality or widget disabling causes the bug.Belize
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().Ewer
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().Ewer
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).Impanation
self._editor.setCursor() works as you describe: only at the very edges of the editor field.Impanation
Mhhh, I'll look at it a bit more to see if there is no other way to fix this.Ewer
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.Ewer
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.Ewer
Yup, that changes the cursor properly. The original bug still stands though :-)Impanation
Let us continue this discussion in chat.Ewer
L
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.

Leonoreleonsis answered 2/5, 2015 at 8:10 Comment(1)
Alright, that works. Have reported it as a bug to Qt!Impanation

© 2022 - 2024 — McMap. All rights reserved.