Overriden QLabel Item black image problem - python

I have a custom QLabel item that allows users to track mouse points, draw rectangles on the pixmap. Also, I have a video stream provided by another class that inherits QThread and use OpenCV. My aim is to display the video stream on this custom QLabel. A user can pause the stream and display the last frame on the QLabel to draw rectangles.
However, the paintEvent in my QLabel item causes the black image. How can I display a video stream on my QLabel item and pause on a frame to draw rectangles?
My QLabel class:
class ImageLabel(QLabel):
def __init__(self, width=720, height=540):
super(ImageLabel, self).__init__()
self.setMouseTracking(True)
#### initialize coordinates ####
self.x1 = 0
self.y1 = 0
self.x_curr = 0
self.y_curr = 0
self.x2 = 0
self.y2 = 0
######
self.enable_labelling = False
self.enable_cor = False
################################
self.source = Communicate()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.drawPixmap(self.rect(), self.pixmap)
if self.enable_labelling == True:
self.paintRect(event, qp)
qp.end()
def paintRect(self, event, qp):
br = QtGui.QBrush(QtGui.QColor(50, 255, 255, 40))
qp.setBrush(br)
if self.enable_cor == True:
qp.drawRect(QtCore.QRect(QPoint(self.x1, self.y1), QSize(self.x_curr - self.x1, self.y_curr - self.y1)))
else:
qp.drawRect(QtCore.QRect(QPoint(self.x1, self.y1), QSize(self.x2 - self.x1, self.y2 - self.y1)))
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.enable_labelling == True:
self.x1 = event.x()
self.y1 = event.y()
self.enable_cor = True
self.source.cor_update.emit()
def mouseMoveEvent(self, event):
self.x_curr = event.x()
self.y_curr = event.y()
self.source.cor_curr.emit()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.enable_labelling == True:
self.x2 = event.x()
self.y2 = event.y()
self.source.cor_update.emit()
self.enable_cor = False
Main class:
class MainWindowDesign(QWidget):
def __init__(self, parent, cam_id):
super(MainWindowDesign, self).__init__(parent)
self.parent = parent
self.cam_id = cam_id
self.init_UI()
def init_UI(self):
self.image_lbl = ImageLabel()
self.image_lbl.setStyleSheet(ss.style.image_box)
self.image_lbl.setFixedSize(self.image_width, self.image_height)
th = Stream_Thread()
th.set_index(self.cam_id)
th.changePixmap.connect(self.setImage)
th.start()
#pyqtSlot(QImage)
def setImage(self, image):
self.image_lbl.setPixmap(QPixmap.fromImage(image).scaled(720, 540))
Stream class:
-based on https://stackoverflow.com/a/44404713/13080899
class Stream_Thread(QThread):
changePixmap = pyqtSignal(QImage)
def __init__(self):
super(Stream_Thread, self).__init__()
self.ref = False #refresh flag
def set_index(self, index = 0):
self.index = int(index)
def refresh(self):
self.ref = True
self.capt.open(self.index)
def run(self):
self.capt = cv2.VideoCapture(self.index, cv2.CAP_DSHOW)
while(True):
ret, frame = self.capt.read()
if ret:
rbgImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rbgImage.shape
bytesPerLine = ch*w
convertToQtFormat = QImage(rbgImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
EDIT:
I created a reproducible code. Here it is:
Main class:
import sys
from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton, QHBoxLayout
from ImageLabel import ImageLabel
from Stream_Thread import Stream_Thread
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
self.image_lbl = ImageLabel()
th = Stream_Thread()
th.set_index(1)
th.changePixmap.connect(self.setImage, Qt.QueuedConnection)
th.start()
btn_cnt = QPushButton("Continue")
btn_pa = QPushButton("Pause")
hbox = QHBoxLayout()
hbox.addWidget(btn_cnt)
hbox.addWidget(btn_pa)
vbox = QVBoxLayout()
vbox.addWidget(self.image_lbl)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.show()
#pyqtSlot(QImage)
def setImage(self, image):
self.image_lbl.setPixmap(QPixmap.fromImage(image).scaled(720, 540))
def main():
app = QApplication(sys.argv)
main_form = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Stream_Thread class:
import cv2
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtGui import QImage
#https://stackoverflow.com/a/44404713/13080899
class Stream_Thread(QThread):
changePixmap = pyqtSignal(QImage)
def __init__(self):
super(Stream_Thread, self).__init__()
self.ref = False #refresh flag
def set_index(self, index = 0):
self.index = int(index)
def refresh(self):
self.ref = True
self.capt.open(self.index)
def run(self):
self.capt = cv2.VideoCapture(self.index, cv2.CAP_DSHOW)
while(True):
ret, frame = self.capt.read()
if ret:
rbgImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rbgImage.shape
bytesPerLine = ch*w
convertToQtFormat = QImage(rbgImage.data, w, h, bytesPerLine, QImage.Format_RGB888)
p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio)
self.changePixmap.emit(p)
ImageLabel class:
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import Qt, QSize, pyqtSignal, QObject, QPoint, pyqtSlot
from PyQt5.QtWidgets import QLabel
class Communicate(QObject):
cor_update = pyqtSignal()
cor_curr = pyqtSignal()
class ImageLabel(QLabel):
def __init__(self, width=720, height=540):
super(ImageLabel, self).__init__()
self.setMouseTracking(True)
self.pixmap = QtGui.QPixmap("img/logo.jpg")
#### initialize coordinates ####
self.x1 = 0
self.y1 = 0
self.x_curr = 0
self.y_curr = 0
self.x2 = 0
self.y2 = 0
##### enable state to allow user for drawing
self.enable_labelling = False
##### enable state to track coordinates for drawing
self.enable_cor = False
################################
self.source = Communicate()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.drawPixmap(self.rect(), self.pixmap)
if self.enable_labelling == True:
self.paintRect(event, qp)
qp.end()
def paintRect(self, event, qp):
br = QtGui.QBrush(QtGui.QColor(50, 255, 255, 40))
qp.setBrush(br)
if self.enable_cor == True:
qp.drawRect(QtCore.QRect(QPoint(self.x1, self.y1), QSize(self.x_curr - self.x1, self.y_curr - self.y1)))
else:
qp.drawRect(QtCore.QRect(QPoint(self.x1, self.y1), QSize(self.x2 - self.x1, self.y2 - self.y1)))
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.enable_labelling == True:
self.x1 = event.x()
self.y1 = event.y()
self.enable_cor = True
self.source.cor_update.emit()
def mouseMoveEvent(self, event):
self.x_curr = event.x()
self.y_curr = event.y()
self.source.cor_curr.emit()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.enable_labelling == True:
self.x2 = event.x()
self.y2 = event.y()
self.source.cor_update.emit()
self.enable_cor = False

