在 PyQt 中从 QLabel 到 QLabel 画线
Drawing Line from QLabel to QLabel in PyQt
我对 PyQt 还很陌生
我正在尝试从 1 QLabel 到另一个 QLabel 画一条线。
我的 2 QLabel 位于另一个 QLabel 上,它在我的 GUI 中充当图像。
我已经设法跟踪鼠标事件并四处移动标签,但我无法使用 QPainter 在它们之间画线。
提前谢谢你:)
这是我的 MouseTracking class
class MouseTracker(QtCore.QObject):
positionChanged = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.setMouseTracking(True)
self.widget.installEventFilter(self)
@property
def widget(self):
return self._widget
def eventFilter(self, o, e):
if e.type() == QtCore.QEvent.MouseMove:
self.positionChanged.emit(e.pos())
return super().eventFilter(o, e)
这是我的 DraggableLabel class:
class DraggableLabel(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.LabelIsMoving = False
self.setStyleSheet("border-color: rgb(238, 0, 0); border-width : 2.0px; border-style:inset; background: transparent;")
self.origin = None
# self.setDragEnabled(True)
def mousePressEvent(self, event):
if not self.origin:
# update the origin point, we'll need that later
self.origin = self.pos()
if event.button() == Qt.LeftButton:
self.LabelIsMoving = True
self.mousePos = event.pos()
# print(event.pos())
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton:
# move the box
self.move(self.pos() + event.pos() - self.mousePos)
# print(event.pos())
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
print(event.pos())
def paintEvent(self, event):
painter = QPainter()
painter.setBrush(Qt.red)
# painter.setPen(qRgb(200,0,0))
painter.drawLine(10, 10, 200, 200)
这是我为 QTabwigdet 定制的 class(因为每当用户 add/insert 一个新标签时我需要控制和跟踪 2 个 QLabel 的位置)
class DynamicTab(QWidget):
def __init__(self):
super(DynamicTab, self).__init__()
# self.count = 0
self.setMouseTracking(True)
self.setAcceptDrops(True)
self.bool = True
self.layout = QVBoxLayout(self)
self.label = QLabel()
self.layout.addChildWidget(self.label)
self.icon1 = DraggableLabel(parent=self)
#pixmap for icon 1
pixmap = QPixmap('icon1.png')
# currentTab.setLayout(QVBoxLayout())
# currentTab.layout.setWidget(QRadioButton())
self.icon1.setPixmap(pixmap)
self.icon1.setScaledContents(True)
self.icon1.setFixedSize(20, 20)
self.icon2 = DraggableLabel(parent=self)
pixmap = QPixmap('icon1.png')
# currentTab.setLayout(QVBoxLayout())
# currentTab.layout.setWidget(QRadioButton())
self.icon2.setPixmap(pixmap)
self.icon2.setScaledContents(True)
self.icon2.setFixedSize(20, 20)
#self.label.move(event.x() - self.label_pos.x(), event.y() - self.label_pos.y())
MainWindow 和主要方法:
class UI_MainWindow(QMainWindow):
def __init__(self):
super(UI_MainWindow, self).__init__()
self.setWindowTitle("QHBoxLayout")
self.PictureTab = QTabWidget
def __setupUI__(self):
# super(UI_MainWindow, self).__init__()
self.setWindowTitle("QHBoxLayout")
loadUi("IIML_test2.ui", self)
self.tabChanged(self.PictureTab)
# self.tabChanged(self.tabWidget)
self.changeTabText(self.PictureTab, index=0, TabText="Patient1")
self.Button_ImportNew.clicked.connect(lambda: self.insertTab(self.PictureTab))
# self.PictureTab.currentChanged.connect(lambda: self.tabChanged(QtabWidget=self.PictureTab))
# self.tabWidget.currentChanged.connect(lambda: self.tabChanged(QtabWidget=self.tabWidget))
def tabChanged(self, QtabWidget):
QtabWidget.currentChanged.connect(lambda : print("Tab was changed to ", QtabWidget.currentIndex()))
def changeTabText(self, QTabWidget, index, TabText):
QTabWidget.setTabText(index, TabText)
def insertTab(self, QtabWidget):
# QFileDialog.getOpenFileNames(self, 'Open File', '.')
QtabWidget.addTab(DynamicTab(), "New Tab")
# get number of active tab
count = QtabWidget.count()
# change the view to the last added tab
currentTab = QtabWidget.widget(count-1)
QtabWidget.setCurrentWidget(currentTab)
pixmap = QPixmap('cat.jpg')
#currentTab.setLayout(QVBoxLayout())
#currentTab.layout.setWidget(QRadioButton())
# currentTab.setImage("cat.jpg")
currentTab.label.setPixmap(pixmap)
currentTab.label.setScaledContents(True)
currentTab.label.setFixedSize(self.label.width(), self.label.height())
tracker = MouseTracker(currentTab.label)
tracker.positionChanged.connect(self.on_positionChanged)
self.label_position = QtWidgets.QLabel(currentTab.label, alignment=QtCore.Qt.AlignCenter)
self.label_position.setStyleSheet('background-color: white; border: 1px solid black')
currentTab.label.show()
# print(currentTab.label)
@QtCore.pyqtSlot(QtCore.QPoint)
def on_positionChanged(self, pos):
delta = QtCore.QPoint(30, -15)
self.label_position.show()
self.label_position.move(pos + delta)
self.label_position.setText("(%d, %d)" % (pos.x(), pos.y()))
self.label_position.adjustSize()
# def SetupUI(self, MainWindow):
#
# self.setLayout(self.MainLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
UI_MainWindow = UI_MainWindow()
UI_MainWindow.__setupUI__()
widget = QtWidgets.QStackedWidget()
widget.addWidget(UI_MainWindow)
widget.setFixedHeight(900)
widget.setFixedWidth(1173)
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
我的概念:我有一个DynamicTab(QTabWidget)作为图片opener(每当用户按下 Import Now 时)。这个小部件的 child 是 3 个 Qlabels:self.label 是它自己的图片,另外两个 Qlabels 是 icon1 和 icon2,我试图 interact/drag with (Draggable Label)
我的问题: 我正在尝试跟踪我的鼠标移动并自定义画家以相应地绘画。我正在通过告诉画家 class 每当我抓住标签并用鼠标移动它时进行绘画(因此,可拖动)来尝试这一点。但是,每当我没有按住或单击鼠标左键时,我只能跟踪主 QLabel(主图片)内的鼠标位置。
任何帮助将不胜感激。
谢谢大家。
绘画只能在小部件矩形内进行,因此您不能在 DraggableLabel
的边界之外绘制。
解决方案是创建一个共享同一父级的进一步自定义小部件,然后绘制连接其他两个中心的线。
在下面的示例中,我在两个可拖动标签上安装了一个事件过滤器,它将根据它们更新自定义小部件的大小(这样它的几何图形将始终包含这两个几何图形)并调用 self.update()
安排重新粉刷。请注意,由于小部件是在其他两个部件之上创建的,因此它可能会捕获用于其他部件的鼠标事件;为防止这种情况,必须设置 Qt.WA_TransparentForMouseEvents
属性。
class Line(QWidget):
def __init__(self, obj1, obj2, parent):
super().__init__(parent)
self.obj1 = obj1
self.obj2 = obj2
self.obj1.installEventFilter(self)
self.obj2.installEventFilter(self)
self.setAttribute(Qt.WA_TransparentForMouseEvents)
def eventFilter(self, obj, event):
if event.type() in (event.Move, event.Resize):
rect = self.obj1.geometry() | self.obj2.geometry()
corner = rect.bottomRight()
self.resize(corner.x(), corner.y())
self.update()
return super().eventFilter(obj, event)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(painter.Antialiasing)
painter.setPen(QColor(200, 0, 0))
painter.drawLine(
self.obj1.geometry().center(),
self.obj2.geometry().center()
)
class DynamicTab(QWidget):
def __init__(self):
# ...
self.line = Line(self.icon1, self.icon2, self)
备注:
- 为了简单起见,我只用了
resize()
(不是setGeometry()
),这样widget会一直放在parent的左上角,我们可以直接拿到另一个没有任何转换的widget坐标;
- 自定义小部件位于其他两个之上,因为它是在它们之后添加的;如果你想把它放在下面,使用
self.line.lower()
;
- painter 必须始终使用 paint 设备参数进行初始化,可以使用
QPainter(obj)
或 painter.begin(obj)
,否则不会进行任何绘画(并且输出中会出现很多错误) ;
- 不使用
layout.addChildWidget()
(布局内部使用),而是布局适当的addWidget()
功能;
- 可以使用
border: 2px inset rgb(238, 0, 0);
; 缩短样式表边框语法
insertTab
的第一行可以更简单:currentTab = DynamicTab()
QtabWidget.addTab(currentTab, "New Tab")
;
currentTab.label.setFixedSize(self.label.size())
;
- QMainWindow 通常用作顶级小部件,通常不鼓励将其添加到 QStackedWidget;请注意,如果你这样做是因为 Youtube 教程,那么该教程以建议糟糕的做法(如最后的 try/except 块)而闻名,应该 不 遵循这些做法;
- 只有 类 和常量的名称应该大写,变量和函数则应始终以小写字母开头;
我对 PyQt 还很陌生
我正在尝试从 1 QLabel 到另一个 QLabel 画一条线。
我的 2 QLabel 位于另一个 QLabel 上,它在我的 GUI 中充当图像。
我已经设法跟踪鼠标事件并四处移动标签,但我无法使用 QPainter 在它们之间画线。
提前谢谢你:)
这是我的 MouseTracking class
class MouseTracker(QtCore.QObject):
positionChanged = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.setMouseTracking(True)
self.widget.installEventFilter(self)
@property
def widget(self):
return self._widget
def eventFilter(self, o, e):
if e.type() == QtCore.QEvent.MouseMove:
self.positionChanged.emit(e.pos())
return super().eventFilter(o, e)
这是我的 DraggableLabel class:
class DraggableLabel(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.LabelIsMoving = False
self.setStyleSheet("border-color: rgb(238, 0, 0); border-width : 2.0px; border-style:inset; background: transparent;")
self.origin = None
# self.setDragEnabled(True)
def mousePressEvent(self, event):
if not self.origin:
# update the origin point, we'll need that later
self.origin = self.pos()
if event.button() == Qt.LeftButton:
self.LabelIsMoving = True
self.mousePos = event.pos()
# print(event.pos())
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton:
# move the box
self.move(self.pos() + event.pos() - self.mousePos)
# print(event.pos())
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
print(event.pos())
def paintEvent(self, event):
painter = QPainter()
painter.setBrush(Qt.red)
# painter.setPen(qRgb(200,0,0))
painter.drawLine(10, 10, 200, 200)
这是我为 QTabwigdet 定制的 class(因为每当用户 add/insert 一个新标签时我需要控制和跟踪 2 个 QLabel 的位置)
class DynamicTab(QWidget):
def __init__(self):
super(DynamicTab, self).__init__()
# self.count = 0
self.setMouseTracking(True)
self.setAcceptDrops(True)
self.bool = True
self.layout = QVBoxLayout(self)
self.label = QLabel()
self.layout.addChildWidget(self.label)
self.icon1 = DraggableLabel(parent=self)
#pixmap for icon 1
pixmap = QPixmap('icon1.png')
# currentTab.setLayout(QVBoxLayout())
# currentTab.layout.setWidget(QRadioButton())
self.icon1.setPixmap(pixmap)
self.icon1.setScaledContents(True)
self.icon1.setFixedSize(20, 20)
self.icon2 = DraggableLabel(parent=self)
pixmap = QPixmap('icon1.png')
# currentTab.setLayout(QVBoxLayout())
# currentTab.layout.setWidget(QRadioButton())
self.icon2.setPixmap(pixmap)
self.icon2.setScaledContents(True)
self.icon2.setFixedSize(20, 20)
#self.label.move(event.x() - self.label_pos.x(), event.y() - self.label_pos.y())
MainWindow 和主要方法:
class UI_MainWindow(QMainWindow):
def __init__(self):
super(UI_MainWindow, self).__init__()
self.setWindowTitle("QHBoxLayout")
self.PictureTab = QTabWidget
def __setupUI__(self):
# super(UI_MainWindow, self).__init__()
self.setWindowTitle("QHBoxLayout")
loadUi("IIML_test2.ui", self)
self.tabChanged(self.PictureTab)
# self.tabChanged(self.tabWidget)
self.changeTabText(self.PictureTab, index=0, TabText="Patient1")
self.Button_ImportNew.clicked.connect(lambda: self.insertTab(self.PictureTab))
# self.PictureTab.currentChanged.connect(lambda: self.tabChanged(QtabWidget=self.PictureTab))
# self.tabWidget.currentChanged.connect(lambda: self.tabChanged(QtabWidget=self.tabWidget))
def tabChanged(self, QtabWidget):
QtabWidget.currentChanged.connect(lambda : print("Tab was changed to ", QtabWidget.currentIndex()))
def changeTabText(self, QTabWidget, index, TabText):
QTabWidget.setTabText(index, TabText)
def insertTab(self, QtabWidget):
# QFileDialog.getOpenFileNames(self, 'Open File', '.')
QtabWidget.addTab(DynamicTab(), "New Tab")
# get number of active tab
count = QtabWidget.count()
# change the view to the last added tab
currentTab = QtabWidget.widget(count-1)
QtabWidget.setCurrentWidget(currentTab)
pixmap = QPixmap('cat.jpg')
#currentTab.setLayout(QVBoxLayout())
#currentTab.layout.setWidget(QRadioButton())
# currentTab.setImage("cat.jpg")
currentTab.label.setPixmap(pixmap)
currentTab.label.setScaledContents(True)
currentTab.label.setFixedSize(self.label.width(), self.label.height())
tracker = MouseTracker(currentTab.label)
tracker.positionChanged.connect(self.on_positionChanged)
self.label_position = QtWidgets.QLabel(currentTab.label, alignment=QtCore.Qt.AlignCenter)
self.label_position.setStyleSheet('background-color: white; border: 1px solid black')
currentTab.label.show()
# print(currentTab.label)
@QtCore.pyqtSlot(QtCore.QPoint)
def on_positionChanged(self, pos):
delta = QtCore.QPoint(30, -15)
self.label_position.show()
self.label_position.move(pos + delta)
self.label_position.setText("(%d, %d)" % (pos.x(), pos.y()))
self.label_position.adjustSize()
# def SetupUI(self, MainWindow):
#
# self.setLayout(self.MainLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
UI_MainWindow = UI_MainWindow()
UI_MainWindow.__setupUI__()
widget = QtWidgets.QStackedWidget()
widget.addWidget(UI_MainWindow)
widget.setFixedHeight(900)
widget.setFixedWidth(1173)
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
我的概念:我有一个DynamicTab(QTabWidget)作为图片opener(每当用户按下 Import Now 时)。这个小部件的 child 是 3 个 Qlabels:self.label 是它自己的图片,另外两个 Qlabels 是 icon1 和 icon2,我试图 interact/drag with (Draggable Label)
我的问题: 我正在尝试跟踪我的鼠标移动并自定义画家以相应地绘画。我正在通过告诉画家 class 每当我抓住标签并用鼠标移动它时进行绘画(因此,可拖动)来尝试这一点。但是,每当我没有按住或单击鼠标左键时,我只能跟踪主 QLabel(主图片)内的鼠标位置。 任何帮助将不胜感激。 谢谢大家。
绘画只能在小部件矩形内进行,因此您不能在 DraggableLabel
的边界之外绘制。
解决方案是创建一个共享同一父级的进一步自定义小部件,然后绘制连接其他两个中心的线。
在下面的示例中,我在两个可拖动标签上安装了一个事件过滤器,它将根据它们更新自定义小部件的大小(这样它的几何图形将始终包含这两个几何图形)并调用 self.update()
安排重新粉刷。请注意,由于小部件是在其他两个部件之上创建的,因此它可能会捕获用于其他部件的鼠标事件;为防止这种情况,必须设置 Qt.WA_TransparentForMouseEvents
属性。
class Line(QWidget):
def __init__(self, obj1, obj2, parent):
super().__init__(parent)
self.obj1 = obj1
self.obj2 = obj2
self.obj1.installEventFilter(self)
self.obj2.installEventFilter(self)
self.setAttribute(Qt.WA_TransparentForMouseEvents)
def eventFilter(self, obj, event):
if event.type() in (event.Move, event.Resize):
rect = self.obj1.geometry() | self.obj2.geometry()
corner = rect.bottomRight()
self.resize(corner.x(), corner.y())
self.update()
return super().eventFilter(obj, event)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(painter.Antialiasing)
painter.setPen(QColor(200, 0, 0))
painter.drawLine(
self.obj1.geometry().center(),
self.obj2.geometry().center()
)
class DynamicTab(QWidget):
def __init__(self):
# ...
self.line = Line(self.icon1, self.icon2, self)
备注:
- 为了简单起见,我只用了
resize()
(不是setGeometry()
),这样widget会一直放在parent的左上角,我们可以直接拿到另一个没有任何转换的widget坐标; - 自定义小部件位于其他两个之上,因为它是在它们之后添加的;如果你想把它放在下面,使用
self.line.lower()
; - painter 必须始终使用 paint 设备参数进行初始化,可以使用
QPainter(obj)
或painter.begin(obj)
,否则不会进行任何绘画(并且输出中会出现很多错误) ; - 不使用
layout.addChildWidget()
(布局内部使用),而是布局适当的addWidget()
功能; - 可以使用
border: 2px inset rgb(238, 0, 0);
; 缩短样式表边框语法
insertTab
的第一行可以更简单:currentTab = DynamicTab()
QtabWidget.addTab(currentTab, "New Tab")
;currentTab.label.setFixedSize(self.label.size())
;- QMainWindow 通常用作顶级小部件,通常不鼓励将其添加到 QStackedWidget;请注意,如果你这样做是因为 Youtube 教程,那么该教程以建议糟糕的做法(如最后的 try/except 块)而闻名,应该 不 遵循这些做法;
- 只有 类 和常量的名称应该大写,变量和函数则应始终以小写字母开头;