How to Animate an Image/Icon Widget - python

I have a custom 'loading' image which I need to be able to start/stop a rotation animation while some action is occurring. I'm looking for code that will show me how to do this but I can't find anything specific to rotation in PyQt. I already have the following code which causes the image to rotate at the click of a button:
import os
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class myApplication(QWidget):
def __init__(self, parent=None):
super(myApplication, self).__init__(parent)
self.imgPath = os.path.join('path/to/image','image.svg')
pixmap = QPixmap(self.imgPath)
diag = (pixmap.width()**2 + pixmap.height()**2)**0.5
self.label = QLabel()
self.label.setMinimumSize(diag, diag)
self.label.setAlignment(Qt.AlignCenter)
self.label.setPixmap(pixmap)
#---- Prepare a Layout ----
grid = QGridLayout()
button = QPushButton('Rotate 15 degrees')
button.clicked.connect(self.rotate_pixmap)
grid.addWidget(self.label, 0, 0)
grid.addWidget(button, 1, 0)
self.setLayout(grid)
self.rotation = 0
def rotate_pixmap(self):
pixmap = QPixmap(self.imgPath)
self.rotation += 15
transform = QTransform().rotate(self.rotation)
pixmap = pixmap.transformed(transform, Qt.SmoothTransformation)
#---- update label ----
self.label.setPixmap(pixmap)
if __name__ == '__main__':
app = QApplication(sys.argv)
instance = myApplication()
instance.show()
sys.exit(app.exec_())
I found this C++ code which shows how to create a rotation animation using QVariantAnimation and QTransform:
class RotateMe : public QLabel {
Q_OBJECT
public:
explicit RotateMe(QWidget* parent = Q_NULLPTR) :
QLabel(parent),
pixmap(100, 100),
animation(new QVariantAnimation )
{
resize(200, 200);
pixmap.fill(Qt::red);
animation->setDuration(10000);
animation->setStartValue(0.0f);
animation->setEndValue(90.0f);
connect(animation, &QVariantAnimation::valueChanged, [=](const QVariant &value){
qDebug()<<value;
QTransform t;
t.rotate(value.toReal());
setPixmap(pixmap.transformed(t));
});
animation->start();
}
private:
QPixmap pixmap;
QVariantAnimation *animation;
};
How can I re-write this in python? I can't find any examples which show how QVariantAnimation is used in PyQT.

