I have a set of predetermined X and Y coordinates that I am using to place QGraphicsItem's as points and then placing each of those points in a QGraphicsView. I have my panning button set to be the middle mouse button, but I can only pan if I have zoomed in. Also, I can only pan to my furthers point.
Is there a way to set the QGraphicsView so that it will not stop panning at a point and that I can pan at any zoom level? Also as a side note, Later I would like to be able to select these points to set and get attributes from them, so I would still like to ability to interact with them?
Update
I made a couple of gif's to show how the controls currently work and then how I would like them to work.
Currently
You can see here I am not able to move past the limits of the scroll bars
What I would like
I would like to be able to move the screen past where the squares are being placed so that if the user desires they can move some off the screen or they could zoom out further than where fitInView would allow. I know the zoom part is in my wheel event and just haven't changed it yet. The big thing is being able to move them past at any zoom level.
please let me know if you need more explanation.
Code
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
QTimer.singleShot(0, self.fitInView) # This is done so that it can fit into view on load
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(self.ScrollHandDrag)
# self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Fit Into View')
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
self.viewer.fitInView()
def fitPoints(self):
self.viewer.fitInView()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
Update2
Please try this Code. Is this appropriate answer for you?
If so, I want to add new explanation.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Line(QGraphicsLineItem):
def __init__(self, x1, y1, x2, y2):
super(Line, self).__init__()
pen = self.pen()
pen.setWidth(10)
pen.setColor(Qt.gray)
pen.setStyle(Qt.SolidLine)
self.setPen(pen)
self.origin = self.pos()
self.setZValue(1)
self.setLine(QLineF(x1, y1, x2, y2))
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self.origin = QPointF(self.pos())
self._brush = QBrush(Qt.black)
self.setZValue(2)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self.mid_panning = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self._scene.setBackgroundBrush(Qt.white)
self.white_board = QGraphicsRectItem()
self.white_board.setZValue(1)
self.white_board.setBrush(Qt.white)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self._old_x = QCursor.pos().x()
self._old_y = QCursor.pos().y()
QTimer.singleShot(0, self.fitInView) # This is done so that it can fit into view on load
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX , minY , distance , distance )
self.white_board.setRect(QRectF(minX , minY , distance , distance ))
self._scene.addItem(self.white_board)
line1 = Line(minX, minY, minX+distance, minY+distance)
line2 = Line(minX+distance, minY, minX, minY+distance)
self._scene.addItem(line1)
self._scene.addItem(line2)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def make_area2(self, area):
x = area.x()
y = area.y()
width = area.width()
height = area.height()
x -= 2*x
y -= 2*y
width = width*2
height = height*2
area = QRectF(x, y, width, height)
return area
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
print(unity.width(), unity.height())
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
print(scenerect.width(), scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(self.ScrollHandDrag)
# self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.origin = event.pos()
self.original_event = event
self.mid_panning = True
self.scene_origin = self.mapToScene(event.pos())
self._old_x = QCursor.pos().x()
self._old_y = QCursor.pos().y()
for i in self._scene.items():
i.origin = i.pos()
# I recommend that you get the all item position.
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
self.mid_panning = False
# here you set the original point.
for i in self._scene.items():
i.setPos(i.origin)
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def calc_offset(self, x, y):
offset_x = x - int(self.viewport().width()/2)
offset_y = y - int(self.viewport().height()/2)
return offset_x, offset_y
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
elif self.mid_panning:
new_x = event.x()
new_y = event.y()
offset_x, offset_y = self.calc_offset(new_x, new_y)
for item in self._scene.items():
item.setPos(QPointF(item.pos().x() - (new_x - self._old_x)*10, item.pos().y() - (new_y - self._old_y)*10))
self._old_x = new_x
self._old_y = new_y
return
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Fit Into View')
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
self.viewer.fitInView()
def fitPoints(self):
self.viewer.fitInView()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(300, 400, 800, 600)
window.show()
sys.exit(app.exec_())
Update
The key solution is to use scroll(dx, dy).
Explanation
At the first time, I doubted if there was the place of "offscreen" or not.
The reason is QGraphicsView only shows QGraphicsScene and QGraphicsItem on the scene.QGraphicsView can show all the scene or the part of the scene,I thought it cannot show the "offscreen".
I have experienced pygame,so I attempted to implement the idea of "offset",but pygame has originally an infinite vast of screen and only shows the slight part of it.So We can see the "offscreen".On the contrast,QGraphicsScene is a finite screen the user decide the range by setSceneRect.And fitInView means the same range of screen.
But QGraphicsView is a widget for showing the scene.We can scroll the widget.
So, We can do the same thing by scrolling the widget itself.
code.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self.mid_panning = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
QTimer.singleShot(0, self.fitInView) # This is done so that it can fit into view on load
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
print(unity.width(), unity.height())
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(self.ScrollHandDrag)
# self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.origin = event.pos()
self.original_event = event
self.mid_panning = True
self.scene_origin = self.mapToScene(event.pos())
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
self.mid_panning = False
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def calc_offset(self, x, y):
offset_x = x - int(self.viewport().width()/2)
offset_y = y - int(self.viewport().height()/2)
return offset_x, offset_y
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
elif self.mid_panning:
offset_x, offset_y = self.calc_offset(event.pos().x(), event.pos().y())
self.scroll(offset_x,offset_y)
return
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Fit Into View')
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
self.viewer.fitInView()
def fitPoints(self):
self.viewer.fitInView()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(300, 400, 800, 600)
window.show()
sys.exit(app.exec_())
Related
I am writing a program in Pyside6 and use my own Titlebar.
When running with pyside6, I get this problem.
I need to remove this space
Using PyQt5 everything is fine
What differences in the work of these frameworks can cause this problem and what should be used to fix it.
the code on both frameworks is the same, but the result is different, I can't understand why this is happening
My code using PySide6
TextEditorQt.py
from PySide6 import QtGui
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtWidgets import QApplication
from TextEditorUI import Ui_MainWindow, QMainWindow # импорт нашего сгенерированного файла
from PySide6.QtCore import QSettings, QPoint, QSize
from Titlebar import FramelessWindow
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(762, 580)
MainWindow.setStyleSheet(u"")
self.open_action = QAction(MainWindow)
self.open_action.setObjectName(u"open_action")
self.newfile_action = QAction(MainWindow)
self.newfile_action.setObjectName(u"newfile_action")
self.save_action = QAction(MainWindow)
self.save_action.setObjectName(u"save_action")
self.saveas_action = QAction(MainWindow)
self.saveas_action.setObjectName(u"saveas_action")
self.exit_action = QAction(MainWindow)
self.exit_action.setObjectName(u"exit_action")
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout")
self.textEdit = QTextEdit(self.centralwidget)
self.textEdit.setObjectName(u"textEdit")
self.textEdit.setStyleSheet(u"alternate-background-color: rgb(85, 0, 255);\n"
"background-color: rgb(255, 255, 255);")
self.gridLayout.addWidget(self.textEdit, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 762, 22))
self.menu = QMenu(self.menubar)
self.menu.setObjectName(u"menu")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menu.menuAction())
self.menu.addAction(self.open_action)
self.menu.addAction(self.newfile_action)
self.menu.addAction(self.save_action)
self.menu.addAction(self.saveas_action)
self.menu.addSeparator()
self.menu.addAction(self.exit_action)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Matewriter", None))
self.open_action.setText(
QCoreApplication.translate("MainWindow", u"\u041e\u0442\u043a\u0440\u044b\u0442\u044c", None))
self.newfile_action.setText(QCoreApplication.translate("MainWindow", u"\u041d\u043e\u0432\u044b\u0439", None))
self.save_action.setText(
QCoreApplication.translate("MainWindow", u"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", None))
self.saveas_action.setText(QCoreApplication.translate("MainWindow",
u"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a...",
None))
self.exit_action.setText(QCoreApplication.translate("MainWindow", u"\u0412\u044b\u0445\u043e\u0434", None))
# if QT_CONFIG(tooltip)
self.textEdit.setToolTip(
QCoreApplication.translate("MainWindow", u"<html><head/><body><p><br/></p></body></html>", None))
# endif // QT_CONFIG(tooltip)
self.menu.setTitle(QCoreApplication.translate("MainWindow", u"\u0424\u0430\u0439\u043b", None))
# retranslateUi
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowIcon(QtGui.QIcon('icon.ico'))
self.setupUi(self)
self.curFile = ''
self.setCurrentFile('')
self.createStatusBar()
self.textEdit.document().contentsChanged.connect(self.documentWasModified)
self.setCurrentFile('')
self.settings = QSettings('Matewriter', 'Matewriter')
self.exit_action.triggered.connect(QApplication.quit)
self.save_action.triggered.connect(self.save)
self.open_action.triggered.connect(self.open)
self.newfile_action.triggered.connect(self.newFile)
self.saveas_action.triggered.connect(self.saveAs)
self.open_action.setShortcut('Ctrl+O')
self.newfile_action.setShortcut('Ctrl+N')
self.save_action.setShortcut('Ctrl+S')
# Конфиги окна
windowScreenGeometry = self.settings.value("windowScreenGeometry")
windowScreenState = self.settings.value("windowScreenState")
if windowScreenGeometry:
self.restoreGeometry(windowScreenGeometry)
else:
# self.resize(600)
self.resize(600, 600) # !!!
if windowScreenState:
self.restoreState(windowScreenState)
def closeEvent(self, event):
self.settings.setValue("windowScreenGeometry", self.saveGeometry())
self.settings.setValue("windowScreenState", self.saveState())
if self.maybeSave():
self.writeSettings()
event.accept()
else:
event.ignore()
def newFile(self):
if self.maybeSave():
self.textEdit.clear()
self.setCurrentFile('')
def open(self):
if self.maybeSave():
fileName, _ = QFileDialog.getOpenFileName(self)
if fileName:
self.loadFile(fileName)
def save(self):
if self.curFile:
return self.saveFile(self.curFile)
return self.saveAs()
def saveAs(self):
fileName, _ = QFileDialog.getSaveFileName(self)
if fileName:
return self.saveFile(fileName)
return False
def documentWasModified(self):
self.setWindowModified(self.textEdit.document().isModified())
def createStatusBar(self):
self.statusBar().showMessage("Ready")
def readSettings(self):
settings = QSettings("MateWriter")
pos = settings.value("pos", QPoint(200, 200))
size = settings.value("size", QSize(400, 400))
self.resize(size)
self.move(pos)
def writeSettings(self):
settings = QSettings("MateWriter")
settings.setValue("pos", self.pos())
settings.setValue("size", self.size())
def maybeSave(self):
if self.textEdit.document().isModified():
ret = QMessageBox.warning(self, "MateWriter",
"The document has been modified.\nDo you want to save "
"your changes?",
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
if ret == QMessageBox.Save:
return self.save()
if ret == QMessageBox.Cancel:
return False
return True
def loadFile(self, fileName):
file = QFile(fileName)
if not file.open(QFile.ReadOnly | QFile.Text):
QMessageBox.warning(self, "MateWriter",
"Cannot read file %s:\n%s." % (fileName, file.errorString()))
return
inf = QTextStream(file)
QApplication.setOverrideCursor(Qt.WaitCursor)
self.textEdit.setPlainText(inf.readAll())
QApplication.restoreOverrideCursor()
self.setCurrentFile(fileName)
self.statusBar().showMessage("File loaded", 2000)
def saveFile(self, fileName):
file = QFile(fileName)
if not file.open(QFile.WriteOnly | QFile.Text):
QMessageBox.warning(self, "MateWriter",
"Cannot write file %s:\n%s." % (fileName, file.errorString()))
return False
outf = QTextStream(file)
QApplication.setOverrideCursor(Qt.WaitCursor)
outf << self.textEdit.toPlainText()
QApplication.restoreOverrideCursor()
self.setCurrentFile(fileName)
self.statusBar().showMessage("File saved", 2000)
return True
def setCurrentFile(self, fileName):
self.curFile = fileName
self.textEdit.document().setModified(False)
self.setWindowModified(False)
if self.curFile:
shownName = self.strippedName(self.curFile)
else:
shownName = 'untitled.txt'
self.setWindowTitle(" %s[*] - MateWriter" % shownName)
def strippedName(self, fullFileName):
return QFileInfo(fullFileName).fileName()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
app.setStyle('Fusion')
w = FramelessWindow()
w.setWindowTitle('Тестовая строка заголовка')
w.setWindowIcon(QIcon('Qt.ico'))
# w.setWidget(MainWindow(MainWindow)) # Добавить свое окно
w.setWidget(MainWindow()) # !!!
w.show()
sys.exit(app.exec_())
Titlebar.py
from PySide6.QtCore import Qt, Signal, QPoint, QFileInfo
from PySide6.QtGui import QFont, QEnterEvent, QPainter, QColor, QPen
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QSpacerItem, QSizePolicy, QPushButton)
class TitleBar(QWidget):
# Сигнал минимизации окна
windowMinimumed = Signal()
# увеличить максимальный сигнал окна
windowMaximumed = Signal()
# сигнал восстановления окна
windowNormaled = Signal()
# сигнал закрытия окна
windowClosed = Signal()
# Окно мобильных
windowMoved = Signal(QPoint)
# Сигнал Своя Кнопка +++
signalButtonMy = Signal()
def __init__(self, *args, **kwargs):
super(TitleBar, self).__init__(*args, **kwargs)
# Поддержка настройки фона qss
self.setAttribute(Qt.WA_StyledBackground, True)
self.mPos = None
self.iconSize = 20 # Размер значка по умолчанию
# Установите цвет фона по умолчанию, иначе он будет прозрачным из-за влияния родительского окна
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(palette.Window, QColor(240, 240, 240))
self.setPalette(palette)
# макет
layout = QHBoxLayout(self, spacing=0)
layout.setContentsMargins(0, 0, 0, 0)
# значок окна
self.iconLabel = QLabel(self)
self.iconLabel.setMargin(10)
# self.iconLabel.setScaledContents(True)
layout.addWidget(self.iconLabel)
# название окна
self.titleLabel = QLabel(self)
self.titleLabel.setMargin(2)
layout.addWidget(self.titleLabel)
# Средний телескопический бар
layout.addSpacerItem(QSpacerItem(
40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
# Использовать шрифты Webdings для отображения значков
font = self.font() or QFont()
font.setFamily('Webdings')
# Своя Кнопка ++++++++++++++++++++++++++
self.buttonMy = QPushButton(
'#', self, clicked=self.showButtonMy, font=font, objectName='buttonMy')
layout.addWidget(self.buttonMy)
# Свернуть кнопку
self.buttonMinimum = QPushButton(
'0', self, clicked=self.windowMinimumed.emit, font=font, objectName='buttonMinimum')
layout.addWidget(self.buttonMinimum)
# Кнопка Max / restore
self.buttonMaximum = QPushButton(
'1', self, clicked=self.showMaximized, font=font, objectName='buttonMaximum')
layout.addWidget(self.buttonMaximum)
# Кнопка закрытия
self.buttonClose = QPushButton(
'r', self, clicked=self.windowClosed.emit, font=font, objectName='buttonClose')
layout.addWidget(self.buttonClose)
# начальная высота
self.setHeight()
# +++ Вызывается по нажатию кнопки buttonMy
def showButtonMy(self):
print("Своя Кнопка ")
self.signalButtonMy.emit()
def showMaximized(self):
if self.buttonMaximum.text() == '1':
# Максимизировать
self.buttonMaximum.setText('2')
self.windowMaximumed.emit()
else: # Восстановить
self.buttonMaximum.setText('1')
self.windowNormaled.emit()
def setHeight(self, height=38):
""" Установка высоты строки заголовка """
self.setMinimumHeight(height)
self.setMaximumHeight(height)
# Задайте размер правой кнопки ?
self.buttonMinimum.setMinimumSize(height, height)
self.buttonMinimum.setMaximumSize(height, height)
self.buttonMaximum.setMinimumSize(height, height)
self.buttonMaximum.setMaximumSize(height, height)
self.buttonClose.setMinimumSize(height, height)
self.buttonClose.setMaximumSize(height, height)
self.buttonMy.setMinimumSize(height, height)
self.buttonMy.setMaximumSize(height, height)
def setTitle(self, title):
""" Установить заголовок """
self.titleLabel.setText(title)
def setIcon(self, icon):
""" настройки значокa """
self.iconLabel.setPixmap(icon.pixmap(self.iconSize, self.iconSize))
def setIconSize(self, size):
""" Установить размер значка """
self.iconSize = size
def enterEvent(self, event):
self.setCursor(Qt.ArrowCursor)
super(TitleBar, self).enterEvent(event)
def mouseDoubleClickEvent(self, event):
super(TitleBar, self).mouseDoubleClickEvent(event)
self.showMaximized()
def mousePressEvent(self, event):
""" Событие клика мыши """
if event.button() == Qt.LeftButton:
self.mPos = event.pos()
event.accept()
def mouseReleaseEvent(self, event):
''' Событие отказов мыши '''
self.mPos = None
event.accept()
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton and self.mPos:
self.windowMoved.emit(self.mapToGlobal(event.pos() - self.mPos))
event.accept()
# Перечислить верхнюю левую, нижнюю правую и четыре неподвижные точки
Left, Top, Right, Bottom, LeftTop, RightTop, LeftBottom, RightBottom = range(8)
class FramelessWindow(QWidget):
# Четыре периметра
Margins = 5
def __init__(self, *args, **kwargs):
super(FramelessWindow, self).__init__(*args, **kwargs)
self._pressed = False
self.Direction = None
self.resize(762, 580)
# Фон прозрачный
self.setAttribute(Qt.WA_TranslucentBackground, True)
# Нет границы
self.setWindowFlag(Qt.FramelessWindowHint)
# Отслеживание мыши
self.setMouseTracking(True)
# макет
layout = QVBoxLayout(self, spacing=0)
# Зарезервировать границы для изменения размера окна без полей
layout.setContentsMargins(
self.Margins, self.Margins, self.Margins, self.Margins)
# Панель заголовка
self.titleBar = TitleBar(self)
layout.addWidget(self.titleBar)
# слот сигнала
self.titleBar.windowMinimumed.connect(self.showMinimized)
self.titleBar.windowMaximumed.connect(self.showMaximized)
self.titleBar.windowNormaled.connect(self.showNormal)
self.titleBar.windowClosed.connect(self.close)
self.titleBar.windowMoved.connect(self.move)
self.windowTitleChanged.connect(self.titleBar.setTitle)
self.windowIconChanged.connect(self.titleBar.setIcon)
def setTitleBarHeight(self, height=38):
""" Установка высоты строки заголовка """
self.titleBar.setHeight(height)
def setIconSize(self, size):
""" Установка размера значка """
self.titleBar.setIconSize(size)
def setWidget(self, widget):
""" Настройте свои собственные элементы управления """
if hasattr(self, '_widget'):
return
self._widget = widget
# Установите цвет фона по умолчанию, иначе он будет прозрачным из-за влияния родительского окна
self._widget.setAutoFillBackground(True)
palette = self._widget.palette()
palette.setColor(palette.Window, QColor(240, 240, 240))
self._widget.setPalette(palette)
self._widget.installEventFilter(self)
self.layout().addWidget(self._widget)
def move(self, pos):
if self.windowState() == Qt.WindowMaximized or self.windowState() == Qt.WindowFullScreen:
# Максимизировать или полноэкранный режим не допускается
return
super(FramelessWindow, self).move(pos)
def showMaximized(self):
""" Чтобы максимизировать, удалите верхнюю, нижнюю, левую и правую границы.
Если вы не удалите его, в пограничной области будут пробелы. """
super(FramelessWindow, self).showMaximized()
self.layout().setContentsMargins(0, 0, 0, 0)
def showNormal(self):
""" Восстановить, сохранить верхнюю и нижнюю левую и правую границы,
иначе нет границы, которую нельзя отрегулировать """
super(FramelessWindow, self).showNormal()
self.layout().setContentsMargins(
self.Margins, self.Margins, self.Margins, self.Margins)
def eventFilter(self, obj, event):
""" Фильтр событий, используемый для решения мыши в других элементах
управления и восстановления стандартного стиля мыши """
if isinstance(event, QEnterEvent):
self.setCursor(Qt.ArrowCursor)
return super(FramelessWindow, self).eventFilter(obj, event)
def paintEvent(self, event):
""" Поскольку это полностью прозрачное фоновое окно, жесткая для поиска
граница с прозрачностью 1 рисуется в событии перерисовывания, чтобы отрегулировать размер окна. """
super(FramelessWindow, self).paintEvent(event)
painter = QPainter(self)
painter.setPen(QPen(QColor(255, 255, 255, 1), 2 * self.Margins))
painter.drawRect(self.rect())
def mousePressEvent(self, event):
""" Событие клика мыши """
super(FramelessWindow, self).mousePressEvent(event)
if event.button() == Qt.LeftButton:
self._mpos = event.pos()
self._pressed = True
def mouseReleaseEvent(self, event):
''' Событие отказов мыши '''
super(FramelessWindow, self).mouseReleaseEvent(event)
self._pressed = False
self.Direction = None
def mouseMoveEvent(self, event):
""" Событие перемещения мыши """
super(FramelessWindow, self).mouseMoveEvent(event)
pos = event.pos()
xPos, yPos = pos.x(), pos.y()
wm, hm = self.width() - self.Margins, self.height() - self.Margins
if self.isMaximized() or self.isFullScreen():
self.Direction = None
self.setCursor(Qt.ArrowCursor)
return
if event.buttons() == Qt.LeftButton and self._pressed:
self._resizeWidget(pos)
return
if xPos <= self.Margins and yPos <= self.Margins:
# Верхний левый угол
self.Direction = LeftTop
self.setCursor(Qt.SizeFDiagCursor)
elif wm <= xPos <= self.width() and hm <= yPos <= self.height():
# Нижний правый угол
self.Direction = RightBottom
self.setCursor(Qt.SizeFDiagCursor)
elif wm <= xPos and yPos <= self.Margins:
# верхний правый угол
self.Direction = RightTop
self.setCursor(Qt.SizeBDiagCursor)
elif xPos <= self.Margins and hm <= yPos:
# Нижний левый угол
self.Direction = LeftBottom
self.setCursor(Qt.SizeBDiagCursor)
elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm:
# Влево
self.Direction = Left
self.setCursor(Qt.SizeHorCursor)
elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm:
# Право
self.Direction = Right
self.setCursor(Qt.SizeHorCursor)
elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins:
# выше
self.Direction = Top
self.setCursor(Qt.SizeVerCursor)
elif self.Margins <= xPos <= wm and hm <= yPos <= self.height():
# ниже
self.Direction = Bottom
self.setCursor(Qt.SizeVerCursor)
def _resizeWidget(self, pos):
""" Отрегулируйте размер окна """
if self.Direction == None:
return
mpos = pos - self._mpos
xPos, yPos = mpos.x(), mpos.y()
geometry = self.geometry()
x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height()
if self.Direction == LeftTop: # Верхний левый угол
if w - xPos > self.minimumWidth():
x += xPos
w -= xPos
if h - yPos > self.minimumHeight():
y += yPos
h -= yPos
elif self.Direction == RightBottom: # Нижний правый угол
if w + xPos > self.minimumWidth():
w += xPos
self._mpos = pos
if h + yPos > self.minimumHeight():
h += yPos
self._mpos = pos
elif self.Direction == RightTop: # верхний правый угол
if h - yPos > self.minimumHeight():
y += yPos
h -= yPos
if w + xPos > self.minimumWidth():
w += xPos
self._mpos.setX(pos.x())
elif self.Direction == LeftBottom: # Нижний левый угол
if w - xPos > self.minimumWidth():
x += xPos
w -= xPos
if h + yPos > self.minimumHeight():
h += yPos
self._mpos.setY(pos.y())
elif self.Direction == Left: # Влево
if w - xPos > self.minimumWidth():
x += xPos
w -= xPos
else:
return
elif self.Direction == Right: # Право
if w + xPos > self.minimumWidth():
w += xPos
self._mpos = pos
else:
return
elif self.Direction == Top: # выше
if h - yPos > self.minimumHeight():
y += yPos
h -= yPos
else:
return
elif self.Direction == Bottom: # ниже
if h + yPos > self.minimumHeight():
h += yPos
self._mpos = pos
else:
return
self.setGeometry(x, y, w, h)
The reason is that the named arguments to QVBoxLayout are not supported
layout = QVBoxLayout(self, spacing=0)
The property needs to be set by calling the setter.
layout = QVBoxLayout(self)
layout.setSpacing(0)
I am painting some squares and a circle on in a graphics scene and I would like to connect those squares with multiple lines that will start at the circle and branch out like a tree. When the user clicks the button in the lower left corner the lines should draw in the scene. Once the lines are drawn in the scene the user should be able to zoom into the lines and select the lines just like you can with the circle and squares.
The path is determined by a function called group. This function will take the points and make a graph structure for an outline of how the points are supposed to be connected. I know this can be better and I would like it to be better, but I didn't want to ask too many questions in this post. As an example, the output should look something like
{0: [1, 5, 6], 1: [7], 7: [12], 5: [4]}
This means point 0 is connected to points 1,5and 6 and point 1 is connected to point 7 and so on.
Then I have a class called PathLine that should set all of the attributes for changing colors when it is selected and whatever else I would like the user to be able to see in the future.
When the user clicks the button it will run a function in my Viewer class called drawConnectingLines this is where I wanted it to draw the lines. I can tell the function is running and I am not getting any errors, but nothing is showing up in the window.
I have tried adding QApplication.processEvents(), self.update(), and self.scene().update(), but nothing seems to make a difference. Also If I try to draw the lines on the load of the program it will not draw anything. Not even the points.
Any help is greatly appreciated.
rom PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt,cos,acos,asin,degrees,pi,hypot
class LogObject(QObject):
hovered = pyqtSignal()
notHovered = pyqtSignal()
def create_square():
scale = 250
path = QPainterPath()
path.addRect(-0.076,-0.07,0.1520,0.1400)
tr = QTransform()
tr.scale(scale, scale)
path = tr.map(path)
return path
def create_circle():
scale = 250
path = QPainterPath()
path.addEllipse(QPointF(0,0), 0.0750, 0.0750) # Using QPointF will center it
tr = QTransform()
tr.scale(scale, scale)
path = tr.map(path)
return path
def drawPath(x1,y1,x2,y2):
scale = 250
path = QPainterPath()
path.moveTo(x1,y1)
path.lineTo(x2,y2)
tr = QTransform()
tr.scale(scale, scale)
path = tr.map(path)
return path
class PathLine(QGraphicsPathItem):
def __init__(self,x1,y1,x2,y2):
super(PathLine,self).__init__()
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
# self.name = name
self.setFlag(QGraphicsItem.ItemIsSelectable,True)
self.setScale(1.5)
self.setPath(drawPath(x1,y1,x2,y2))
self.setAcceptHoverEvents(True)
self.log = LogObject()
self.setPos(x1, y1)
self.isSelected = False
def findLineWidth(self,zoomValue): # This function is for creating the line width value of all the drawn Objects
if zoomValue > 18:
zoomValue = 18
lineWidthF = -0.0000177256625115696*(zoomValue)**4 + 0.000440875172476041*(zoomValue)**3 + 0.00941580772740735*(zoomValue)**2 - 0.370069940941448*(zoomValue) + 3
self.updateLineWidth(lineWidthF)
def updateLineWidth(self,lineWidth):
pen = self.pen()
pen.setWidthF(lineWidth)
self.setPen(pen)
def itemChange(self, change, value):
if change == self.ItemSelectedChange:
color = QColor(Qt.green) if value else QColor(Qt.white)
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
return QGraphicsItem.itemChange(self, change, value)
def hoverEnterEvent(self, event):
color = QColor("red")
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
self.log.hovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
color = QColor(Qt.green) if self.isSelected else QColor(Qt.white)
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
self.log.notHovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
class Point(QGraphicsPathItem):
def __init__(self, x, y, r, name):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
if self.name.split('__')[1] == '0':
self.setPath(create_circle())
else:
self.setPath(create_square())
self.setScale(1.5)
self.x = x
self.y = y
self.r = r
self.setRotation(180+self.r)
self.setAcceptHoverEvents(True)
self.log = LogObject()
self.setPos(x, y)
self.isSelected = False
pen = QPen(Qt.white)
pen.setStyle(Qt.SolidLine)
pen.setWidthF(3)
self.setPen(pen)
def findLineWidth(self,zoomValue): # This function is for creating the line width value of all the drawn Objects
if zoomValue > 18:
zoomValue = 18
lineWidthF = -0.0000177256625115696*(zoomValue)**4 + 0.000440875172476041*(zoomValue)**3 + 0.00941580772740735*(zoomValue)**2 - 0.370069940941448*(zoomValue) + 3
self.updateLineWidth(lineWidthF)
def updateLineWidth(self,lineWidth):
pen = self.pen()
pen.setWidthF(lineWidth)
self.setPen(pen)
def itemChange(self, change, value):
if change == self.ItemSelectedChange:
color = QColor(Qt.green) if value else QColor(Qt.white)
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
return QGraphicsItem.itemChange(self, change, value)
def hoverEnterEvent(self, event):
color = QColor("red")
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
self.log.hovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
color = QColor(Qt.green) if self.isSelected else QColor(Qt.white)
pen = self.pen()
pen.setColor(color)
self.setPen(pen)
self.log.notHovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def mouseDoubleClickEvent(self,event):
print(self.name)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self.setRenderHints(QPainter.Antialiasing)
self._zoom = 0
self._empty = True
self.setScene(QGraphicsScene(self))
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self.viewport().setCursor(Qt.ArrowCursor)
QTimer.singleShot(0, self.reset_fit)
self.selectedItems = []
self.setBackgroundBrush(Qt.black)
def setItems(self):
self.data = {
"x": [
-2415594.9965,
-2414943.8686,
-2417160.6592,
# -2417160.6592,
-2417856.1783,
-2417054.7618,
-2416009.9966,
-2416012.5232,
# -2418160.8952,
-2418160.8952,
# -2416012.5232,
# -2417094.7694,
-2417094.7694,
],
"y": [
10453172.2426,
10454269.7008,
10454147.2672,
# 10454147.2672,
10453285.2456,
10452556.8132,
10453240.2808,
10455255.8752,
# 10455183.1912,
10455183.1912,
# 10455255.8752,
# 10456212.5959,
10456212.5959,
],
"rotation":[
0,
313.9962,
43.9962,
# 223.9962,
227.7070,
227.7070,
313.9962,
43.9962,
# 43.9962,
223.9962,
# 223.9962,
# 43.9962,
223.9962,
]
}
self.adjustedPoints = {}
for i, (x, y,r) in enumerate(zip(self.data["x"], self.data["y"],self.data["rotation"])):
p = Point(x, y,r, "Point__" + str(i))
p.log.hovered.connect(self.hoverChange)
p.log.notHovered.connect(self.notHoverChange)
self.scene().addItem(p)
self.adjustedPoints[i] = [x,y]
# if i == 0:
# self.adjustedPoints['c__'+str(i)] = [x,y]
# else:
# self.adjustedPoints['s__'+str(i)] = [x,y]
def drawConnectingLines(self):
# result = self.group(self.adjustedPoints, 'c__0')
result = self.group(self.adjustedPoints, 0)
for startPoint in result.items():
x1 = self.adjustedPoints[startPoint[0]][0]
y1 = self.adjustedPoints[startPoint[0]][1]
for endPoint in startPoint[1]:
x2 = self.adjustedPoints[endPoint][0]
y2 = self.adjustedPoints[endPoint][1]
connectingLine = PathLine(x1,y1,x2,y2)
# connectingLine.drawPath()
self.scene().addItem(connectingLine)
# QApplication.processEvents()
self.scene().update()
def findMinDistance(self,data, start):
xStart, yStart = data[start]
distances = []
for item,[x,y] in data.items():
if item != start and item != 0:
distances.append(hypot(abs(xStart - x),abs(yStart-y)))
output = self.mean(distances)-min(distances)
if output < min(distances):
output = min(distances)
return output
def mean(self,numbers):
return float(sum(numbers)) / max(len(numbers), 1)
def group(self,d, start,seen = []):
x, y = d[start]
r =[]
print(start)
dist = self.findMinDistance(d,start)
print(dist)
for a, [j, k] in d.items():
if a != start and a not in seen and hypot(abs(x-j), abs(y-k)) <= dist:
r.append(a)
if not r:
return {}
result = {start:r}
for i in r:
result.update(self.group(d, i, seen+[start, *r]))
return result
def setPoints(self):
self.setItems()
# self.drawConnectingLines()
self.setDragMode(self.ScrollHandDrag)
def wheelEvent(self, event):
for item in self.scene().items():
item.findLineWidth(self._zoom)
if event.angleDelta().y() > 0: # angleDelta is positive 120 zooming in and -120 going out
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.reset_fit()
else:
self._zoom = 0
def hoverChange(self):
self.viewport().setCursor(Qt.PointingHandCursor)
def notHoverChange(self):
self.viewport().setCursor(Qt.ArrowCursor)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
singleItem = self.itemAt(event.pos().x(), event.pos().y())
if singleItem != None:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
if singleItem.isSelected == True:
singleItem.setSelected(False)
singleItem.isSelected = False
self.selectedItems.remove(singleItem)
elif singleItem.isSelected == False:
singleItem.setSelected(True)
singleItem.isSelected = True
self.selectedItems.append(singleItem)
else:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(
QEvent.MouseButtonPress,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mousePressEvent(self, handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
point = event.pos()
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
if self.rubberBand.isVisible():
self.rubberBand.hide()
rect = self.rubberBand.geometry()
rect_scene = self.mapToScene(rect).boundingRect()
selected = self.scene().items(rect_scene)
if selected:
# print(selected)
for selectedPoints in selected:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
if selectedPoints.isSelected == True:
selectedPoints.setSelected(False)
selectedPoints.isSelected = False
self.selectedItems.remove(selectedPoints)
elif selectedPoints.isSelected == False: # if the shif key is not depressed and its not selected, then select it
selectedPoints.setSelected(True)
selectedPoints.isSelected = True
self.selectedItems.append(selectedPoints)
print( "".join("Item: %s\n" % child.name for child in self.selectedItems))
else:
print(" Nothing\n")
for selected in self.selectedItems:
selected.setSelected(False)
selected.isSelected = False
self.selectedItems.clear()
QGraphicsView.mouseReleaseEvent(self, event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ArrowCursor)
handmade_event = QMouseEvent(
QEvent.MouseButtonRelease,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mouseReleaseEvent(self, handmade_event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(
QRect(self.origin, event.pos()).normalized()
)
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self, event)
super(Viewer, self).mouseMoveEvent(event)
def reset_fit(self):
r = self.scene().itemsBoundingRect()
self.resetTransform()
self.setSceneRect(r)
self.fitInView(r, Qt.KeepAspectRatio)
self._zoom = 0
self.scale(1, -1)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnFindPath = QToolButton(self)
self.btnFindPath.setText("Draw Path")
self.btnFindPath.clicked.connect(self.autoDrawLines)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnFindPath)
VBlayout.addLayout(HBlayout)
def autoDrawLines(self):
self.viewer.drawConnectingLines()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
drawPath(x1,y1,x2,y2) effectively draws a line from scale*(x1, y1) to scale*(x2, y2) in item coordinates where scale = 250. In PathLine.__init__ you then move this line to (x1, y1) (in scene coordinates) and you scale it by a factor 1.5 which means that in scene coordinates the lines go from (1.5*scale+1)*(x1,y1) to 1.5*scale*(x2,y2)+(x1,y1).
However the circles and squares generated by create_circle and create_square are centered around (0,0) in item coordinates and moved to (x1,y1) in Point.__init__.
Therefore, to get the lines to match up with the circles and squares you should set scale to 1.0 in drawPath, keep the position of the line at (0,0) and its scale to 1.0 in PathLine.__init__. Also, you need to set an initial line with and line color to be able to see them, i.e.
def drawPath(x1,y1,x2,y2):
path = QPainterPath()
path.moveTo(x1,y1)
path.lineTo(x2,y2)
return path
class PathLine(QGraphicsPathItem):
def __init__(self,x1,y1,x2,y2):
super(PathLine,self).__init__()
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
# self.name = name
self.setFlag(QGraphicsItem.ItemIsSelectable,True)
self.setPath(drawPath(x1,y1,x2,y2))
self.setAcceptHoverEvents(True)
self.log = LogObject()
pen = QPen(Qt.white)
pen.setStyle(Qt.SolidLine)
pen.setWidthF(4)
self.setPen(pen)
self.isSelected = False
I have some code that separately seemed to work, but once they are combined I get an error saying RecursionError: maximum recursion depth exceeded while calling a Python object. My goal is the have the left mouse click draw a box around the points (Later will select these points that the box is around. Have not figured that part out yet), my wheel to zoom in and out, and my center button to pan around the view. I appreciate any help.
Code
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from math import sqrt
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(True)
self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
self.mousePressEvent(event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
self.mousePressEvent(handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
self.mouseReleaseEvent(event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
self.mouseReleaseEvent(handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
self.mouseMoveEvent(event)
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Load Points')
self.btnLoad.clicked.connect(self.loadPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
def loadPoints(self):
self.viewer.setPoints()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
your question is good for specifing the distinguish of self.mousePressEvent(event) and QGraphicsView.mousePressEvent(self,event)
1.
self.mousePressEvent(event)
This means that recurse the event on itself.
2.
QGraphicsView.mousePressEvent(event)
This means that it executes on the other , general QGraphicsView.mousePressEvent
On the occasion of 1.
Please remember,in the previous question, you made a handmade object of LeftButton in MidButton.If you made it and throw it into self.mousePressEvent(handmade_event),because handmade_event is actually interpretted as LeftButton,so it executes at the just above of if event.button() == Qt.LeftButton codes.So,eternal recursion happens.
Please try to write return under the LeftButton event.The error will be stopped instead of not manipulating them at all.
On the occasion of 2.
This is a more general.It is very popular and doesn't burst the recursion.
Because it is not the Viewer mousePressEvent.
mousePressEvent is installed by default in all widget objects.
But the execution is rather attachable.
In a word, you throw the event into the other event handler.
On the occasion of 1, you throw the event into the same event handler.
So, recursion happens.
My goal is the have the left mouse click draw a box around the points (Later will select these points that the box is around.
I updated at 13:19
If other problems happen,please ask me.
Result
All events are executed as the same thing.Only class Object and the type of event are probably the mark of distinguish.If we override it and write commands before it, we can regulate its execution in advance.So,the place you don't need to handle, you simply put the mousePressEvent at the last position.
(it is the same if the others.)
Please give it a try this code, it won't have errors.
Tips:QGraphicsView.mousePressEvent(self,event) and super(Viewer, self).mousePressEvent(event) is the same thing and you can delete either of them probably.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(True)
self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Load Points')
self.btnLoad.clicked.connect(self.loadPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
def loadPoints(self):
self.viewer.setPoints()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
I have some QGraphicItems that are being loaded in at multiple predetermined X and Y coordinates. I have a function that is called fitInView and when the user clicks a button to call that function the view is scaled just fine, but If I try to call that function during loading It does not scale how I would like. I believe it has something to do with how I am determining the scale factor. When I print out the values of the width and height of the viewport on load and when the user clicks the button, they are very different.
My question is how do I get the objects to be at the same scaled on load as they are when the user clicks the button. Thank you for your help.
Code
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class Point(QGraphicsItem):
def __init__(self, x, y):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.rectF = QRectF(0, 0, 30, 30)
self.x=x
self.y=y
self._brush = QBrush(Qt.black)
def setBrush(self, brush):
self._brush = brush
self.update()
def boundingRect(self):
return self.rectF
def paint(self, painter=None, style=None, widget=None):
painter.fillRect(self.rectF, self._brush)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsItem.hoverMoveEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self.fitInView()
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
for i,x in enumerate(self.data['x']):
x = self.data['x'][i]
y = self.data['y'][i]
p = Point(x,y)
p.setPos(x,y)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
print('viewrect.width: '+str(viewrect.width()))
print('scenerect.width: '+str(scenerect.width()))
print('viewrect.height: '+str(viewrect.height()))
print('scenerect.height: '+str(scenerect.height()))
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(self.ScrollHandDrag)
# self.fitInView()
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
#QGraphicsView.mousePressEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
QGraphicsView.mouseReleaseEvent(self,event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.OpenHandCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
super(Viewer, self).mouseMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Fit Into View')
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
self.viewer.fitInView()
def fitPoints(self):
self.viewer.fitInView()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
An instant before the QGraphicsView is displayed, internal elements are recalculated so that you observe a different behavior, so the solution is to apply the function a moment after it is displayed. for this there are at least 2 options:
1. QTimer.singleShot(0, ...):
# ...
self.setPoints()
QTimer.singleShot(0, self.fitInView)
# ...
1. override showEvent method of Viewer
def showEvent(self, event):
self.fitInView()
super(Viewer, self).showEvent(event)
I've search around online for a solution and most of them are in C++ and not complete answers. I was wondering if someone could help me find out how to remove the selection rectangle around a qgraphicsitem as seen in the image below?
import sys
from PySide.QtGui import *
from PySide.QtCore import *
import random
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
# self.setBackgroundBrush(QImage("C:/Users/jmartini/Desktop/Temp/images/flag_0140.jpg"))
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff )
self.setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff )
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._mousePressed = True
if self._isPanning:
self.setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
else:
super(MyGraphicsView, self).mousePressEvent(event)
elif event.button() == Qt.MiddleButton:
self._mousePressed = True
self._isPanning = True
self.setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
def mouseMoveEvent(self, event):
if self._mousePressed and self._isPanning:
newPos = event.pos()
diff = newPos - self._dragPos
self._dragPos = newPos
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
event.accept()
else:
super(MyGraphicsView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if self._isPanning:
self.setCursor(Qt.OpenHandCursor)
else:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
self._mousePressed = False
elif event.button() == Qt.MiddleButton:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
self._mousePressed = False
super(MyGraphicsView, self).mouseReleaseEvent(event)
def mouseDoubleClickEvent(self, event):
self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
pass
def keyPressEvent(self, event):
if event.key() == Qt.Key_Space and not self._mousePressed:
self._isPanning = True
self.setCursor(Qt.OpenHandCursor)
else:
super(MyGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Space:
if not self._mousePressed:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
else:
super(MyGraphicsView, self).keyPressEvent(event)
def wheelEvent(self, event):
# zoom factor
factor = 1.25
# Set Anchors
self.setTransformationAnchor(QGraphicsView.NoAnchor)
self.setResizeAnchor(QGraphicsView.NoAnchor)
# Save the scene pos
oldPos = self.mapToScene(event.pos())
# Zoom
if event.delta() < 0:
factor = 1.0 / factor
self.scale(factor, factor)
# Get the new position
newPos = self.mapToScene(event.pos())
# Move scene to old position
delta = newPos - oldPos
self.translate(delta.x(), delta.y())
class MyGraphicsScene(QGraphicsScene):
def __init__(self, parent):
super(MyGraphicsScene, self).__init__()
self.setBackgroundBrush(QBrush(QColor(50,50,50)))
# self.setSceneRect(50,50,0,0)
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.setWindowTitle("Test")
self.resize(800,600)
self.gv = MyGraphicsView()
self.gv.setScene(MyGraphicsScene(self))
self.setCentralWidget(self.gv)
self.gv.scene().selectionChanged.connect(self.selection_changed)
self.populate()
def populate(self):
scene = self.gv.scene()
for i in range(300):
x = random.randint(-1000, 1000)
y = random.randint(-1000, 1000)
r = random.randint(1, 50)
rect = scene.addEllipse(x, y, r, r,
QPen(QColor(255,128,0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin),
QBrush(QColor(255,128,20,128)))
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
rect = scene.addEllipse(10, 20, 20, 20,
QPen(QColor(255,128,0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin),
QBrush(QColor(255,0,0,128)))
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
points = QPolygonF()
points.append(QPointF(10.4, 10.5))
points.append(QPointF(40.2, 60.2))
points.append(QPointF(30.2, 90.2))
points.append(QPointF(10.2, 80.2))
poly = scene.addPolygon(points,
QPen(QColor(255,128,0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin),
QBrush(QColor(255,0,0,128)))
poly.setFlag( QGraphicsItem.ItemIsSelectable )
poly.setFlag( QGraphicsItem.ItemIsMovable )
def selection_changed(self):
selection = self.gv.scene().selectedItems()
print 'Selected:', len(selection)
for i in selection:
i.setPen(QPen(QColor(255,255,255), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
def main():
app = QApplication(sys.argv)
ex = MyMainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The default items will draw the boundingRect() when selected, the solution is to overwrite the paint(...) method:
class EllipseItem(QGraphicsEllipseItem):
def paint(self, painter, option, widget):
option.state &= ~QStyle.State_Selected
super(EllipseItem, self).paint(painter, option, widget)
class PolygonItem(QGraphicsPolygonItem):
def paint(self, painter, option, widget):
option.state &= ~QStyle.State_Selected
super(PolygonItem, self).paint(painter, option, widget)
So now you must use it in your code:
def populate(self):
scene = self.gv.scene()
for i in range(300):
x = random.randint(-1000, 1000)
y = random.randint(-1000, 1000)
r = random.randint(1, 50)
rect = EllipseItem(x, y, r, r)
rect.setPen(QPen(QColor(255,128,0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
rect.setBrush(QBrush(QColor(255,128,20,128)))
scene.addItem(rect)
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
rect = EllipseItem(10, 20, 20, 20)
rect.setPen(QPen(QColor(255,128,0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
rect.setBrush(QBrush(QColor(255,0,0,128)))
rect.setFlag( QGraphicsItem.ItemIsSelectable )
rect.setFlag( QGraphicsItem.ItemIsMovable )
scene.addItem(rect)
points = QPolygonF()
points.append(QPointF(10.4, 10.5))
points.append(QPointF(40.2, 60.2))
points.append(QPointF(30.2, 90.2))
points.append(QPointF(10.2, 80.2))
poly = PolygonItem(points)
poly.setPen(QPen(QColor(255,128,0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
poly.setBrush(QBrush(QColor(255,0,0,128)))
poly.setFlag( QGraphicsItem.ItemIsSelectable )
poly.setFlag( QGraphicsItem.ItemIsMovable )
scene.addItem(poly)