【问题标题】:Removing dotted rectangle on selected item删除所选项目上的虚线矩形
【发布时间】:2019-12-21 15:19:00
【问题描述】:

我一直在尝试通过查看以下示例来更多地了解 QGraphicsView 小部件:

Toggle QPen for selected items after QGraphicsScene selection change

我发现这很有帮助,但是我注意到在选择项目时会弹出选取框。我一直在寻找摆脱这种情况的方法,似乎在 C++ 中有一个一致的答案,我在这里看到了一个例子:

https://www.qtcentre.org/threads/39659-About-QGraphicsItem-question

但我对如何将其翻译成 pyqt 感到非常困惑,所以我想知道是否有人可以从第一个链接的示例项目中提供有关如何在示例项目中执行此操作的任何见解......

我一直在通过查看如下代码来寻找可能的等价物:

https://www.riverbankcomputing.com/pipermail/pyqt/2012-November/032110.html

虽然说实话我什至不知道这些示例代码的应用是什么......

【问题讨论】:

  • 对不起,我看错了你的问题,我已经重新打开了你的问题。另一方面,你的意思是什么代码,因为另一个链接上有很多代码,所以这让我很困惑
  • 您好。对于第一个链接,我指的是答案中已编辑的代码。运行时似乎可以正常工作。因此,当我查找摆脱虚线矩形的方法时,我在 C++ 中找到了多个示例,说明如何通过编辑 QStyleOptionGraphicsItem 并弄乱一些名为 State_Selected 的属性来实现这一点。与 opt.state (第二个链接)有关。所以在第三个链接中,我在 pyqt 中找到了一个引用 opt.state 的示例。但我不知道在那种情况下它在做什么......

标签: python python-2.7 pyqt pyqt4 qgraphicsview


【解决方案1】:

如果你想从 python 中的项目选择中删除矩形,你必须使用以下命令:

class GraphicsEllipseItem(QGraphicsEllipseItem):
    def paint(self, painter, option, widget):
        option.state &= ~QStyle.State_Selected
        super(GraphicsEllipseItem, self).paint(painter, option, widget)

完整的脚本

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import random


class GraphicsEllipseItem(QGraphicsEllipseItem):
    def paint(self, painter, option, widget):
        option.state &= ~QStyle.State_Selected
        super(GraphicsEllipseItem, self).paint(painter, option, widget)


