Adjust GraphicView according to any image geometry - python

My QGraphicsView should show an image of a large resolution. The size should fit inside a resizable window. Currently, the image is viewed in a way that I want it to but only by providing some manually adjusted values to the initial view geometry. This doe not look neat. I also tried to refer to the solutions posted here: Graphics View and Pixmap Size
My current Window looks like this:
class ImageCheck(Ui_ImageCheck.Ui_MainWindow, QMainWindow):
def __init__(self, parent=None):
super(ImageCheck, self).__init__()
self.setupUi(self)
self.setWindowTitle("Image Analyzer")
self.crop_ratio_w = 1
self.crop_ratio_h = 1
self.path = None
self.scene = QGraphicsScene()
self.scene.clear()
self.image_item = QGraphicsPixmapItem()
# This is the approximate shift in coordinates of my initial view from the window
self.view.setGeometry(self.geometry().x()+ 10, self.geometry().y()+ 39,
self.geometry().width()- 55, self.geometry().height()- 110)
self.view.setAlignment(Qt.AlignCenter)
self.view.setFrameShape(QFrame.NoFrame)
def setImage(self, path):
self.path = path
self.crop_ratio_w = self.pixmap.width() / self.view.width()
self.crop_ratio_h = self.pixmap.height() / self.view.height()
pixmap = QPixmap(path)
smaller_pixmap = pixmap.scaled(self.view.width(), self.view.height(),
Qt.IgnoreAspectRatio, t.FastTransformation)
self.image_item.setPixmap(smaller_pixmap)
self.scene.addItem(self.image_item)
self.scene.setSceneRect(0, 0, self.view.width(), self.view.height())
self.view.setGeometry(0, 0, self.view.width(), self.view.height())
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.view.setScene(self.scene)
self.view.setSceneSize()
def resizeEvent(self, event):
self.view.setGeometry(self.geometry().x()+ 10, self.geometry().y()+ 39,
self.geometry().width()- 55, self.geometry().height()- 110)
self.setImage(self.path)
My manual override was probably not a good idea when I tried to determine distances between two points. Even the scaled distance gives me a slightly wrong value.

I can not use your code because there are many hidden things so I will propose the next solution that is to rescale the view based on the scene each time the window changes its size. I have also implemented a signal that transports the clicked information in the image based on the coordinates of the image.
from PyQt5 import QtCore, QtGui, QtWidgets
class ClickableGraphicsView(QtWidgets.QGraphicsView):
clicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent=None):
super(ClickableGraphicsView, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
self.pixmap_item = None
def setImage(self, path):
pixmap = QtGui.QPixmap(path)
self.pixmap_item = self.scene().addPixmap(pixmap)
self.pixmap_item.setShapeMode(
QtWidgets.QGraphicsPixmapItem.BoundingRectShape
)
def mousePressEvent(self, event):
if self.pixmap_item is not None:
if self.pixmap_item == self.itemAt(event.pos()):
sp = self.mapToScene(event.pos())
lp = self.pixmap_item.mapToItem(self.pixmap_item, sp)
p = lp.toPoint()
if self.pixmap_item.pixmap().rect().contains(p):
self.clicked.emit(p)
super(ClickableGraphicsView, self).mousePressEvent(event)
def resizeEvent(self, event):
self.fitInView(self.sceneRect(), QtCore.Qt.IgnoreAspectRatio)
super(ClickableGraphicsView, self).resizeEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("Image Analyzer")
view = ClickableGraphicsView()
view.clicked.connect(print)
view.setImage("image.jpg")
label = QtWidgets.QLabel("Distance")
display = QtWidgets.QLCDNumber()
buttonbox = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
)
widget = QtWidgets.QWidget()
self.setCentralWidget(widget)
lay = QtWidgets.QGridLayout(widget)
lay.addWidget(view, 0, 0, 1, 2)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(label)
hlay.addWidget(display)
hlay.addStretch()
lay.addLayout(hlay, 1, 0)
lay.addWidget(buttonbox, 1, 1)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Related

How to resize square children widgets after parent resize in Qt5?