The logic is similar as I show below:
import os
from PyQt5 import QtCore, QtGui, QtWidgets
class RotateMe(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super(RotateMe, self).__init__(*args, **kwargs)
self._pixmap = QtGui.QPixmap()
self._animation = QtCore.QVariantAnimation(
self,
startValue=0.0,
endValue=360.0,
duration=1000,
valueChanged=self.on_valueChanged
)
def set_pixmap(self, pixmap):
self._pixmap = pixmap
self.setPixmap(self._pixmap)
def start_animation(self):
if self._animation.state() != QtCore.QAbstractAnimation.Running:
self._animation.start()
#QtCore.pyqtSlot(QtCore.QVariant)
def on_valueChanged(self, value):
t = QtGui.QTransform()
t.rotate(value)
self.setPixmap(self._pixmap.transformed(t))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
label = RotateMe(alignment=QtCore.Qt.AlignCenter)
img_path = os.path.join('path/to/image','image.svg')
label.set_pixmap(QtGui.QPixmap(img_path))
button = QtWidgets.QPushButton('Rotate')
button.clicked.connect(label.start_animation)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(label)
lay.addWidget(button)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Related

Pyqt5 - Zoom on a QLabel

I'm trying to apply a zoom example that I've found on this site but it uses a QGraphicsScene and a QGraphicsView while I should use a simple QLabel. This is the code but it does not work. Can I zoom on a Qlabel or is it impossible?
The zoom should work with ctrl++ / ctrl+- shortcut.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QLabel
class GuiZoom(QtWidgets.QMainWindow):
factor = 1.5
def __init__(self, parent=None):
super(GuiZoom, self).__init__(parent)
self.setFixedSize(800, 600)
self.lblZoom = QLabel("Facciamo lo zoom")
self.lblZoom.setStyleSheet("border:10px solid green")
self.setCentralWidget(self.lblZoom)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomIn),
self.lblZoom,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_in,
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomOut),
self.lblZoom,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_out,
)
def zoom_in(self):
scale_tr = QtGui.QTransform()
scale_tr.scale(GuiZoom.factor, GuiZoom.factor)
tr = self.lblZoom.transform() * scale_tr
self.lblZoom.setTransform(tr)
def zoom_out(self):
scale_tr = QtGui.QTransform()
scale_tr.scale(GuiZoom.factor, GuiZoom.factor)
scale_inverted, invertible = scale_tr.inverted()
if invertible:
tr = self.lblZoom.transform() * scale_inverted
self.lblZoom.setTransform(tr)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = GuiZoom()
ui.show()
sys.exit(app.exec_())
Moreover can you explain me what the last lines of zoom_out functions do? Is this a way to invert the zoom?
One possible solution is to use a QGraphicsEffect to scale the QLabel's painting.
Note: The size of the widget is not changed but the painting is scaled.
from PyQt5 import QtCore, QtGui, QtWidgets
class ZoomEffect(QtWidgets.QGraphicsEffect):
factor = 1.5
_scale = 1.0
#property
def scale(self):
return self._scale
#scale.setter
def scale(self, scale):
self._scale = scale
self.update()
def draw(self, painter):
painter.setRenderHints(
QtGui.QPainter.Antialiasing
| QtGui.QPainter.SmoothPixmapTransform
| QtGui.QPainter.HighQualityAntialiasing
)
center = self.sourceBoundingRect(QtCore.Qt.DeviceCoordinates).center()
pixmap, offset = self.sourcePixmap(QtCore.Qt.DeviceCoordinates)
painter.setWorldTransform(QtGui.QTransform())
painter.translate(center)
painter.scale(self.scale, self.scale)
painter.translate(-center)
painter.drawPixmap(offset, pixmap)
def zoom_in(self):
self.scale *= self.factor
def zoom_out(self):
self.scale /= self.factor
class GuiZoom(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(GuiZoom, self).__init__(parent)
self.setFixedSize(800, 600)
self.lblZoom = QtWidgets.QLabel("Facciamo lo zoom")
self.lblZoom.setStyleSheet("border:10px solid green")
self.zoom_effect = ZoomEffect()
self.lblZoom.setGraphicsEffect(self.zoom_effect)
self.setCentralWidget(self.lblZoom)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomIn),
self.lblZoom,
context=QtCore.Qt.WindowShortcut,
activated=self.zoom_effect.zoom_in,
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomOut),
self.lblZoom,
context=QtCore.Qt.WindowShortcut,
activated=self.zoom_effect.zoom_out,
)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = GuiZoom()
ui.show()
sys.exit(app.exec_())

Embed QGraphics in widget

I have a main widget inside a window who contains a lot of widgets. How can I insert a QGraphics view and a QGraphicsScene in that widget? I have not found a direct insertion method, so I am trying using a wrapper, in this case a box layout but it is not a good solution. The QGraphicsScene stands out from the layout limits.
Code:
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedSize(1500, 1015)
widget_central = QtWidgets.QWidget(self)
wrapper = QtWidgets.QHBoxLayout(widget_central)
scene = QtWidgets.QGraphicsScene(wrapper)
vista = QtWidgets.QGraphicsView(scene)
wrapper.addWidget(vista)
self.diedrico = Diedrico() # This is a class who draw things, not relevant
self.diedrico.setFixedSize(2000, 2000)
scene.addWidget(self.diedrico)
self.setCentralWidget(widget_central)
I would like to get this result:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QPen, QColor
import sys
class Diedrico(QWidget):
def __init__(self, parent):
super().__init__(parent)
def paintEvent(self, event):
qp = QPainter(self)
qp.setPen(QPen(QColor(Qt.black), 5))
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(520, 520)
self.widget_central = QtWidgets.QWidget(self)
scrol = QtWidgets.QScrollArea(self.widget_central)
scrol.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scrol.setGeometry(QtCore.QRect(30, 30, 500, 500))
scrol.setWidgetResizable(False)
contenido = QtWidgets.QWidget()
contenido.setGeometry(QtCore.QRect(0, 0, 2000, 2000))
scrol.setWidget(contenido)
self.Diedrico = Diedrico(contenido)
self.Diedrico.setGeometry(QtCore.QRect(0, 0, 2000, 2000))
self.setCentralWidget(self.widget_central)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
But using QGraphics instead of a scroll area
The QGraphicsProxyWidget that is created using the widget takes into account the minimum size of the widget to set the boundingRect, and the QGraphicsScene uses the boundingRect to set the initial scene rect.
from PyQt5 import QtCore, QtGui, QtWidgets
class Diedrico(QtWidgets.QWidget):
def paintEvent(self, event):
qp = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black), 5)
qp.setPen(pen)
qp.drawRect(500, 500, 1000, 1000)
def minimumSizeHint(self):
return QtCore.QSize(2000, 2000)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(520, 520)
widget_central = QtWidgets.QWidget()
self.setCentralWidget(widget_central)
lay = QtWidgets.QVBoxLayout(widget_central)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
diedrico = Diedrico()
scene.addWidget(diedrico)
lay.addWidget(view)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())