class MyGraphicsView(QGraphicsView):
    def __init__(self):
        super(MyGraphicsView, self).__init__()
        self.setDragMode(QGraphicsView.RubberBandDrag)
        self._isPanning = False
        self._mousePressed = False
        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setScene(MyGraphicsScene(self))
        self.scene().selectionChanged.connect(self.selection_changed)
        self._current_selection = []

    def select_items(self, items, on):
        pen = QPen(
            QColor(255, 255, 255) if on else QColor(255, 128, 0),
            0.5,
            Qt.SolidLine,
            Qt.RoundCap,
            Qt.RoundJoin,
        )
        for item in items:
            item.setPen(pen)

    def selection_changed(self):
        try:
            self.select_items(self._current_selection, False)
            self._current_selection = self.scene().selectedItems()
            self.select_items(self._current_selection, True)
        except RuntimeError:
            pass

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._mousePressed = True
            if self._isPanning:
                self.setCursor(Qt.ClosedHandCursor)
                self._dragPos = event.pos()
                event.accept()
            else:
                super(MyGraphicsView, self).mousePressEvent(event)
        elif event.button() == Qt.MiddleButton:
            self._mousePressed = True
            self._isPanning = True
            self.setCursor(Qt.ClosedHandCursor)
            self._dragPos = event.pos()
            event.accept()

    def mouseMoveEvent(self, event):
        if self._mousePressed and self._isPanning:
            newPos = event.pos()
            diff = newPos - self._dragPos
            self._dragPos = newPos
            self.horizontalScrollBar().setValue(
                self.horizontalScrollBar().value() - diff.x()
            )
            self.verticalScrollBar().setValue(
                self.verticalScrollBar().value() - diff.y()
            )
            event.accept()
        else:
            super(MyGraphicsView, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self._isPanning:
                self.setCursor(Qt.OpenHandCursor)
            else:
                self._isPanning = False
                self.setCursor(Qt.ArrowCursor)
            self._mousePressed = False
        elif event.button() == Qt.MiddleButton:
            self._isPanning = False
            self.setCursor(Qt.ArrowCursor)
            self._mousePressed = False
        super(MyGraphicsView, self).mouseReleaseEvent(event)

    def mouseDoubleClickEvent(self, event):
        self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
        pass

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Space and not self._mousePressed:
            self._isPanning = True
            self.setCursor(Qt.OpenHandCursor)
        else:
            super(MyGraphicsView, self).keyPressEvent(event)

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Space:
            if not self._mousePressed:
                self._isPanning = False
                self.setCursor(Qt.ArrowCursor)
        else:
            super(MyGraphicsView, self).keyPressEvent(event)

    def wheelEvent(self, event):
        # zoom factor
        factor = 1.25

        # Set Anchors
        self.setTransformationAnchor(QGraphicsView.NoAnchor)
        self.setResizeAnchor(QGraphicsView.NoAnchor)

        # Save the scene pos
        oldPos = self.mapToScene(event.pos())

        # Zoom
        if event.delta() < 0:
            factor = 1.0 / factor
        self.scale(factor, factor)

        # Get the new position
        newPos = self.mapToScene(event.pos())

        # Move scene to old position
        delta = newPos - oldPos
        self.translate(delta.x(), delta.y())


class MyGraphicsScene(QGraphicsScene):
    def __init__(self, parent):
        super(MyGraphicsScene, self).__init__(parent)
        self.setBackgroundBrush(QBrush(QColor(50, 50, 50)))
        # self.setSceneRect(50,50,0,0)


class MyMainWindow(QMainWindow):
    def __init__(self):
        super(MyMainWindow, self).__init__()
        self.setWindowTitle("Test")
        self.resize(800, 600)
        self.gv = MyGraphicsView()
        self.setCentralWidget(self.gv)
        self.populate()

    def populate(self):
        scene = self.gv.scene()
        for i in range(500):
            x = random.randint(0, 1000)
            y = random.randint(0, 1000)
            r = random.randint(2, 8)
            rect = GraphicsEllipseItem(x, y, r, r)
            rect.setPen(
                QPen(QColor(255, 128, 0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
            )
            rect.setBrush(QBrush(QColor(255, 128, 20, 128)))
            scene.addItem(rect)
            rect.setFlag(QGraphicsItem.ItemIsSelectable)
            rect.setFlag(QGraphicsItem.ItemIsMovable)

        rect = GraphicsEllipseItem(300, 500, 20, 20)
        rect.setPen(
            QPen(QColor(255, 128, 0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
        )
        rect.setBrush(QBrush(QColor(255, 0, 0, 128)))
        scene.addItem(rect)
        rect.setFlag(QGraphicsItem.ItemIsSelectable)
        rect.setFlag(QGraphicsItem.ItemIsMovable)


def main():
    app = QApplication(sys.argv)
    ex = MyMainWindow()
    ex.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

option.state &amp;= ~QStyle.State_Selected的解释:

state是QStyleOptionGraphicsItem的一个属性,它存储了绘画状态的信息,可能包含以下标志的组合:

在这种情况下,可以使用“|”来组合标志运算符,并使用“&~”停用。

为了更好的理解,我们用下面的例子:我们将State_Active和State_Editing中的状态设置为初始状态:

state = State_Active | State_Editing
      = 0x00010000 | 0x00400000
      = 0x00410000

并停用 State_Active 标志:

state & ~State_Active
0x00410000 & ~(0x00010000)
0x00410000 &   0xFFFEFFFF
0x00400000

如您所见,标志 State_Active 被移除而没有移除其他标志,仅通过二进制操作。

那么你想如何停用 State_Selected 标志并将其替换为必须完成的状态:

option.state = option.state & ~QStyle.State_Selected
option.state &= ~QStyle.State_Selected

【讨论】:

  • 感谢您的回答!只是想澄清这里发生了什么。如果我没记错的话,我相信 C++ 中的 tilda 符号被用来破坏内存中的某些东西(或类似的东西)。这就是这里发生的事情吗?这也使用了我在 C++ 示例中看到的相同的“&=”运算符,这背后的实际含义是什么......?
  • 啊,我明白了。所以基本上这种 & 运算符的使用是通过二进制方法删除这个“State_Selected”标志而不影响其他标志或“State_*”?
  • 您好,很抱歉,我花了一段时间来测试您的答案。看起来它就像一个魅力!非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-28
  • 2011-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多