Related

Differences in pyside6 and pyqt5

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)

QPainter delete previously drawn shapes

I am trying to code a simple image editor like paint.
I implemented drawing lines rectangles and ellipses.
what i want is to see an animation (a foreshadowing ?) of how the rectangle will look like, just like in paint when you draw a shape you can see what it actually looks like without really drawing on the canvas.
here is a shortened version of the code
class Canvas(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.initLogic()
def initUI(self):
self.image = QImage(self.size(), QImage.Format_RGB32)
self.image.fill(Qt.white)
def initLogic(self):
self.brushSize = 1
self.brushStyle = Qt.SolidLine
self.brushColor = QColor(0, 0, 0)
self.shapeMode = None
self.drawing = False
self.mousePointer = None
def mousePressEvent(self, event):
self.drawing = True
self.mousePointer = event.pos()
def mouseMoveEvent(self, event):
#if no pen mode set draw lines from event to event
if self.drawing:
painter = QPainter(self.image)
painter.setPen(QPen(self.brushColor,
self.brushSize,
self.brushStyle))
shape = None #i try later to assign the method Qpainter.draw<someShape> to this variable
# hoping it works like in tkinter.
if self.shapeMode == None:#free shape
painter.drawLine(self.mousePointer, event.pos())
self.mousePointer = event.pos()
else:
#previous x and previous y
ox, oy = self.mousePointer.x(), self.mousePointer.y()
#current x and current y
dx, dy = event.pos().x(), event.pos().y()
width, height = dx - ox, dy - oy
#self.shapeMode is a string corresponding to a QPainter method
#we get the corresponding method using getattr builtin function
drawMethod = getattr(painter, self.shapeMode)# = painter.someFunc this works fine
shape = drawMethod(ox, oy, width, height) #assigning the method call to a variable
self.update()
if shape != None:
painter.eraseRect(shape)
"""
if self.drawing and self.shapeMode == None:
painter = QPainter(self.image)
painter.setPen(QPen(self.brushColor,
self.brushSize,
self.brushStyle))
painter.drawLine(self.mousePointer, event.pos())
self.mousePointer = event.pos()
self.update()"""
#otherwise if pen mode set draw shape at event then delete until release
def mouseReleaseEvent(self, event):
if self.shapeMode != None:
painter = QPainter(self.image)
painter.setPen(QPen(self.brushColor,
self.brushSize,
self.brushStyle))
#previous x and previous y
ox, oy = self.mousePointer.x(), self.mousePointer.y()
#current x and current y
dx, dy = event.pos().x(), event.pos().y()
width, height = dx - ox, dy - oy
#self.shapeMode is a string corresponding to a QPainter methid
#we get the corresponding method using getattr builtin function
drawMethod = getattr(painter, self.shapeMode)# = painter.someFunc
shape = drawMethod(ox, oy, width, height)
self.update()
self.mousePointer = event.pos()
self.drawing = False
#TODO end registering the action
def paintEvent(self, event):
widgetPainter = QPainter(self)
widgetPainter.drawImage(self.rect(), self.image, self.rect())
the canvas keeps drawing rectangles as long as i hold the mouse, what i want is the rectangle to resize and only definitively be drawn after mouse release.
You can achieve this by taking advantage of which paint device to pass to QPainter. During mouseMoveEvent keep a reference to the points and sizes calculated so in paintEvent you can draw onto the main widget. This way anything painted will only last until the next update. Then in mouseReleaseEvent you can paint on the QImage to permanently draw the rectangle.
class Canvas(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.initLogic()
def initUI(self):
self.image = QImage(self.size(), QImage.Format_RGB32)
self.image.fill(Qt.white)
def initLogic(self):
self.brushSize = 1
self.brushStyle = Qt.SolidLine
self.brushColor = QColor(0, 0, 0)
self.shapeMode = 'drawRect'
self.temp_rect = QRect()
self.drawing = False
self.mousePointer = None
def mousePressEvent(self, event):
self.drawing = True
self.mousePointer = event.pos()
def mouseMoveEvent(self, event):
#if no pen mode set draw lines from event to event
if self.drawing:
painter = QPainter(self.image)
painter.setPen(QPen(self.brushColor,
self.brushSize,
self.brushStyle))
if self.shapeMode == None:#free shape
painter.drawLine(self.mousePointer, event.pos())
self.mousePointer = event.pos()
else:
#previous x and previous y
ox, oy = self.mousePointer.x(), self.mousePointer.y()
#current x and current y
dx, dy = event.pos().x(), event.pos().y()
width, height = dx - ox, dy - oy
self.temp_rect = QRect(ox, oy, width, height)
self.update()
def mouseReleaseEvent(self, event):
if self.shapeMode != None:
painter = QPainter(self.image)
painter.setPen(QPen(self.brushColor,
self.brushSize,
self.brushStyle))
#self.shapeMode is a string corresponding to a QPainter methid
#we get the corresponding method using getattr builtin function
drawMethod = getattr(painter, self.shapeMode)# = painter.someFunc
drawMethod(self.temp_rect)
self.update()
self.mousePointer = event.pos()
self.drawing = False
#TODO end registering the action
def paintEvent(self, event):
widgetPainter = QPainter(self)
widgetPainter.drawImage(self.rect(), self.image, self.rect())
if self.drawing:
drawMethod = getattr(widgetPainter, self.shapeMode)
drawMethod(self.temp_rect)
Outcome:

PYQT5 Graphic Area Not Updating When Button Clicked

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

Pyqt5 QgraphicsView pan past scroll bar limits

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_())

Remove Dotted Selection around QGraphicsItem

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)

Categories

Resources