Zoom in and out in widget

How can I make a zoom effect with key input on a widget? The widget is inside a scroll area and there are some drawings made with QPainter who change with user input. The zoom would affect the length of the scrolling bar, the closer you are, the smaller the bar becomes. The zoom at minimum level should make the scroll bar as big as the widget area, so all the content in the widget could be visualized.
MRE:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QPen, QColor
import sys
class Diedrico(QWidget):
def __init__(self, parent):
super().__init__(parent)
def paintEvent(self, event):
qp = QPainter(self)
qp.setPen(QPen(QColor(Qt.black), 5))
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(520, 520)
self.widget_central = QtWidgets.QWidget(self)
scrol = QtWidgets.QScrollArea(self.widget_central)
scrol.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scrol.setGeometry(QtCore.QRect(10, 10, 500, 500))
scrol.setWidgetResizable(False)
contenido = QtWidgets.QWidget()
contenido.setGeometry(QtCore.QRect(0, 0, 2000, 2000))
scrol.setWidget(contenido)
self.Diedrico = Diedrico(contenido)
self.Diedrico.setGeometry(QtCore.QRect(0, 0, 2000, 2000))
self.setCentralWidget(self.widget_central)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
Why do you want to reinvent the wheel? Instead of wanting to implement the logic of the scaling feature, use the classes that already do it. In this case, a good option is to use QGraphicsView with QGraphicsScene:
Note: The shortcut standard Zoom In and Zoom Out are associated with Ctrl + + and Ctrl + -, respectively.
from PyQt5 import QtCore, QtGui, QtWidgets
class Diedrico(QtWidgets.QWidget):
def paintEvent(self, event):
qp = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black), 5)
qp.setPen(pen)
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
factor = 1.5
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self._scene = QtWidgets.QGraphicsScene(self)
self._view = QtWidgets.QGraphicsView(self._scene)
self._diedrico = Diedrico()
self._diedrico.setFixedSize(2000, 2000)
self._scene.addWidget(self._diedrico)
self.setCentralWidget(self._view)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomIn),
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_in,
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.ZoomOut),
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_out,
)
#QtCore.pyqtSlot()
def zoom_in(self):
scale_tr = QtGui.QTransform()
scale_tr.scale(UiVentana.factor, UiVentana.factor)
tr = self._view.transform() * scale_tr
self._view.setTransform(tr)
#QtCore.pyqtSlot()
def zoom_out(self):
scale_tr = QtGui.QTransform()
scale_tr.scale(UiVentana.factor, UiVentana.factor)
scale_inverted, invertible = scale_tr.inverted()
if invertible:
tr = self._view.transform() * scale_inverted
self._view.setTransform(tr)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
Update:
If you want to use + and - for ZoomIn and ZoomOut, respectively, then just change the shortcuts to:
QtWidgets.QShortcut(
QtGui.QKeySequence(QtCore.Qt.Key_Plus), # <---
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_in,
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtCore.Qt.Key_Minus), # <---
self._view,
context=QtCore.Qt.WidgetShortcut,
activated=self.zoom_out,
)

setting background image for a widget in another class

