I want to move the following red cross in the canvas with the mouse events. it should only move when we click on it and drag it with the move. it should stop moving when we release the mouse.
I do get the events of the mouse. but I don't know how i can detect that I clicked on the object to make it move.
also for example i can't set self.plot1.pos to change its position. we don't have access to that attribute.
does anybody have an idea?
I am using python 3.5 and OpenGL Canvas with vispy and a QtWidgets window.
import sys
from PySide2 import QtWidgets
from vispy import scene
from PySide2.QtCore import QMetaObject
from PySide2.QtWidgets import *
import numpy as np
class my_canvas(scene.SceneCanvas):
def __init__(self):
super().__init__(keys="interactive")
self.unfreeze()
self.view = self.central_widget.add_view()
self.view.bgcolor = '#ffffff' # set the canva to a white background
window_size_0 = 800, 400
window_center = window_size_0[0] / 2, window_size_0[1] / 2
crosshair_max_length = 50
data_1 = np.random.normal(size=(2, 2))
data_1[0] = window_center[0] - crosshair_max_length, window_center[1]
data_1[1] = window_center[0] + crosshair_max_length, window_center[1]
data_2 = np.random.normal(size=(2, 2))
data_2[0] = window_center[0], window_center[1] - crosshair_max_length
data_2[1] = window_center[0], window_center[1] + crosshair_max_length
self.plot1 = scene.Line(data_1, parent=self.view.scene, color="r")
self.plot2 = scene.Line(data_2, parent=self.view.scene, color="r")
self.selected_object = None
self.freeze()
def on_mouse_press(self, event):
if event.button == 1:
print("pressed left")
if event.button == 2:
print("pressed right")
def on_mouse_move(self, event):
pass
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName("MainWindow")
MainWindow.resize(748, 537)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.groupBox = QGroupBox(self.centralwidget)
self.groupBox.setObjectName("groupBox")
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
QMetaObject.connectSlotsByName(MainWindow)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# OpenGL drawing surface
self.canvas = my_canvas()
self.canvas.create_native()
self.canvas.native.setParent(self)
self.setWindowTitle('MyApp')
def main():
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('my_gui')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
i found the solution for the ones who are interested.
self.plot1.set_data(pos=...)
with this method we can move it easily
Related
I am new to QtWidgets and trying to build an app in QtWidgets and Python (3.x). the end goal of the app is to show images and a
superposed cursor (to be exact, a "plus" sign of 2cm) that can be moved along the image reacting to mouse events. I concentrate now first on this cursor. So far, I read examples on how to do it on matplotlib. however, i have trouble to understand how to integrate matplotlib on my code.
Also, is matplotlib the easiest way to do it on this code. or there might be a better way to do it.
any hint would be helpful
thank you in advance.
here is my desired output and the code of my app
import sys
from PySide2 import QtWidgets
from vispy import scene
from PySide2.QtCore import QMetaObject
from PySide2.QtWidgets import *
class SimpleItem(QtWidgets.QGraphicsItem):
def __init__(self):
QtWidgets.QGraphicsItem.__init__(self)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
def boundingRect(self):
penWidth = 1.0
return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
20 + penWidth, 20 + penWidth)
def paint(self, painter, option, widget):
rect = self.boundingRect()
painter.drawRect(rect)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.groupBox = QGroupBox(self.centralwidget)
self.groupBox.setObjectName("groupBox")
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
QMetaObject.connectSlotsByName(MainWindow)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# OpenGL drawing surface
self.canvas = scene.SceneCanvas(keys='interactive')
self.canvas.create_native()
self.canvas.native.setParent(self)
self.view = self.canvas.central_widget.add_view()
self.view.bgcolor = '#ffffff' # set the canva to a white background
scene2 = QGraphicsScene()
item = SimpleItem()
scene2.addItem(item)
self.setWindowTitle('MyApp')
def main():
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('my_gui')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
edit: I added a class (here it is a rectangle just as an example) to illustrate my problem. i have trouble integrating that snippet of the code (with SimpleItem) to OpenGL canvas
You can use the QApplication.setOverrideCursor method to assign a .png image file as your cursor when it appears inside of the Qt program.
Here is an example that is mostly based on the code in your question. And below is a gif that demonstrates the example. And the last image is the image I used in the code as cursor.png
Hope this helps
import sys
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
class SimpleItem(QtWidgets.QGraphicsItem):
def __init__(self):
QtWidgets.QGraphicsItem.__init__(self)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self._brush = QBrush(Qt.black)
def boundingRect(self):
penWidth = 1.0
return QRectF(-50 - penWidth / 2, -50 - penWidth / 2,
50 + penWidth, 50 + penWidth)
def paint(self, painter, option, widget):
rect = self.boundingRect()
painter.drawRect(rect)
painter.fillRect(rect, self._brush)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(800, 600)
self.scene = QGraphicsScene()
self.canvas = scene.SceneCanvas(keys='interactive')
self.view = QGraphicsView(self.scene)
item = SimpleItem()
self.scene.addItem(item)
self.setCentralWidget(self.view)
self.setWindowTitle('MyApp')
def main():
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('my_gui')
app = QtWidgets.QApplication([])
app.setOverrideCursor(QCursor(QPixmap('cursor.png')))
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am trying to create a gui with several live plot EEG/ECG graphs (each plot on different axes).
From what I understand, I need to create multiple PlotWidgets inside a grid layout.
I have created a live plot using pyqtgraph, below however I am unsure how to merge this into the GUI:
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import collections
import random
import time
import math
import numpy as np
class DynamicPlotter:
def __init__(self, sampleinterval=0.1, timewindow=10., size=(600, 350)):
# Data stuff
self.interval = int(sampleinterval * 1000)
self.bufsize = int(timewindow / sampleinterval)
self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
self.x = np.linspace(-timewindow, 0.0, self.bufsize)
self.y = np.zeros(self.bufsize, dtype=float)
# PyQtGraph stuff
self.app = QtGui.QApplication([])
self.plt = pg.plot(title='EEG/ECG Live Plot')
self.plt.resize(*size)
self.plt.showGrid(x=True, y=True)
#self.plt.setXRange(5,20, padding=0)
self.plt.setLabel('left', 'Amplitude', 'uVrms')
self.plt.setLabel('bottom', 'Time', 's')
self.curve = self.plt.plot(self.x, self.y, pen=(255, 0, 0))
# QTimer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateplot)
self.timer.start(self.interval)
def getdata(self):
frequency = 0.5
noise = random.normalvariate(0., 1.)
new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
return new
def updateplot(self):
self.databuffer.append(self.getdata())
self.y[:] = self.databuffer
self.curve.setData(self.x, self.y)
self.app.processEvents()
def run(self):
self.app.exec_()
if __name__ == '__main__':
livePlot = DynamicPlotter(sampleinterval=0.05, timewindow=5.)
livePlot.run()
Here is the basic GUI (3 plot widgets inside grid, and a few labels in mainWindow):
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(845, 727)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.labelTitle = QtWidgets.QLabel(self.centralwidget)
self.labelTitle.setGeometry(QtCore.QRect(280, 0, 291, 51))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
font.setWeight(75)
self.labelTitle.setFont(font)
self.labelTitle.setObjectName("labelTitle")
self.labelCh1 = QtWidgets.QLabel(self.centralwidget)
self.labelCh1.setGeometry(QtCore.QRect(20, 90, 31, 51))
self.labelCh1.setObjectName("labelCh1")
self.labelCh2 = QtWidgets.QLabel(self.centralwidget)
self.labelCh2.setGeometry(QtCore.QRect(20, 180, 31, 51))
self.labelCh2.setObjectName("labelCh2")
self.labelCh3 = QtWidgets.QLabel(self.centralwidget)
self.labelCh3.setGeometry(QtCore.QRect(20, 260, 31, 51))
self.labelCh3.setObjectName("labelCh3")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(70, 70, 741, 261))
self.widget.setObjectName("widget")
self.gridLayout = QtWidgets.QGridLayout(self.widget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.ch1PlotWidget = PlotWidget(self.widget)
self.ch1PlotWidget.setObjectName("ch1PlotWidget")
self.gridLayout.addWidget(self.ch1PlotWidget, 0, 0, 1, 1)
self.ch2PlotWidget = PlotWidget(self.widget)
self.ch2PlotWidget.setObjectName("ch2PlotWidget")
self.gridLayout.addWidget(self.ch2PlotWidget, 1, 0, 1, 1)
self.ch3PlotWidget = PlotWidget(self.widget)
self.ch3PlotWidget.setObjectName("ch3PlotWidget")
self.gridLayout.addWidget(self.ch3PlotWidget, 2, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 845, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.labelTitle.setText(_translate("MainWindow", "EEG/ECG Recording GUI"))
self.labelCh1.setText(_translate("MainWindow", "Ch 1"))
self.labelCh2.setText(_translate("MainWindow", "Ch 2"))
self.labelCh3.setText(_translate("MainWindow", "Ch 3"))
from pyqtgraph import PlotWidget
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
My question is how do I integrate these two so I can plot the live graphs in each widget?
Ideally I want to use a super class so I can simply import the unedited gui.
I have tried importing the gui.Ui_MainWindow into the class and then overwriting the self.plt to self.Ch1PlotWidget
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
import gui as gui
import sys
import pyqtgraph as pg
import collections
import random
import time
import math
import numpy as np
class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
# Data stuff
self.interval = 100
self.bufsize = int(10 / self.interval)
self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
self.x = np.linspace(-10, 0.0, self.bufsize)
self.y = np.zeros(self.bufsize, dtype=float)
# PyQtGraph stuff
self.app = QtGui.QApplication([])
self.ch1PlotWidget = pg.plot(title='Live Plot')
self.ch1PlotWidget.resize(600, 350)
self.ch1PlotWidget.showGrid(x=True, y=True)
# self.plt.setXRange(5,20, padding=0)
self.ch1PlotWidget.setLabel('left', 'Amplitude', 'uVrms')
self.ch1PlotWidget.setLabel('bottom', 'Time', 's')
self.curve = self.ch1PlotWidget.plot(self.x, self.y, pen=(255, 0, 0))
# QTimer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateplot)
self.timer.start(self.interval)
def getdata(self):
frequency = 0.5
noise = random.normalvariate(0., 1.)
new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
return new
def updateplot(self):
self.databuffer.append(self.getdata())
self.y[:] = self.databuffer
self.curve.setData(self.x, self.y)
self.app.processEvents()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Sorry for all the code, I am just very confused on how to implement logic to the gui.
Here is an option where you can use both classes with minimal changes.
Change the DynamicPlotter constructor to accept a PlotWidget as a argument instead of creating a new one, since they are created and added to a layout in Ui_Mainwindow. In the MainWindow class, create a DynamicPlotter object for each plot (and keep a persistent reference, in this case I added them to a list self.plots).
class DynamicPlotter:
def __init__(self, plot, sampleinterval=0.1, timewindow=10., size=(600, 350)):
# Data stuff
self.interval = int(sampleinterval * 1000)
self.bufsize = int(timewindow / sampleinterval)
self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
self.x = np.linspace(-timewindow, 0.0, self.bufsize)
self.y = np.zeros(self.bufsize, dtype=float)
# PyQtGraph stuff
self.plt = plot
self.plt.setTitle('EEG/ECG Live Plot')
self.plt.resize(*size)
self.plt.showGrid(x=True, y=True)
#self.plt.setXRange(5,20, padding=0)
self.plt.setLabel('left', 'Amplitude', 'uVrms')
self.plt.setLabel('bottom', 'Time', 's')
self.curve = self.plt.plot(self.x, self.y, pen=(255, 0, 0))
# QTimer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateplot)
self.timer.start(self.interval)
def getdata(self):
frequency = 0.5
noise = random.normalvariate(0., 1.)
new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
return new
def updateplot(self):
self.databuffer.append(self.getdata())
self.y[:] = self.databuffer
self.curve.setData(self.x, self.y)
class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
self.plots = []
for plot in (self.ch1PlotWidget, self.ch2PlotWidget, self.ch3PlotWidget):
self.plots.append(
DynamicPlotter(plot, sampleinterval=0.05, timewindow=5.)
)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I have a QGraphicsView with a QGraphicsScene. On QGraphicsScene, I output the image using QGraphicsPixmapItem. And I would like to draw an ellipse over the image with my mouse.
I have that code now. And the ellipse is drawn on MainWindow.
What do I need to do to have the ellipse drawn on an image in QGraphicsView?
Code OFT_MainWindow
# OFT_MainWindow
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(862, 710)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView.setGeometry(QtCore.QRect(10, 90, 841, 571))
self.graphicsView.setMouseTracking(True)
self.graphicsView.setObjectName("graphicsView")
self.buttonBox = QtWidgets.QDialogButtonBox(self.centralwidget)
self.buttonBox.setGeometry(QtCore.QRect(360, 670, 81, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.buttonBox.setFont(font)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(760, 10, 91, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.pushButton.setFont(font)
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(10, 10, 731, 31))
self.lineEdit.setObjectName("lineEdit")
self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_2.setGeometry(QtCore.QRect(10, 670, 331, 31))
self.lineEdit_2.setObjectName("lineEdit_2")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(10, 50, 841, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.pushButton_2.setFont(font)
self.pushButton_2.setObjectName("pushButton_2")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Browse"))
self.pushButton_2.setText(_translate("MainWindow", "Highlight field area"))
Main code
import OFT_MainWindow
import sys
import os
import numpy as np
import cv2
from PIL import Image, ImageQt, ImageEnhance
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.Qt import Qt
def main():
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
class MainWindow(QtWidgets.QMainWindow, OFT_MainWindow.Ui_MainWindow, QtWidgets.QGraphicsView, QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.browse_file)
self.pushButton_2.clicked.connect(self.imageFrame)
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.show()
###################################################################################
def browse_file(self):
self.lineEdit.clear()
file = QtWidgets.QFileDialog.getOpenFileName(self, "Choose file")
file = str(file[0])
if file:
self.lineEdit.setText(file) # добавить путь в lineEdit
# cap = cv2.VideoCapture(file)
###################################################################################
def imageFrame(self):
file = self.lineEdit.text()
self.drawing = False
self.lastPoint = QPoint()
scene = QtWidgets.QGraphicsScene(self)
self.image = QPixmap(file)
item = QtWidgets.QGraphicsPixmapItem(self.image)
scene.addItem(item)
view = self.graphicsView.setScene(scene)
# self.show()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
br = QtGui.QBrush(QtGui.QColor(0, 255, 0, 25))
qp.setBrush(br)
ellipse = qp.drawEllipse(QtCore.QRect(self.begin, self.end))
coord_a = self.begin
coord_a = str(coord_a)
coord_a = coord_a[20:][:-1]
coord_b = self.end
coord_b = str(coord_b)
coord_b = coord_b[20:][:-1]
coord = ('begin = ' + coord_a + ' end = ' + coord_b)
self.lineEdit_2.setText(coord)
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
###################################################################################
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.aboutToQuit.connect(app.deleteLater)
sys.exit(app.exec_())
If you want to paint something on a QGraphicsView, you have to add QGraphicsItem to its scene, otherwise you're making almost useless to crete a QGraphicsView to begin with.
One possible solution is to install an event filter on the scene, listen for mouse button events and draw an ellipse (using QGraphicsEllipseItem) on the scene if the first click is done within the contents of a QGraphicsPixmapItem.
Note that another possibility is to subclass from QGraphicsPixmapItem and implement its mouse events instead. It all depends on what you're going to do and what you're going to need from your program.
class MainWindow(QtWidgets.QMainWindow, OFT_MainWindow.Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# ...
self.ellipseItem = None
def imageFrame(self):
# ...
scene.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.GraphicsSceneMousePress:
for item in self.graphicsView.scene().items(event.scenePos()):
if isinstance(item, QtWidgets.QGraphicsPixmapItem):
self.reference = item
self.ellipseItem = QtWidgets.QGraphicsEllipseItem(item)
self.ellipseItem.setBrush(QtGui.QColor(0, 255, 0, 25))
self.start = item.mapFromScene(event.scenePos())
elif event.type() == QtCore.QEvent.GraphicsSceneMouseMove and self.ellipseItem:
end = self.reference.mapFromScene(event.scenePos())
self.ellipseItem.setRect(QtCore.QRectF(self.start, end))
elif event.type() == QtCore.QEvent.GraphicsSceneMouseRelease and self.ellipseItem:
self.ellipseItem = None
return super().eventFilter(source, event)
Note that subclassing your Main window from all those classes (QtWidgets.QMainWindow, OFT_MainWindow.Ui_MainWindow, QtWidgets.QGraphicsView, QtWidgets.QGraphicsScene) is not only unnecessary and meaningless, but absolutely WRONG: multiple-inheritance subclassing should only be done when you know what you're inheriting from and why, otherwise, just don't. For Qt widgets that load from UI files, just subclass from the base widget class and the Ui.
I have a QApplication in PyQt5 that tracks the mouse movement and updates a label with x & y coordinates. This works as expected when the mouse movement occurs within the main QDialog. However, when the mouse moves into a QTabWidget the label updating stops. It will resume again if I click (hold) and drag the mouse, but stops again once the left button is released. Why is this, and how can I change my code so the label continues to update when the mouse is moved into the QTabWidget?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyForm(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.show()
def mouseMoveEvent(self, event):
x = event.x()
y = event.y()
text = "x: {0}, y: {1}".format(x, y)
self.ui.labelTracking.setText(text)
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(653, 450)
Dialog.setMouseTracking(True)
self.tabWidget = QtWidgets.QTabWidget(Dialog)
self.tabWidget.setGeometry(QtCore.QRect(160, 0, 481, 451))
self.tabWidget.setMouseTracking(True)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.tabWidget.addTab(self.tab, "")
self.labelTracking = QtWidgets.QLabel(Dialog)
self.labelTracking.setGeometry(QtCore.QRect(10, 80, 131, 61))
self.labelTracking.setMouseTracking(True)
self.labelTracking.setText("")
self.labelTracking.setObjectName("labelTracking")
self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "Test Tab"))
if __name__=="__main__":
app = QtWidgets.QApplication(sys.argv)
w = MyForm()
w.show()
sys.exit(app.exec_())
It will work if you enable mouse tracking on the widget inside the QTabWidget, and set its mouseMoveEvent to the one you defined.
class MyForm(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.tab.mouseMoveEvent = self.mouseMoveEvent
self.show()
def mouseMoveEvent(self, event):
pos = event.windowPos().toPoint()
x = pos.x()
y = pos.y()
text = "x: {0}, y: {1}".format(x, y)
self.ui.labelTracking.setText(text)
And in Ui_Dialog include self.tab.setMouseTracking(True). QMouseEvent.windowPos() is used instead of pos() to get the coordinates relative to the window that received the event, as otherwise it would be relative to the tab widget when hovering over it.
I'm trying to overlay 2 pixmaps and convert them into a single pixmap in a QGraphics scene. Both pixmaps are transparent at certain locations. I want to combine the maps using the 'SourceOver' blend type listed here: I have a simple toy example below to illustrate my issue where I have created two dummy transparent pixmaps, one green and one blue. In reality, these maps are loaded from images and painted over, but this example reproduces the problem. Based on this How to add an image on the top of another image?, the approach I tried (4 lines commented out) was to create a QPainter with one of the pixmaps and then draw the other pixmap on top of it, however that crashes the program. Any ideas on how to fix this? I eventually want to be able to save the combined pixmap.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QPixmap, QPainter, QPen, QBrush, QPainterPath
from PyQt5.QtCore import (QLineF, QPointF, QRectF, Qt)
class Viewer(QtWidgets.QGraphicsView):
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self._scene = QtWidgets.QGraphicsScene(self)
self.photo = QtWidgets.QGraphicsPixmapItem()
self.label = QtWidgets.QGraphicsPixmapItem()
self._scene.addItem(self.photo)
self._scene.addItem(self.label)
self.setScene(self._scene)
def overlayMaps(self):
blue = QtGui.QPixmap(600, 600)
blue.fill(QtGui.QColor(0,0,255,0))
p = QPainter(blue)
self.pen = QPen()
self.pen.setColor(QtGui.QColor(0,0,255,255))
self.pen.setWidth(10)
p.setPen(self.pen)
p.drawLine(0,0,600,600)
green = QtGui.QPixmap(600, 600)
green.fill(QtGui.QColor(0,255,0,0))
p = QPainter(green)
self.pen = QPen()
self.pen.setColor(QtGui.QColor(0,255,0,255))
self.pen.setWidth(10)
p.setPen(self.pen)
p.drawLine(600,0,0,600)
self.photo.setPixmap(blue)
self.label.setPixmap(green)
resultPixmap = QtGui.QPixmap(self.photo.pixmap().width(), self.photo.pixmap().height())
# resultPainter = QtGui.QPainter(resultPixmap)
# resultPainter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
# resultPainter.drawPixmap(300,300, self.photo.pixmap())
# resultPainter.drawPixmap(300,300, self.label.pixmap())
def saveOverlayMap(self):
pass
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.viewer.overlayMaps()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 600, 600)
window.show()
sys.exit(app.exec_())
I have implemented a function that performs the action of joining depending on the mode, for a better appreciation I have moved the items.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
def join_pixmap(p1, p2, mode=QtGui.QPainter.CompositionMode_SourceOver):
s = p1.size().expandedTo(p2.size())
result = QtGui.QPixmap(s)
result.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(result)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.drawPixmap(QtCore.QPoint(), p1)
painter.setCompositionMode(mode)
painter.drawPixmap(result.rect(), p2, p2.rect())
painter.end()
return result
class Viewer(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(Viewer, self).__init__(parent)
self._scene = QtWidgets.QGraphicsScene(self)
self.setScene(self._scene)
blue = QtGui.QPixmap(100, 100)
blue.fill(QtCore.Qt.transparent)
p = QtGui.QPainter(blue)
pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0,0,255)), 10)
p.setPen(pen)
p.drawLine(0, 0, 100, 100)
p.end()
self.photo = self._scene.addPixmap(blue)
green = QtGui.QPixmap(100, 100)
green.fill(QtCore.Qt.transparent)
p = QtGui.QPainter(green)
pen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(0, 255, 0, 255)), 10)
p.setPen(pen)
p.drawLine(100, 0, 0, 100)
p.end()
self.label = self._scene.addPixmap(green)
self.label.setPos(200, 0)
self.overlayMaps()
def overlayMaps(self):
p1 = QtGui.QPixmap(self.photo.pixmap())
p2 = QtGui.QPixmap(self.label.pixmap())
result_pixmap = join_pixmap(self.photo.pixmap(), self.label.pixmap())
self.result_item = self._scene.addPixmap(result_pixmap)
self.result_item.setPos(100, 200)
result_pixmap.save("result_pixmap.png")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Viewer()
window.resize(640, 480)
window.show()
sys.exit(app.exec_())
result_pixmap.png