如何通过QMainWindow在QGraphicsView上实现绘图操作?
Got it, let's break down your requirements step by step and fix this for you—adding scrollbars to QGraphicsView, routing all mouse events to QMainWindow, and drawing on the view via the main window—with concrete, working code examples.
1. Enable Scrollbars in QGraphicsView
QGraphicsView comes with scrollbars out of the box, but they only show up when your QGraphicsScene is larger than the view's visible area. If you want to force them to stay visible (regardless of scene size), you can set their policies explicitly:
class Window(QtGui.QGraphicsView): def __init__(self, parent=None): super(Window, self).__init__(parent) self.scene = QtGui.QGraphicsScene(self) self.setScene(self.scene) # Force scrollbars to remain visible (optional, adjust as needed) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) # Set a larger scene size to trigger scrollbars by default self.scene.setSceneRect(0, 0, 1000, 1000)
2. Route All Mouse Events to QMainWindow
Since you want QMainWindow to handle every mouse interaction, you need to override the mouse event methods in your QGraphicsView class and forward events up to the parent main window. We'll use custom signals here for cleaner, more modular code:
class Window(QtGui.QGraphicsView): # Define custom signals to emit mouse event data mousePressed = QtCore.pyqtSignal(QtCore.QPoint) mouseMoved = QtCore.pyqtSignal(QtCore.QPoint) mouseReleased = QtCore.pyqtSignal(QtCore.QPoint) def __init__(self, parent=None): super(Window, self).__init__(parent) self.scene = QtGui.QGraphicsScene(self) self.setScene(self.scene) # ... scrollbar setup from step 1 ... # Override mouse events to emit signals def mousePressEvent(self, event): self.mousePressed.emit(event.pos()) super(Window, self).mousePressEvent(event) # Keep default view behavior if needed def mouseMoveEvent(self, event): self.mouseMoved.emit(event.pos()) super(Window, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): self.mouseReleased.emit(event.pos()) super(Window, self).mouseReleaseEvent(event)
Then connect these signals to your QMainWindow's handler methods:
class MainWindow(QtGui.QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.view = Window(self) self.setCentralWidget(self.view) # Link view signals to main window handlers self.view.mousePressed.connect(self.handleMousePress) self.view.mouseMoved.connect(self.handleMouseMove) self.view.mouseReleased.connect(self.handleMouseRelease) def handleMousePress(self, pos): # Convert view coordinates to scene coordinates (critical for scrollbars!) scene_pos = self.view.mapToScene(pos) print(f"Mouse pressed at scene position: {scene_pos.x()}, {scene_pos.y()}") # Add drawing logic here (see next section) def handleMouseMove(self, pos): scene_pos = self.view.mapToScene(pos) print(f"Mouse moved to scene position: {scene_pos.x()}, {scene_pos.y()}") def handleMouseRelease(self, pos): scene_pos = self.view.mapToScene(pos) print(f"Mouse released at scene position: {scene_pos.x()}, {scene_pos.y()}")
3. Draw on QGraphicsView via QMainWindow
The proper way to draw in QGraphicsView is to add items to its QGraphicsScene (the view is just a viewport for the scene). From your QMainWindow, you can access the view's scene directly to add shapes, lines, or custom graphics items.
Example: Draw a Red Line on Mouse Drag
Modify the MainWindow handlers to add interactive drawing:
class MainWindow(QtGui.QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle("Graphics View with Main Window Control") self.setGeometry(100, 100, 800, 600) self.view = Window(self) self.setCentralWidget(self.view) self.view.mousePressed.connect(self.handleMousePress) self.view.mouseMoved.connect(self.handleMouseMove) self.view.mouseReleased.connect(self.handleMouseRelease) self.current_line = None # Track the line being drawn def handleMousePress(self, pos): scene_pos = self.view.mapToScene(pos) # Create a new line item starting at the press position self.current_line = QtGui.QGraphicsLineItem(QtCore.QLineF(scene_pos, scene_pos)) self.current_line.setPen(QtGui.QPen(QtCore.Qt.red, 2)) self.view.scene.addItem(self.current_line) def handleMouseMove(self, pos): if self.current_line is not None: scene_pos = self.view.mapToScene(pos) # Update the line's end position as the mouse moves line = self.current_line.line() line.setP2(scene_pos) self.current_line.setLine(line) def handleMouseRelease(self, pos): self.current_line = None # Reset after drawing is done
Full Working Code
Putting it all together, here's the complete, runnable code:
import sys from PyQt4 import QtGui from PyQt4 import QtCore class Window(QtGui.QGraphicsView): mousePressed = QtCore.pyqtSignal(QtCore.QPoint) mouseMoved = QtCore.pyqtSignal(QtCore.QPoint) mouseReleased = QtCore.pyqtSignal(QtCore.QPoint) def __init__(self, parent=None): super(Window, self).__init__(parent) self.scene = QtGui.QGraphicsScene(self) self.setScene(self.scene) # Enable scrollbars self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) # Set large scene size to test scrollbars self.scene.setSceneRect(0, 0, 1000, 1000) def mousePressEvent(self, event): self.mousePressed.emit(event.pos()) super(Window, self).mousePressEvent(event) def mouseMoveEvent(self, event): self.mouseMoved.emit(event.pos()) super(Window, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): self.mouseReleased.emit(event.pos()) super(Window, self).mouseReleaseEvent(event) class MainWindow(QtGui.QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle("Graphics View with Main Window Control") self.setGeometry(100, 100, 800, 600) self.view = Window(self) self.setCentralWidget(self.view) self.view.mousePressed.connect(self.handleMousePress) self.view.mouseMoved.connect(self.handleMouseMove) self.view.mouseReleased.connect(self.handleMouseRelease) self.current_line = None def handleMousePress(self, pos): scene_pos = self.view.mapToScene(pos) self.current_line = QtGui.QGraphicsLineItem(QtCore.QLineF(scene_pos, scene_pos)) self.current_line.setPen(QtGui.QPen(QtCore.Qt.red, 2)) self.view.scene.addItem(self.current_line) def handleMouseMove(self, pos): if self.current_line is not None: scene_pos = self.view.mapToScene(pos) line = self.current_line.line() line.setP2(scene_pos) self.current_line.setLine(line) def handleMouseRelease(self, pos): self.current_line = None if __name__ == "__main__": app = QtGui.QApplication(sys.argv) main_win = MainWindow() main_win.show() sys.exit(app.exec_())
Quick Notes:
- Always use
mapToScene()when converting mouse positions from the view to the scene—this accounts for scrollbar offsets and any zoom you might add later. - Drawing via
QGraphicsSceneitems is far more flexible than painting directly on the view; you can easily move, edit, or delete items later. - The signal-based approach keeps your code decoupled, so you can adjust event handling or drawing logic without rewriting the entire view class.
内容的提问来源于stack exchange,提问作者Aquarius_Girl

