I have the same problem from this topic: QRubberBand move when I resize window, after a few try I realized that solution from this topic doesn't apply on QGraphics View. Why my selection move, arout QgraphicsView when I resize window.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# from PyQt4 import QtCore, QtWidgets
class ResizableRubberBand(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(ResizableRubberBand, self).__init__(parent)
self.draggable = False
self.mousePressPos = None
self.mouseMovePos = None
self._band = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
self._band.setGeometry(550, 550, 550, 550)
self._band.show()
self.show()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
self.mousePressPos = event.globalPos() # global
self.mouseMovePos = event.globalPos() - self.pos() # local
self.draggable = True
elif event.button() == QtCore.Qt.LeftButton:
self.position = QtCore.QPoint(event.pos())
self.upper_left = self.position
self.lower_right = self.position
self.mode = "drag_lower_right"
self._band.show()
def mouseMoveEvent(self, event):
if self.draggable and event.buttons() & QtCore.Qt.RightButton:
globalPos = event.globalPos()
print(globalPos)
diff = globalPos - self.mouseMovePos
self.move(diff)
self.mouseMovePos = globalPos - self.pos()
elif self._band.isVisible():
# visible selection
if self.mode is "drag_lower_right":
self.lower_right = QtCore.QPoint(event.pos())
# print(str(self.lower_right))
elif self.mode is "drag_upper_left":
self.upper_left = QtCore.QPoint(event.pos())
# print(str(self.upper_left))
# update geometry
self._band.setGeometry(QtCore.QRect(self.upper_left, self.lower_right).normalized())
def mouseReleaseEvent(self, event):
self.draggable = False
my main class:
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.band = ResizableRubberBand()
scene = QtWidgets.QGraphicsScene(self)
photo = QtGui.QPixmap('image.jpg')
scene.addPixmap(photo)
self.band.setScene(scene)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.band)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(800, 100, 600, 500)
window.show()
sys.exit(app.exec_())
before resize:
after resize:
The problem is caused because the coordinate system of the image is not the same as that of the QRubberBand. So the solution is that you both share the same coordinate system and for this we add the QRubberBand to the scene.
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
self.m_rubberBand = QtWidgets.QRubberBand(
QtWidgets.QRubberBand.Rectangle
)
self.m_rubberBand.setGeometry(QtCore.QRect(-1, -1, 2, 2))
self.m_rubberBand.hide()
item = self.scene().addWidget(self.m_rubberBand)
item.setZValue(1)
self.m_draggable = False
self.m_origin = QtCore.QPoint()
def mousePressEvent(self, event):
self.m_origin = self.mapToScene(event.pos()).toPoint()
self.m_rubberBand.setGeometry(
QtCore.QRect(self.m_origin, QtCore.QSize())
)
self.m_rubberBand.show()
self.m_draggable = True
super(GraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.m_draggable:
end_pos = self.mapToScene(event.pos()).toPoint()
self.m_rubberBand.setGeometry(
QtCore.QRect(self.m_origin, end_pos).normalized()
)
self.m_rubberBand.show()
def mouseReleaseEvent(self, event):
end_pos = self.mapToScene(event.pos()).toPoint()
self.m_rubberBand.setGeometry(
QtCore.QRect(self.m_origin, end_pos).normalized()
)
self.m_draggable = False
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = GraphicsView()
photo = QtGui.QPixmap("image.jpg")
w.scene().addPixmap(photo)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Related
I'm pretty new to PyQt and am trying to make an application with a QPixmap on the left, which can be drawn on, and a QTextEdit on the right (for a simple OCR GUI).
I looked at:
PyQt5 Image and QGridlayout
but I couldn't connect it with the code below (I'm losing my hair with all the head scratching!!)
When I try adapting the following code, what I get is a QMainWindow with the QPixmap as the background which can be drawn on with the mouse and a second occurance of the QPixmap in it's correct position, which can not be drawn on. Can someone tell me what I'm doing wrong?
Thank you very much!
# https://stackoverflow.com/questions/51475306/
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication,QGridLayout, QLabel, QWidget, QTextEdit
from PyQt5.QtGui import QPixmap, QPainter, QPen
class Menu(QMainWindow):
def __init__(self):
super().__init__()
self.drawing = False
self.lastPoint = QPoint()
self.image = QPixmap("S3.png")
self.setGeometry(100, 100, 500, 300)
self.resize(self.image.width(), self.image.height())
layout = QGridLayout()
# Add a QTextEdit box
self.edit = QTextEdit()
layout.addWidget(self.edit, 0, 0, 10, 20)
# This:
# https://stackoverflow.com/questions/52616553
# indicates that a QPixmap must be put into a label to insert into a QGridLayout
self.label = QLabel()
self.label.setPixmap(self.image)
layout.addWidget(self.label, 10, 20, 10, 20)
# https://stackoverflow.com/questions/37304684/
self.widget = QWidget()
self.widget.setLayout(layout)
self.setCentralWidget(self.widget)
self.show()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawPixmap(self.rect(), self.image)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
print(self.lastPoint)
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton and self.drawing:
painter = QPainter(self.image)
painter.setPen(QPen(Qt.red, 3, Qt.SolidLine))
painter.drawLine(self.lastPoint, event.pos())
print(self.lastPoint,event.pos())
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if event.button == Qt.LeftButton:
self.drawing = False
if __name__ == '__main__':
app = QApplication(sys.argv)
mainMenu = Menu()
sys.exit(app.exec_())
Each widget must fulfill a specific task, so I have created a widget that only has the painted function, the main widget works as a container for the painting widget and the QTextEdit.
from PyQt5 import QtCore, QtGui, QtWidgets
class Label(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Label, self).__init__(parent)
self.image = QtGui.QPixmap("S3.png")
self.drawing = False
self.lastPoint = QtCore.QPoint()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(QtCore.QPoint(), self.image)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.drawing = True
self.lastPoint = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
painter = QtGui.QPainter(self.image)
painter.setPen(QtGui.QPen(QtCore.Qt.red, 3, QtCore.Qt.SolidLine))
painter.drawLine(self.lastPoint, event.pos())
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if event.button == QtCore.Qt.LeftButton:
self.drawing = False
def sizeHint(self):
return self.image.size()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.label = Label()
self.textedit = QtWidgets.QTextEdit()
widget = QtWidgets.QWidget()
self.setCentralWidget(widget)
lay = QtWidgets.QHBoxLayout(widget)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
lay.addWidget(self.textedit)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I want to select a region from an image and get her coordinates. I have searched and I have found this code, it create object rubberband but I can't resize it.. weird, on linux (lubuntu) it's working but on macOS doesn't.. I want a version of a rubberband on macOS in special with those "grips", in macOS neither those grips doesn't appear.. and one more thing, it is possible to to move the rubberband rectangle by selecting with right click and move on image wherever I want, like moving an icon on desktop. Thanks!
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class ResizableRubberBand(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ResizableRubberBand, self).__init__(parent)
self.setWindowFlags(QtCore.Qt.SubWindow)
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(
QtWidgets.QSizeGrip(self), 0,
QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
layout.addWidget(
QtWidgets.QSizeGrip(self), 0,
QtCore.Qt.AlignRight | QtCore.Qt.AlignBottom)
self._band = QtWidgets.QRubberBand(
QtWidgets.QRubberBand.Rectangle, self)
self._band.show()
self.show()
def resizeEvent(self, event):
size = QtCore.QSize(3, 4)
size.scale(self.size(), QtCore.Qt.KeepAspectRatio)
self.resize(size)
self._band.resize(self.size())
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton('Show Rubber Band')
self.button.clicked.connect(self.handleButton)
self.label = QtWidgets.QLabel()
self.label.setScaledContents(True)
self.label.setPixmap(QtGui.QPixmap('image.JPG'))
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.label)
layout.addWidget(self.button)
def handleButton(self):
self.band = ResizableRubberBand(self.label)
self.band.setGeometry(50, 50, 150, 300)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(800, 100, 600, 500)
window.show()
sys.exit(app.exec_())
You're on the right track. PyQt uses event handlers to control mouse interaction and resize events. For your application, resizing grips will call resizeEvent and repainting the changed window involves paintEvent. Similarly, mouse interaction utilizes mousePressEvent, mouseMoveEvent, and mouseReleaseEvent as event handlers are called when these actions are triggered. Essentially, for each interaction that you want, you must implement the handler for that particular event. I used PyQt4 to test but it should be the same for PyQt5.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# from PyQt4 import QtCore, QtWidgets
class ResizableRubberBand(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ResizableRubberBand, self).__init__(parent)
self.draggable = True
self.dragging_threshold = 5
self.mousePressPos = None
self.mouseMovePos = None
self.borderRadius = 5
self.setWindowFlags(QtCore.Qt.SubWindow)
layout = QtWidgets.QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(
QtWidgets.QSizeGrip(self), 0,
QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
layout.addWidget(
QtWidgets.QSizeGrip(self), 0,
QtCore.Qt.AlignRight | QtCore.Qt.AlignBottom)
self._band = QtWidgets.QRubberBand(
QtWidgets.QRubberBand.Rectangle, self)
self._band.show()
self.show()
def resizeEvent(self, event):
self._band.resize(self.size())
def paintEvent(self, event):
# Get current window size
window_size = self.size()
qp = QtWidgets.QPainter()
qp.begin(self)
qp.setRenderHint(QtWidgets.QPainter.Antialiasing, True)
qp.drawRoundedRect(0, 0, window_size.width(), window_size.height(),
self.borderRadius, self.borderRadius)
qp.end()
def mousePressEvent(self, event):
if self.draggable and event.button() == QtCore.Qt.RightButton:
self.mousePressPos = event.globalPos() # global
self.mouseMovePos = event.globalPos() - self.pos() # local
super(ResizableRubberBand, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.draggable and event.buttons() & QtCore.Qt.RightButton:
globalPos = event.globalPos()
moved = globalPos - self.mousePressPos
if moved.manhattanLength() > self.dragging_threshold:
# Move when user drag window more than dragging_threshold
diff = globalPos - self.mouseMovePos
self.move(diff)
self.mouseMovePos = globalPos - self.pos()
super(ResizableRubberBand, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.mousePressPos is not None:
if event.button() == QtCore.Qt.RightButton:
moved = event.globalPos() - self.mousePressPos
if moved.manhattanLength() > self.dragging_threshold:
# Do not call click event or so on
event.ignore()
self.mousePressPos = None
super(ResizableRubberBand, self).mouseReleaseEvent(event)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton('Show Rubber Band')
self.button.clicked.connect(self.handleButton)
self.label = QtWidgets.QLabel()
self.label.setScaledContents(True)
self.label.setPixmap(QtGui.QPixmap('image.JPG'))
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.label)
layout.addWidget(self.button)
def handleButton(self):
self.band = ResizableRubberBand(self.label)
self.band.setGeometry(150, 150, 150, 150)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(800, 100, 600, 500)
window.show()
sys.exit(app.exec_())
I have a scene like this
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
def mousePressEvent(self, event):
print('scene pressed')
self.wid = MyRect(event.pos(), event.pos())
self.addItem(self.wid)
self.wid.show()
I would like class MyRect(QtWidgets.QGraphicsRectItem) with painter, mouse event and so on to be a draggable rectangle.
all stuff in MyRect
So then I could have many Rectangle to the scene and even after draw line between them and so on (kind of diagram app), but keeping objects related editable options in MyRect, MyLine , ....
I thought :
class MyRect(QtWidgets.QGraphicsRectItem):
def __init__(self, begin, end, parent=None):
super().__init__(parent)
self.begin = begin
self.end = end
def paintEvent(self, event):
print('painting')
qp = QtGui.QPainter(self)
qp.drawRect(QtCore.QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
But I does not work (paint event not initiated whereas mousepressed event in scene is intiated)
I did not find what I wanted through the web so started totry do it by myself. I'm pretty sure it is a must known starting point but I cannot find it
First of all a QGraphicsItem is not a QWidget, so it has those events and does not handle them directly, that's what QGraphicsView and QGraphicsScene do. For example you say that you want to have a moveable rectangle because that task is simple is QGraphicsView, it is not necessary to overwrite:
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
If you want to change the way you paint the rectangle you must overwrite the paint() method as shown below:
from PyQt5 import QtCore, QtGui, QtWidgets
class RectItem(QtWidgets.QGraphicsRectItem):
def paint(self, painter, option, widget=None):
super(RectItem, self).paint(painter, option, widget)
painter.save()
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(QtCore.Qt.red)
painter.drawEllipse(option.rect)
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = RectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Update:
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(GraphicsScene, self).__init__(QtCore.QRectF(-500, -500, 1000, 1000), parent)
self._start = QtCore.QPointF()
self._current_rect_item = None
def mousePressEvent(self, event):
if self.itemAt(event.scenePos(), QtGui.QTransform()) is None:
self._current_rect_item = QtWidgets.QGraphicsRectItem()
self._current_rect_item.setBrush(QtCore.Qt.red)
self._current_rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.addItem(self._current_rect_item)
self._start = event.scenePos()
r = QtCore.QRectF(self._start, self._start)
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._current_rect_item is not None:
r = QtCore.QRectF(self._start, event.scenePos()).normalized()
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self._current_rect_item = None
super(GraphicsScene, self).mouseReleaseEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene =GraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
I am using an overlay with a QPaintEvent to draw over other widgets. I still need the widgets underneath to register mouse events. When I call raise() with the WA_TransparentForMouseEvents flag, I gain control of the widgets again, but of course, lose the paintevent as it's no longer registering any mouse events. What are my options here?
class Overlay(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Overlay, self).__init__(parent)
self.hotBox = parent
self.resize(self.hotBox.width, self.hotBox.height)
def paintEvent(self, event):
#args: [QEvent]
if any ([self.hotBox.name=="main", self.hotBox.name=="viewport"]):
self.raise_()
self.setWindowFlags(QtCore.Qt.WA_TransparentForMouseEvents)
#Initialize painter
painter = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(115, 115, 115), 3, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
painter.setPen(pen)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setBrush(QtGui.QColor(115, 115, 115))
painter.drawEllipse(self.hotBox.point, 5, 5)
#perform paint
if self.hotBox.mousePosition:
mouseX = self.hotBox.mousePosition.x()
mouseY = self.hotBox.mousePosition.y()
line = QtCore.QLine(mouseX, mouseY, self.hotBox.point.x(), self.hotBox.point.y())
painter.drawLine(line)
painter.drawEllipse(mouseX-5, mouseY-5, 10, 10)
If you want to create an overlay using the events mouseXXXEvent you must use an eventFilter, for this solution I based on this answer of #KubaOber adding more functionalities:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
class Overlay(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Overlay, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground)
self.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
self.start_line, self.end_line = QtCore.QPoint(), QtCore.QPoint()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.fillRect(self.rect(), QtGui.QColor(80, 80, 255, 128))
if not self.start_line.isNull() and not self.end_line.isNull():
painter.drawLine(self.start_line, self.end_line)
def mousePressEvent(self, event):
self.start_line = event.pos()
self.end_line = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end_line = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.start_line = QtCore.QPoint()
self.end_line = QtCore.QPoint()
class OverlayFactoryFilter(QtCore.QObject):
def __init__(self, parent=None):
super(OverlayFactoryFilter, self).__init__(parent)
self.m_overlay = None
def setWidget(self, w):
w.installEventFilter(self)
if self.m_overlay is None:
self.m_overlay = Overlay()
self.m_overlay.setParent(w)
def eventFilter(self, obj, event):
if not obj.isWidgetType():
return False
if event.type() == QtCore.QEvent.MouseButtonPress:
self.m_overlay.mousePressEvent(event)
elif event.type() == QtCore.QEvent.MouseButtonRelease:
self.m_overlay.mouseReleaseEvent(event)
elif event.type() == QtCore.QEvent.MouseMove:
self.m_overlay.mouseMoveEvent(event)
elif event.type() == QtCore.QEvent.MouseButtonDblClick:
self.m_overlay.mouseDoubleClickEvent(event)
elif event.type() == QtCore.QEvent.Resize:
if self.m_overlay and self.m_overlay.parentWidget() == obj:
self.m_overlay.resize(obj.size())
elif event.type() == QtCore.QEvent.Show:
self.m_overlay.raise_()
return super(OverlayFactoryFilter, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
factory = OverlayFactoryFilter()
w = QtWidgets.QWidget()
factory.setWidget(w)
button = QtWidgets.QPushButton("Press me", w)
w.show()
sys.exit(app.exec_())
I have an app that sets a matplotlib graph to a FigurCanvas and then adds the FigurCanvas to my AppWindow, I have it set up so the graph is draggable. However when I drag the graph the window it is contained in stays where it is so the graph get dragged off the window. Is there a way to bind the two together so when the graph is moved the window stays with it? Here is the code.
from PyQt4 import QtCore
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class GraphCanvas(FigureCanvas):
def __init__(self):
# The window
self.fig = Figure(figsize=(5, 5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
self.ax1.plot([1,2,3], [1,2,3], linewidth=2, color="#c6463d", label="line1")
FigureCanvas.__init__(self, self.fig)
# drag properties
self.draggable = True
self.dragging_threshold = 5
self.__mousePressPos = None
self.__mouseMovePos = None
def mousePressEvent(self, event):
if self.draggable and event.button() == QtCore.Qt.LeftButton:
self.__mousePressPos = event.globalPos() # global
self.__mouseMovePos = event.globalPos() - self.pos() # local
super(GraphCanvas, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.draggable and event.buttons() & QtCore.Qt.LeftButton:
globalPos = event.globalPos()
moved = globalPos - self.__mousePressPos
if moved.manhattanLength() > self.dragging_threshold:
# move when user drag window more than dragging_threshould
diff = globalPos - self.__mouseMovePos
self.move(diff)
self.__mouseMovePos = globalPos - self.pos()
super(GraphCanvas, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.__mousePressPos is not None:
if event.button() == QtCore.Qt.LeftButton:
moved = event.globalPos() - self.__mousePressPos
if moved.manhattanLength() > self.dragging_threshold:
# do not call click event or so on
event.ignore()
self.__mousePressPos = None
super(GraphCanvas, self).mouseReleaseEvent(event)
''' End Class '''
class AppWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(AppWindow, self).__init__(parent)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
layout = QtGui.QVBoxLayout(self)
cpu_canvas = GraphCanvas()
layout.addWidget(cpu_canvas)
''' End Class'''
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
main = AppWindow()
main.show()
sys.exit(app.exec_())
If you want to drag the AppWindow you should register the dragging in this class, instead of the Figure canvas.
Inside figure canvas you can then route the drag events to the AppWindow.
The following should work for you, where I did not change much of the code, just rearanged it, added the parent argument to GraphCanvas and let the dragging functions call their parent's functions.
from PyQt4 import QtCore
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class GraphCanvas(FigureCanvas):
def __init__(self, parent = None):
self.parent = parent
# The window
self.fig = Figure(figsize=(5, 5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
self.ax1.plot([1,2,3], [1,2,3], linewidth=2, color="#c6463d", label="line1")
FigureCanvas.__init__(self, self.fig)
def mousePressEvent(self, event):
self.parent.mousePressEvent(event)
def mouseMoveEvent(self,event):
self.parent.mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.parent.mouseReleaseEvent(event)
''' End Class '''
class AppWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(AppWindow, self).__init__(parent)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
layout = QtGui.QVBoxLayout(self)
cpu_canvas = GraphCanvas(self)
layout.addWidget(cpu_canvas)
self.draggable = True
self.dragging_threshold = 5
self.__mousePressPos = None
self.__mouseMovePos = None
def mousePressEvent(self, event):
if self.draggable and event.button() == QtCore.Qt.LeftButton:
self.__mousePressPos = event.globalPos() # global
self.__mouseMovePos = event.globalPos() - self.pos() # local
super(AppWindow, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.draggable and event.buttons() & QtCore.Qt.LeftButton:
globalPos = event.globalPos()
moved = globalPos - self.__mousePressPos
if moved.manhattanLength() > self.dragging_threshold:
# move when user drag window more than dragging_threshould
diff = globalPos - self.__mouseMovePos
self.move(diff)
self.__mouseMovePos = globalPos - self.pos()
super(AppWindow, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.__mousePressPos is not None:
if event.button() == QtCore.Qt.LeftButton:
moved = event.globalPos() - self.__mousePressPos
if moved.manhattanLength() > self.dragging_threshold:
# do not call click event or so on
event.ignore()
self.__mousePressPos = None
super(AppWindow, self).mouseReleaseEvent(event)
''' End Class'''
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
main = AppWindow()
main.show()
sys.exit(app.exec_())