1

Or maybe QGraphicsView? I'm still not understanding mapping. See the comment

# I'm not computing x and y correctly. mapTo/From/Global/Scene/???

in myview.py below. The idea is that when you single click on the ring, it toggles the mobility, tying it to the mouse movement until you click again -- i.e. the first click sets it to "drag" and the second to "drop".

That works fine, but when I leave the 'frame" (view / scene) and then re-enter elsewhere, the ring jumps to the new location. I want to prevent leaving while the ring is "mobile" or in "drag" mode.

main code:

# Form implementation generated from reading ui file 'frames.ui'
# ...and then adjusted by hand... a lot

from PySide.QtCore import *
from PySide.QtGui  import *
from myview        import *


class Frames(QDialog):
    def __init__(self, parent=None):
        super(Frames, self).__init__(parent)

        self.verticalLayout = QVBoxLayout()

        self.setWindowFlags(Qt.FramelessWindowHint)

        self.topBox = QGroupBox()
        sizePolicy = QSizePolicy(QSizePolicy.Preferred,
                                 QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(self.topBox
                                     .sizePolicy().hasHeightForWidth())
        self.topBox.setSizePolicy(sizePolicy)
        self.verticalLayout.addWidget(self.topBox)

        self.middleBox = QGroupBox()
        sizePolicy = QSizePolicy(QSizePolicy.Preferred,
                                 QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(10)
        sizePolicy.setHeightForWidth(self.middleBox
                                     .sizePolicy().hasHeightForWidth())
        self.middleBox.setSizePolicy(sizePolicy)
        self.horizontalLayout = QHBoxLayout(self.middleBox)

        self.view = MyView(self)
        self.horizontalLayout.addWidget(self.view)

        self.rightBox = QGroupBox(self.middleBox)
        sizePolicy = QSizePolicy(QSizePolicy.Preferred,
                                 QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(1)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.rightBox
                                     .sizePolicy().hasHeightForWidth())
        self.rightBox.setSizePolicy(sizePolicy)
        self.horizontalLayout.addWidget(self.rightBox)
        self.verticalLayout.addWidget(self.middleBox)

        self.bottomBox = QGroupBox()
        sizePolicy = QSizePolicy(QSizePolicy.Preferred,
                                 QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(self.bottomBox
                                     .sizePolicy().hasHeightForWidth())
        self.bottomBox.setSizePolicy(sizePolicy)
        self.verticalLayout.addWidget(self.bottomBox)
        self.setLayout(self.verticalLayout)
        self.showFullScreen()

        QMetaObject.connectSlotsByName(self)


def main():
    import sys
    app = QApplication(sys.argv)
    frames = Frames()
    frames.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

And myview.py:

from PySide.QtCore import *
from PySide.QtGui  import *


class Ring(QGraphicsEllipseItem):

    def __init__(self, parent=None):
        super(Ring, self).__init__(0, 0, 80, 80, parent)
        self.mobile   = 0     # Initially immobile

        self.setFlags(QGraphicsItem.ItemIsMovable)
        self.setAcceptsHoverEvents(True)

    def mousePressEvent(self, event):
        super(Ring, self).mousePressEvent(event)
        self.mobile = (self.mobile + 1) % 2  # Toggle mobility


class MyView(QGraphicsView):

    def __init__(self, parent=None):
        super(MyView, self).__init__(parent)
        self.installEventFilter(self)

        sizePolicy = QSizePolicy(QSizePolicy.Expanding,
                                 QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(2)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)

        self.scene = QGraphicsScene(self)
        self.ring  = Ring()
        self.scene.addItem(self.ring)
        self.curse = self.cursor()

    def eventFilter(self, obj, event):
        super(MyView, self).eventFilter(obj, event)
        if event.type() == QEvent.WindowActivate:

            self.setWindowFlags(Qt.FramelessWindowHint)
            self.setFrameShape(QFrame.NoFrame)

            bounds = self.geometry()

            self.X1, self.Y1, self.w,  self.h  = bounds.getRect()
            self.X1, self.Y1, self.X2, self.Y2 = bounds.getCoords()

            self.scene.setSceneRect(self.X1, self.Y1,
                                    self.w, self.h)

            self.cx  = bounds.center().x()
            self.cy  = bounds.center().y()

            self.ring.setPos(self.cx - 40, self.cy - 40)

            self.setScene(self.scene)
            brush = QBrush(QColor(255, 255, 127))
            brush.setStyle(Qt.SolidPattern)
            self.setBackgroundBrush(brush)

            self.removeEventFilter(obj)
        return False

    def mouseMoveEvent(self, event):
        super(MyView, self).mouseMoveEvent(event)
        if self.ring.mobile:

# I'm not computing x and y correctly. mapTo/From/Global/Scene/???

            x = min(max(event.pos().x(), self.X1), self.X2)
            y = min(max(event.pos().y(), self.Y1), self.Y2)
#           self.curse.setPos(x, y)

            self.ring.setPos(event.pos())
Ubuntourist
  • 775
  • 11
  • 26

1 Answers1

0

The real mouse cursor is managed by the windowing environment, not Qt so I don't think there's a way to do this at that level. At least not in a cross-platform way. However you might be able to fake it.

  • Hide the real cursor, maybe set it to Qt::BlankCursor.
  • Track the real cursor's position relative to the region you want to restrict it to.
  • Draw your own virtual cursor as a graphical object in Qt.
  • Manage the position of the virtual cursor yourself.

However this would not prevent the real cursor from 'clicking' out of your app and interacting with other applications. You might have to detect that happening and the cursor clicking back into your app and have it resume control of your virtual cursor. This way when the real cursor 'comes back' you can resume movement of your virtual cursor from it's previous last position instead of the real cursor's current position.

Simon Hibbs
  • 5,941
  • 5
  • 26
  • 32
  • Bleah! Well if that's true, at least I feel less stupid about not figuring out a way to do it. "Always look on the bright side of ..." – Ubuntourist Feb 24 '17 at 19:48
  • I'm not worried about users interacting with other apps. My app goes full-screen and users will be instructed not to play with Alt-, Command-, , or anything else. If they ignore such instructions, it's not my problem. – Ubuntourist Feb 24 '17 at 19:52