I've been recently learning pyqt5 as my first gui framework. So far I have been experimenting with QtStackedLayout. I currently have two window screens, one created inside the UI class and another in another separate class. I have two concerns:
Everything was working until I started experimenting on adding a background image for Window 1. There is no image displayed but the code runs ok.
There is this small fraction of time in the beginning where window one will get displayed first until it gets loaded to the mainwindow, I tried passing self during instantiation of the object to sert as some kind of parent to prevent this but I think I'm not doing it right.
See below code (I have bad import statements, I will sort it out)
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Ui(QWidget):
def setupUi(self, Main, width, height):
self.stack = QStackedLayout()
self.window_1 = WindowOne(width, height)
self.window_2 = QWidget(self)
self.window_2_UI()
self.stack.addWidget(self.window_1)
self.stack.addWidget(self.window_2)
# Only one button
self.btn = QPushButton("Change window", self)
# Create the central widget of your Main Window
self.main_widget = QWidget(self)
layout = QVBoxLayout(self.main_widget)
layout.addLayout(self.stack)
layout.addWidget(self.btn)
self.setCentralWidget(self.main_widget)
self.btn.clicked.connect(self.change_window)
def change_window(self):
if self.stack.currentIndex() == 0:
self.stack.setCurrentIndex(1)
else:
self.stack.setCurrentIndex(0)
def window_2_UI(self):
label = QLabel("In Window 2", self.window_2)
class WindowOne(QWidget):
def __init__(self, width, height):
super().__init__()
self.set_bg(width, height)
self.set_label()
# self.setStyleSheet("background-image: url(:resource/images/blue_bg.jpg)")
def set_label(self):
label = QLabel("In Window 1", self)
def set_bg(self, w, h):
oImage = QImage("resource/images/blue_bg.jpg")
sImage = oImage.scaled(QSize(w, h))
palette = QPalette()
palette.setBrush(10, QBrush(sImage))
self.setPalette(palette)
class Main(QMainWindow, Ui):
def __init__(self):
super().__init__()
self.w_width = 480
self.w_height = 720
self.resize(self.w_width, self.w_height)
self.init_ui()
self.setupUi(self, self.w_width, self.w_height)
def init_ui(self):
self.center()
self.setWindowTitle('Main Window')
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
if __name__ == "__main__":
app = QApplication(sys.argv)
M = Main()
M.show()
sys.exit(app.exec())
By default only the window (which is different to a widget) will use the background color of QPalette, if you want a widget to use the background color of QPalette you must enable the autoFillBackground property.
# ...
self.set_label(width, height)
self.setAutoFillBackground(True)
# ...
Although your code is a little messy, for example you establish that certain methods receive certain parameters but you never use them. Finally I think you want the background of the image to be re-scale using the size of the window so I have override the resizeEvent() method so that scaling takes the size of the window.
Considering all the above, I have improved your code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_stacked_layout = QtWidgets.QStackedLayout()
self.widget_1 = WidgetOne()
self.widget_2 = QtWidgets.QWidget()
self.widget_2_UI()
self.m_stacked_layout.addWidget(self.widget_1)
self.m_stacked_layout.addWidget(self.widget_2)
button = QtWidgets.QPushButton(
"Change window", clicked=self.change_window
)
lay = QtWidgets.QVBoxLayout(self)
lay.addLayout(self.m_stacked_layout)
lay.addWidget(button)
#QtCore.pyqtSlot()
def change_window(self):
ix = self.m_stacked_layout.currentIndex()
self.m_stacked_layout.setCurrentIndex(1 if ix == 0 else 0)
def widget_2_UI(self):
label = QtWidgets.QLabel("In Window 2", self.widget_2)
class WidgetOne(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setAutoFillBackground(True)
self.set_label()
self.m_image = QtGui.QImage("resource/images/blue_bg.jpg")
def set_label(self):
label = QtWidgets.QLabel("In Window 1", self)
def resizeEvent(self, event):
palette = self.palette()
sImage = self.m_image.scaled(event.size())
palette.setBrush(10, QtGui.QBrush(sImage))
self.setPalette(palette)
super(WidgetOne, self).resizeEvent(event)
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
widget = Widget()
self.setCentralWidget(widget)
self.resize(480, 720)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec())

Move QLabel in absolute position with mouse move

