【问题标题】:Mouse position on mouse move event is set on parent instead of child鼠标移动事件上的鼠标位置设置在父级而不是子级
【发布时间】:2020-03-21 15:36:00
【问题描述】:

我正在学习 python 和 PySide2,并从 learnpytq 跟进一些教程,特别是 https://www.learnpyqt.com/courses/custom-widgets/bitmap-graphics/,我被困在一个点上。

接下来,在创建像素图画布之后,我们在小部件上移动 mouseMoveEvent 以确保鼠标的坐标始终相对于画布。我已经复制了提供的源,但仍然在我正在运行的应用程序中,鼠标位置是相对于窗口(或父小部件,我不确定),导致绘制的线偏移到鼠标位置。

代码如下:

import sys
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Qt

class Canvas(QtWidgets.QLabel):

    def __init__(self):
        super().__init__()
        pixmap = QtGui.QPixmap(600, 300)
        self.setPixmap(pixmap)

        self.last_x, self.last_y = None, None
        self.pen_color = QtGui.QColor('#000000')

    def set_pen_color(self, c):
        self.pen_color = QtGui.QColor(c)

    def mouseMoveEvent(self, e):
        if self.last_x is None: # First event.
            self.last_x = e.x()
            self.last_y = e.y()
            return # Ignore the first time.

        painter = QtGui.QPainter(self.pixmap())
        p = painter.pen()
        p.setWidth(4)
        p.setColor(self.pen_color)
        painter.setPen(p)
        painter.drawLine(self.last_x, self.last_y, e.x(), e.y())
        painter.end()
        self.update()

        # Update the origin for next time.
        self.last_x = e.x()
        self.last_y = e.y()

    def mouseReleaseEvent(self, e):
        self.last_x = None
        self.last_y = None
COLORS = [
# 17 undertones https://lospec.com/palette-list/17undertones
'#000000', '#141923', '#414168', '#3a7fa7', '#35e3e3', '#8fd970', '#5ebb49',
'#458352', '#dcd37b', '#fffee5', '#ffd035', '#cc9245', '#a15c3e', '#a42f3b',
'#f45b7a', '#c24998', '#81588d', '#bcb0c2', '#ffffff',
]


class QPaletteButton(QtWidgets.QPushButton):

    def __init__(self, color):
        super().__init__()
        self.setFixedSize(QtCore.QSize(24,24))
        self.color = color
        self.setStyleSheet("background-color: %s;" % color)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.canvas = Canvas()

        w = QtWidgets.QWidget()
        l = QtWidgets.QVBoxLayout()
        w.setLayout(l)
        l.addWidget(self.canvas)

        palette = QtWidgets.QHBoxLayout()
        self.add_palette_buttons(palette)
        l.addLayout(palette)

        self.setCentralWidget(w)

    def add_palette_buttons(self, layout):
        for c in COLORS:
            b = QPaletteButton(c)
            b.pressed.connect(lambda c=c: self.canvas.set_pen_color(c))
            layout.addWidget(b)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

谁能发现我做错了什么?

【问题讨论】:

    标签: python pyside2


    【解决方案1】:

    问题在于您是根据 widget 坐标绘制的,而不是实际“画布”(“嵌入”像素图)的坐标,如果QLabel 可用的空间大于 QPixmap 大小。

    例如,如果图像垂直居中,则调整窗口大小并且标签高度变为 400(大于像素图高度),无论何时单击位置 100, 100,该位置实际上都会垂直平移乘以 50 像素(标签高度减去图像高度,除以 2)。

    要根据像素图实际获得位置,您必须自己计算它,然后相应地平移鼠标点:

        def mouseMoveEvent(self, e):
            if self.last_x is None: # First event.
                self.last_x = e.x()
                self.last_y = e.y()
                return # Ignore the first time.
            rect = self.contentsRect()
            pmRect = self.pixmap().rect()
            if rect != pmRect:
                # the pixmap rect is different from that available to the label
                align = self.alignment()
                if align & QtCore.Qt.AlignHCenter:
                    # horizontally align the rectangle
                    pmRect.moveLeft((rect.width() - pmRect.width()) / 2)
                elif align & QtCore.Qt.AlignRight:
                    # align to bottom
                    pmRect.moveRight(rect.right())
                if align & QtCore.Qt.AlignVCenter:
                    # vertically align the rectangle
                    pmRect.moveTop((rect.height() - pmRect.height()) / 2)
                elif align &  QtCore.Qt.AlignBottom:
                    # align right
                    pmRect.moveBottom(rect.bottom())
    
            painter = QtGui.QPainter(self.pixmap())
            p = painter.pen()
            p.setWidth(4)
            p.setColor(self.pen_color)
            painter.setPen(p)
            # translate the painter by the pmRect offset; note the negative sign
            painter.translate(-pmRect.topLeft())
            painter.drawLine(self.last_x, self.last_y, e.x(), e.y())
            painter.end()
            self.update()
    
            # Update the origin for next time.
            self.last_x = e.x()
            self.last_y = e.y()
    

    【讨论】:

    • 非常感谢!我还没有测试,但我或多或少地理解你写的内容。另一种解决方案是将 QLabel 的尺寸限制为像素图,对吗?
    • 是的,当然。另外,请注意,如果您设置setScaledContents(True)(调整像素图的大小以填充整个区域),似乎存在阻止重新绘制像素图的错误。为避免这种情况,只需立即将该属性重置为 false 和 true,而不是调用 update(无论如何都会在内部调用)。
    猜你喜欢
    • 2021-09-23
    • 2023-03-12
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 1970-01-01
    • 2023-01-19
    • 2011-02-05
    相关资源
    最近更新 更多