I want to do board with square widgets. When I run code it creates nice board but after resize it become looks ugly. I am trying resize it with resize Event but it exists (probably some errors). I have no idea how to resize children after resize of parent.
Children widgets must be squares so it is also problem since I can not use auto expand. Maybe it is simple problem but I can not find solution. I spend hours testing different ideas but it now works as it should.
This what I want resize (click maximize):
After maximize it looks ugly (I should change children widget but on what event (I think on resizeEvent but it is not works) and how (set from parent or children cause program exit).
This is my minimize code:
import logging
import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QFont, QPaintEvent, QPainter
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout
class Application(QApplication):
pass
class Board(QWidget):
def square_size(self):
size = self.size()
min_size = min(size.height(), size.width())
min_size_1_8 = min_size // 8
square_size = QSize(min_size_1_8, min_size_1_8)
logging.debug(square_size)
return square_size
def __init__(self, parent=None):
super().__init__(parent=parent)
square_size = self.square_size()
grid = QGridLayout()
grid.setSpacing(0)
squares = []
for x in range(8):
for y in range(8):
square = Square(self, (x + y - 1) % 2)
squares.append(squares)
square.setFixedSize(square_size)
grid.addWidget(square, x, y)
self.squares = squares
self.setLayout(grid)
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
# how to resize children?
logging.debug('Resize %s.', self.__class__.__name__)
logging.debug('Size %s.', event.size())
super().resizeEvent(event)
class Square(QWidget):
def __init__(self, parent, color):
super().__init__(parent=parent)
if color:
self.color = QtCore.Qt.white
else:
self.color = QtCore.Qt.black
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
logging.debug('Resize %s.', self.__class__.__name__)
logging.debug('Size %s.', event.size())
super().resizeEvent(event)
def paintEvent(self, event: QPaintEvent) -> None:
painter = QPainter()
painter.begin(self)
painter.fillRect(self.rect(), self.color)
painter.end()
def main():
logging.basicConfig(level=logging.DEBUG)
app = Application(sys.argv)
app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
default_font = QFont()
default_font.setPointSize(12)
app.setFont(default_font)
board = Board()
board.setWindowTitle('Board')
# ugly look
# chessboard.showMaximized()
# looks nize but resize not works
board.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
How should I do resize of square children to avoid holes?
2nd try - improved code but still I have not idea how to resize children
Some new idea with centering it works better (no gaps now) but still I do not know how to resize children (without crash).
After show():
Too wide (it keeps proportions):
Too tall (it keeps proportions):
Larger (it keeps proportions but children is not scaled to free space - I do not know how to resize children still?):
Improved code:
import logging
import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QFont, QPaintEvent, QPainter
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QHBoxLayout, QVBoxLayout
class Application(QApplication):
pass
class Board(QWidget):
def square_size(self):
size = self.size()
min_size = min(size.height(), size.width())
min_size_1_8 = min_size // 8
square_size = QSize(min_size_1_8, min_size_1_8)
logging.debug(square_size)
return square_size
def __init__(self, parent=None):
super().__init__(parent=parent)
square_size = self.square_size()
vertical = QVBoxLayout()
horizontal = QHBoxLayout()
grid = QGridLayout()
grid.setSpacing(0)
squares = []
for x in range(8):
for y in range(8):
square = Square(self, (x + y - 1) % 2)
squares.append(squares)
square.setFixedSize(square_size)
grid.addWidget(square, x, y)
self.squares = squares
horizontal.addStretch()
horizontal.addLayout(grid)
horizontal.addStretch()
vertical.addStretch()
vertical.addLayout(horizontal)
vertical.addStretch()
self.setLayout(vertical)
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
# how to resize children?
logging.debug('Resize %s.', self.__class__.__name__)
logging.debug('Size %s.', event.size())
super().resizeEvent(event)
class Square(QWidget):
def __init__(self, parent, color):
super().__init__(parent=parent)
if color:
self.color = QtCore.Qt.white
else:
self.color = QtCore.Qt.black
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
logging.debug('Resize %s.', self.__class__.__name__)
logging.debug('Size %s.', event.size())
super().resizeEvent(event)
def paintEvent(self, event: QPaintEvent) -> None:
painter = QPainter()
painter.begin(self)
painter.fillRect(self.rect(), self.color)
painter.end()
def main():
logging.basicConfig(level=logging.DEBUG)
app = Application(sys.argv)
app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
default_font = QFont()
default_font.setPointSize(12)
app.setFont(default_font)
board = Board()
board.setWindowTitle('Board')
# ugly look
# chessboard.showMaximized()
# looks nice but resize not works
board.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
How should I resize square children without crash?
There are two possible solution.
You can use the Graphics View framework, which is intended exactly for this kind of applications where custom/specific graphics and positioning have to be taken into account, otherwise create a layout subclass.
While reimplementing a layout is slightly simple in this case, you might face some issues as soon as the application becomes more complex. On the other hand, the Graphics View framework has a steep learning curve, as you'll need to understand how it works and how object interaction behaves.
Subclass the layout
Assuming that the square count is always the same, you can reimplement your own layout that will set the correct geometry based on its contents.
In this example I also created a "container" with other widgets to show the resizing in action.
When the window width is very high, it will use the height as a reference and center it horizontally:
On the contrary, when the height is bigger, it will be centered vertically:
Keep in mind that you should not add other widgets to the board, otherwise you'll get into serious issues.
This would not be impossible, but its implementation might be much more complex, as the layout would need to take into account the other widgets positions, size hints and possible expanding directions in order to correctly compute the new geometry.
from PyQt5 import QtCore, QtGui, QtWidgets
class Square(QtWidgets.QWidget):
def __init__(self, parent, color):
super().__init__(parent=parent)
if color:
self.color = QtCore.Qt.white
else:
self.color = QtCore.Qt.black
self.setMinimumSize(50, 50)
def paintEvent(self, event: QtGui.QPaintEvent) -> None:
painter = QtGui.QPainter(self)
painter.fillRect(self.rect(), self.color)
class EvenLayout(QtWidgets.QGridLayout):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSpacing(0)
def setGeometry(self, oldRect):
# assuming that the minimum size is 50 pixel, find the minimum possible
# "extent" based on the geometry provided
minSize = max(50 * 8, min(oldRect.width(), oldRect.height()))
# create a new squared rectangle based on that size
newRect = QtCore.QRect(0, 0, minSize, minSize)
# move it to the center of the old one
newRect.moveCenter(oldRect.center())
super().setGeometry(newRect)
class Board(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
layout = EvenLayout(self)
self.squares = []
for row in range(8):
for column in range(8):
square = Square(self, not (row + column) & 1)
self.squares.append(square)
layout.addWidget(square, row, column)
class Chess(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
header = QtWidgets.QLabel('Some {}long label'.format('very ' * 20))
layout.addWidget(header, 0, 0, 1, 3, QtCore.Qt.AlignCenter)
self.board = Board()
layout.addWidget(self.board, 1, 1)
leftLayout = QtWidgets.QVBoxLayout()
layout.addLayout(leftLayout, 1, 0)
rightLayout = QtWidgets.QVBoxLayout()
layout.addLayout(rightLayout, 1, 2)
for b in range(1, 9):
leftLayout.addWidget(QtWidgets.QPushButton('Left Btn {}'.format(b)))
rightLayout.addWidget(QtWidgets.QPushButton('Right Btn {}'.format(b)))
footer = QtWidgets.QLabel('Another {}long label'.format('very ' * 18))
layout.addWidget(footer, 2, 0, 1, 3, QtCore.Qt.AlignCenter)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Chess()
w.show()
sys.exit(app.exec_())
Using the Graphics View
The result will be visually identical to the previous one, but while the overall positioning, drawing and interaction would be conceptually a bit easier, understanding how Graphics Views, Scenes and objects work might require you some time to get the hang of it.
from PyQt5 import QtCore, QtGui, QtWidgets
class Square(QtWidgets.QGraphicsWidget):
def __init__(self, color):
super().__init__()
if color:
self.color = QtCore.Qt.white
else:
self.color = QtCore.Qt.black
def paint(self, qp, option, widget):
qp.fillRect(option.rect, self.color)
class Scene(QtWidgets.QGraphicsScene):
def __init__(self):
super().__init__()
self.container = QtWidgets.QGraphicsWidget()
layout = QtWidgets.QGraphicsGridLayout(self.container)
layout.setSpacing(0)
self.container.setContentsMargins(0, 0, 0, 0)
layout.setContentsMargins(0, 0, 0, 0)
self.addItem(self.container)
for row in range(8):
for column in range(8):
square = Square(not (row + column) & 1)
layout.addItem(square, row, column, 1, 1)
class Board(QtWidgets.QGraphicsView):
def __init__(self):
super().__init__()
scene = Scene()
self.setScene(scene)
self.setAlignment(QtCore.Qt.AlignCenter)
# by default a graphics view has a border frame, disable it
self.setFrameShape(0)
# make it transparent
self.setStyleSheet('QGraphicsView {background: transparent;}')
def resizeEvent(self, event):
super().resizeEvent(event)
# zoom the contents keeping the ratio
self.fitInView(self.scene().container, QtCore.Qt.KeepAspectRatio)
class Chess(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
header = QtWidgets.QLabel('Some {}long label'.format('very ' * 20))
layout.addWidget(header, 0, 0, 1, 3, QtCore.Qt.AlignCenter)
self.board = Board()
layout.addWidget(self.board, 1, 1)
leftLayout = QtWidgets.QVBoxLayout()
layout.addLayout(leftLayout, 1, 0)
rightLayout = QtWidgets.QVBoxLayout()
layout.addLayout(rightLayout, 1, 2)
for b in range(1, 9):
leftLayout.addWidget(QtWidgets.QPushButton('Left Btn {}'.format(b)))
rightLayout.addWidget(QtWidgets.QPushButton('Right Btn {}'.format(b)))
footer = QtWidgets.QLabel('Another {}long label'.format('very ' * 18))
layout.addWidget(footer, 2, 0, 1, 3, QtCore.Qt.AlignCenter)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Chess()
w.show()
sys.exit(app.exec_())

How to add notification number to a button icon?

I am trying to make a GUI with PyQt5. It will have a notification button with an icon. I want to add a small bubble with the number of notifications on the icon.
If a number is not possible, I would like to use a red dot as a backup method.
But how should I keep track of the new notifications (like a listener for notification) and change the icon while the window is running?
I have been googling about this problem, but only mobile development stuff and non-PyQt5 related results come up.
Expected result: Let's say we have a list. And the icon of the button will automatically change when a new item is added to the list. Then when the button is clicked, the icon will change back.
A possible solution is to create a widget that has a layout where you place a QToolButton and at the top right a QLabel with a QPixmap that has the number
from PyQt5 import QtCore, QtGui, QtWidgets
def create_pixmap(point, radius=64):
rect = QtCore.QRect(QtCore.QPoint(), 2 * radius * QtCore.QSize(1, 1))
pixmap = QtGui.QPixmap(rect.size())
rect.adjust(1, 1, -1, -1)
pixmap.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(pixmap)
painter.setRenderHints(
QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing
)
pen = painter.pen()
painter.setPen(QtCore.Qt.NoPen)
gradient = QtGui.QLinearGradient()
gradient.setColorAt(1, QtGui.QColor("#FD6684"))
gradient.setColorAt(0, QtGui.QColor("#E0253F"))
gradient.setStart(0, rect.height())
gradient.setFinalStop(0, 0)
painter.setBrush(QtGui.QBrush(gradient))
painter.drawEllipse(rect)
painter.setPen(pen)
painter.drawText(rect, QtCore.Qt.AlignCenter, str(point))
painter.end()
return pixmap
class NotificationButton(QtWidgets.QWidget):
scoreChanged = QtCore.pyqtSignal(int)
def __init__(self, score=0, icon=QtGui.QIcon(), radius=12, parent=None):
super(NotificationButton, self).__init__(parent)
self.m_score = score
self.m_radius = radius
self.setContentsMargins(0, self.m_radius, self.m_radius, 0)
self.m_button = QtWidgets.QToolButton(clicked=self.clear)
self.m_button.setContentsMargins(0, 0, 0, 0)
self.m_button.setIcon(icon)
self.m_button.setIconSize(QtCore.QSize(18, 18))
lay = QtWidgets.QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(self.m_button)
self.m_label = QtWidgets.QLabel(self)
self.m_label.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
self.m_label.raise_()
self.setSizePolicy(self.m_button.sizePolicy())
self.update_notification()
#QtCore.pyqtProperty(int, notify=scoreChanged)
def score(self):
return self.m_score
#score.setter
def score(self, score):
if self.m_score != score:
self.m_score = score
self.update_notification()
self.scoreChanged.emit(score)
#QtCore.pyqtSlot()
def clear(self):
self.score = 0
#QtCore.pyqtProperty(int)
def radius(self):
return self.m_radius
#radius.setter
def radius(self, radius):
self.m_radius = radius
self.update_notification()
def update_notification(self):
self.setContentsMargins(0, self.m_radius, self.m_radius, 0)
self.m_label.setPixmap(create_pixmap(self.m_score, self.m_radius))
self.m_label.adjustSize()
def resizeEvent(self, event):
self.m_label.move(self.width() - self.m_label.width(), 0)
super(NotificationButton, self).resizeEvent(event)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_item_le = QtWidgets.QLineEdit("Stack Overflow")
add_button = QtWidgets.QPushButton("Add", clicked=self.add_item)
self.m_notification_button = NotificationButton(
icon=QtGui.QIcon("image.png")
)
self.m_list_widget = QtWidgets.QListWidget()
vlay = QtWidgets.QVBoxLayout(self)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(self.m_item_le)
hlay.addWidget(add_button)
vlay.addLayout(hlay)
vlay.addWidget(
self.m_notification_button, alignment=QtCore.Qt.AlignRight
)
vlay.addWidget(self.m_list_widget)
#QtCore.pyqtSlot()
def add_item(self):
text = self.m_item_le.text()
self.m_list_widget.addItem(
"%s: %s" % (self.m_list_widget.count(), text)
)
self.m_notification_button.score += 1
self.m_list_widget.scrollToBottom()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
It would be nice if you show your code so far. Anyhow, these may help you solve your question:
You'll need two different icons: one to represent a dirty (just loaded) list and the other for the "clean" list
class YourClass(Dialog):
def __init__(self)
super().__init__()
self.lst = []
# ...
def setUI(self):
# ...
self.notButton = QPushButton(icon_off, '0')
self.notButton.clicked.connect(self.clearButton)
# ...
#pyqtSlot()
def clearButton(self):
self.notButton.setIcon(icon_clean)
def addToList(self, item):
self.lst.append(item)
self.notButton.setIcon(icon_dirty)
self.notButton.setText(str(len(self.lst)
A possible solution to updating the icon would be to have a separate image file for each icon and its associated notification number. You can keep track of the number of current notifications in a counter variable. Use that number to call the corresponding icon.

how to increase the row height and column width of the tablewidget

I want to add images to cells but it cant show properly,can you please tell me how to increase the row height and column width of the table widget.
Here given bellow is my code:
from PyQt4 import QtGui
import sys
imagePath = "pr.png"
class ImgWidget1(QtGui.QLabel):
def __init__(self, parent=None):
super(ImgWidget1, self).__init__(parent)
pic = QtGui.QPixmap(imagePath)
self.setPixmap(pic)
class ImgWidget2(QtGui.QWidget):
def __init__(self, parent=None):
super(ImgWidget2, self).__init__(parent)
self.pic = QtGui.QPixmap(imagePath)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(0, 0, self.pic)
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
tableWidget = QtGui.QTableWidget(10, 2, self)
# tableWidget.horizontalHeader().setStretchLastSection(True)
tableWidget.resizeColumnsToContents()
# tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# tableWidget.setFixedWidth(tableWidget.columnWidth(0) + tableWidget.columnWidth(1))
tableWidget.resize(400,600)
tableWidget.setCellWidget(0, 1, ImgWidget1(self))
tableWidget.setCellWidget(1, 1, ImgWidget2(self))
if __name__ == "__main__":
app = QtGui.QApplication([])
wnd = Widget()
wnd.show()
sys.exit(app.exec_())
When using widgets inside the QTableWidget are not really the content of the table, they are placed on top of it, so resizeColumnsToContents() makes the size of the cells very small since it does not take into account the size of those widgets, resizeColumnsToContents() takes into account the content generated by the QTableWidgetItem.
On the other hand if you want to set the height and width of the cells you must use the headers, in the following example the default size is set using setDefaultSectionSize():
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
tableWidget = QtGui.QTableWidget(10, 2)
vh = tableWidget.verticalHeader()
vh.setDefaultSectionSize(100)
# vh.setResizeMode(QtGui.QHeaderView.Fixed)
hh = tableWidget.horizontalHeader()
hh.setDefaultSectionSize(100)
# hh.setResizeMode(QtGui.QHeaderView.Fixed)
tableWidget.setCellWidget(0, 1, ImgWidget1())
tableWidget.setCellWidget(1, 1, ImgWidget2())
lay = QtGui.QVBoxLayout(self)
lay.addWidget(tableWidget)
If you want the size can not be varied by the user then uncomment the lines.

QGraphics Item Creates a delay in painting

I tried to create a 2D plot using QGraphicsItem, I was successful in doing that but when I drag the QGraphicsItem there is a delay and the view is distorted.
Searching for a solution, I came across this QGraphicsItem paint delay. I applied the mouseMoveEvent to my QGraphicsView but it did not resolve the problem.
Could someone tell me what is causing the problem and how can I fix it?
Here is my code:
from PyQt4 import QtGui, QtCore
import sys
import numpy as np
class MyGraphicsItem(QtGui.QGraphicsItem):
def __init__(self,dataX,dataY):
super(MyGraphicsItem,self).__init__()
self.Xval = dataX
self.Yval = dataY
self.Xvalmin = np.min(self.Xval)
self.Xvalmax = np.max(self.Xval)
self.Yvalmin = np.min(self.Yval)
self.Yvalmax = np.max(self.Yval)
self.rect = QtCore.QRectF(0,0,100,2)
self.points = []
self._picture = None
def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None):
if self._picture is None:
self._picture = QtGui.QPicture()
QPainter.begin(self._picture)
startPoint = QtCore.QPointF(0, 0)
cubicPath = QtGui.QPainterPath()
cubicPath.moveTo(startPoint)
for i in range(len(self.points) - 2):
points_ = self.points[i:i+3]
cubicPath.cubicTo(*points_)
QPainter.setPen(QtGui.QPen(QtCore.Qt.red))
QPainter.drawLine(0,10,100,10)
QPainter.drawLine(0,-10,0,10)
QPainter.setPen(QtGui.QPen(QtCore.Qt.black))
QPainter.drawPath(cubicPath)
QPainter.end()
else:
self._picture.play(QPainter)
def boundingRect(self):
return self.rect
class mygraphicsview(QtGui.QGraphicsView):
def __init__(self):
super(mygraphicsview,self).__init__()
def mouseMoveEvent(self, event):
QtGui.QGraphicsView.mouseMoveEvent(self,event)
if self.scene().selectedItems():
self.update()
class Mainwindow(QtGui.QMainWindow):
def __init__(self):
super(Mainwindow,self).__init__()
self.main_widget = QtGui.QWidget()
self.vl = QtGui.QVBoxLayout()
self.scene = QtGui.QGraphicsScene()
self.view = mygraphicsview()
self.Xval = np.linspace(0,100,1000)
self.Yval = np.sin(self.Xval)
self.painter = QtGui.QPainter()
self.style = QtGui.QStyleOptionGraphicsItem()
self.item = MyGraphicsItem(self.Xval, self.Yval)
self.item.paint(self.painter, self.style,self.main_widget)
self.item.setFlag(QtGui.QGraphicsItem.ItemIsMovable,True)
self.trans = QtGui.QTransform()
self.trans.scale(5,5)
self.item.setTransform(self.trans)
self.scene = QtGui.QGraphicsScene()
self.scene.addItem(self.item)
self.view.setScene(self.scene)
self.vl.addWidget(self.view)
self.main_widget.setLayout(self.vl)
self.setCentralWidget(self.main_widget)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Mainwindow()
window.show()
sys.exit(app.exec_())
I fixed the issue of dragging delay.
The reason for the occurrence of such a delay is due to the boundinRect() function. The boudingRect was too tight around the item designed.
Adding some marigin to the boundingRect(), solved the problem.
self.rect = QtCore.QRectF(-10,-10,120,25)

remove all QGraphicsLineItem in QGraphicsScene.group from a QGraphicsScene

I'm trying to draw vector segments on top of a QGraphicsPixmapItem, i used the mouse events to draw a first "red-dash-line" to positioning the segment. When the mouse is released the segment vertex are stored in a list wich is then appended to an other list. The metalist containing all the segments vertex is then drawn with a green-solid-line.
Once the user finished to draw segments i'm looking for a way to remove the segments from the scene and start over.
I'm not able to find a way to clean-up the scene removing the segments.
An ideal way will be to have each segment listed with a proper segment.identifier and then connect the "clear" push button to a self.scene.removeItem(segment.identifier) to remove it.
#!/usr/bin/env python
import sys
from PyQt4 import QtCore, QtGui
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.scene = QtGui.QGraphicsScene()
self.view = QtGui.QGraphicsView(self.scene)
layout = QtGui.QVBoxLayout()
layout.addWidget(self.view)
self.setLayout(layout)
self.bt = QtGui.QPushButton("Clear lines")
layout.addWidget(self.bt)
self.pixmap_item = QtGui.QGraphicsPixmapItem(QtGui.QPixmap('image.png'), None, self.scene)
self.pixmap_item.mousePressEvent = self.startLine
self.pixmap_item.mouseMoveEvent = self.mouseMoveEvent
self.pixmap_item.mouseReleaseEvent = self.updateProfile
self.startPoint = QtCore.QPointF()
self.profiles = []
self.bt.clicked.connect(self.clean)
self.pp = []
def clean(self):
#self.myItemGroup = self.scene.createItemGroup([])
self.myItemGroup.hide
print(dir(self.myItemGroup))
def startLine(self, event):
pen = QtGui.QPen(QtCore.Qt.red, 2, QtCore.Qt.DashDotLine)
self.sline = QtGui.QGraphicsLineItem(QtCore.QLineF(0,0,0,0))
self.sline.setPen(pen)
self.scene.addItem(self.sline)
print self.profiles
if (QtCore.Qt.LeftButton):
self.startPoint = QtCore.QPointF(event.pos())
def updateProfile(self, event):
self.profiles.append([self.startPoint.x(),self.startPoint.y(), event.pos().x(), event.pos().y()])
#print self.profiles
items = []
pen = QtGui.QPen(QtCore.Qt.green, 2, QtCore.Qt.SolidLine)
for i in self.profiles:
self.pline = QtGui.QGraphicsLineItem(QtCore.QLineF(i[0],i[1],i[2],i[3]))
self.pline.setPen(pen)
#self.scene.addItem(self.pline)
#self.pline.setGroup(self.myItemGroup)
items.append(self.pline)
self.myItemGroup = self.scene.createItemGroup(items)
self.lastPoint = self.startPoint
self.startPoint = QtCore.QPointF(self.profiles[-1][-2],self.profiles[-1][-1])
self.scene.removeItem(self.sline)
print self.startPoint, self.lastPoint
def mouseMoveEvent(self, event):
self.sline.setLine(self.startPoint.x(),self.startPoint.y(), event.pos().x(), event.pos().y())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
widget = MainWidget()
widget.resize(640, 480)
widget.show()
sys.exit(app.exec_())

Categories