I want to move the QLabel with the mouse movement (not like Drag&drop, 'object' disappears while moving). Clicked - moved - released. I did it to some extent, but I ran into a problem. QLabel shrinks as I move it or even disappears (like shrinks to 0 width). How to fix it or what more correct approach to do it?
(self.label_pos is needed to keep the mouse position relative inside self.label)
Or its just monitor's refresh rate issue? But in photoshop's gradient editor, that little color stop isn't shrikns. It's choppy because of refresh rate, but always the same size.
This is what I want to see, recorded using a screen capture program. The same thing I see in Photoshop
This is what I see, recorded on my phone. The quality is poor, but the difference is clearly visible anyway.
This Photoshop is also captured on my phone, here the “object” remains the same size, as in the example made using screen capture
Here is code from eyllanesc's answer, 'object' still shrinks :(
self.label = QLabel(self)
self.label.move(100, 100)
self.label.mousePressEvent = self.mouse_on
self.label.mouseReleaseEvent = self.mouse_off
def mouse_on(self, event):
self.bool = True
self.label_pos = event.pos()
def mouse_off(self, event):
self.bool = False
def mouseMoveEvent(self, event):
if self.bool:
self.label.move(event.x()-self.label_pos.x(), event.y()-self.label_pos.y())
Instead of using a QLabel I recommend using QGraphicsRectItem with a QGraphicsView since it is specialized in this type of tasks:
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
brush = QtWidgets.QApplication.palette().brush(QtGui.QPalette.Window)
self.setBackgroundBrush(brush)
rect_item = self.scene().addRect(
QtCore.QRectF(QtCore.QPointF(), QtCore.QSizeF(40, 80))
)
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
rect_item.setBrush(QtGui.QBrush(QtGui.QColor("red")))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.setFixedSize(640, 480)
w.show()
sys.exit(app.exec_())
If you want to just scroll horizontally then overwrite the itemChange method of QGraphicsItem:
from PyQt5 import QtCore, QtGui, QtWidgets
class HorizontalItem(QtWidgets.QGraphicsRectItem):
def __init__(self, rect, parent=None):
super(HorizontalItem, self).__init__(rect, parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
def itemChange(self, change, value):
if (
change == QtWidgets.QGraphicsItem.ItemPositionChange
and self.scene()
):
return QtCore.QPointF(value.x(), self.pos().y())
return super(HorizontalItem, self).itemChange(change, value)
class Widget(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
brush = QtWidgets.QApplication.palette().brush(QtGui.QPalette.Window)
self.setBackgroundBrush(brush)
rect_item = HorizontalItem(
QtCore.QRectF(QtCore.QPointF(), QtCore.QSizeF(40, 80))
)
rect_item.setBrush(QtGui.QBrush(QtGui.QColor("red")))
self.scene().addItem(rect_item)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.setFixedSize(640, 480)
w.show()
sys.exit(app.exec_())
In the following code there is an example similar to what you want:
from PyQt5 import QtCore, QtGui, QtWidgets
class HorizontalItem(QtWidgets.QGraphicsRectItem):
def __init__(self, rect, parent=None):
super(HorizontalItem, self).__init__(rect, parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
def itemChange(self, change, value):
if (
change == QtWidgets.QGraphicsItem.ItemPositionChange
and self.scene()
):
return QtCore.QPointF(value.x(), self.pos().y())
return super(HorizontalItem, self).itemChange(change, value)
class Widget(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
brush = QtWidgets.QApplication.palette().brush(QtGui.QPalette.Window)
self.setBackgroundBrush(brush)
self.setFixedSize(640, 480)
size = self.mapToScene(self.viewport().rect()).boundingRect().size()
r = QtCore.QRectF(QtCore.QPointF(), size)
self.setSceneRect(r)
rect = QtCore.QRectF(
QtCore.QPointF(), QtCore.QSizeF(0.8 * r.width(), 80)
)
rect.moveCenter(r.center())
rect_item = self.scene().addRect(rect)
rect_item.setBrush(QtGui.QBrush(QtGui.QColor("salmon")))
item = HorizontalItem(
QtCore.QRectF(
rect.bottomLeft() + QtCore.QPointF(0, 20), QtCore.QSizeF(20, 40)
)
)
item.setBrush(QtGui.QBrush(QtGui.QColor("red")))
self.scene().addItem(item)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Categories