Right now, I am learning QGraphicsView. I followed a tutorial given by Nokia from 2011 (YouTube: Qt DevDays 2011, Advanced Qt -A Deep Dive 1/6). It is a very basic demo showing a house (made from QRect and QPolygon) that turns around if clicked. On the backside of the house are a few widgets (e.g. QCombobox) aranged inside a QGraphicsProxyWidget hold by a QGraphicsItemGroup. Now I am facing the problem that since Qt 4.7 the class QGraphicItemsGroup no longer supports the flag "setHandlesChildEvents(False)".
Since QGraphicsItemGroup will handle all the events, how can I achieve that the QGraphicsItem Group will not block the child item's event, as well as letting child item handle it own event?
Thanks in advance for your help!
main_house.py
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsView
from PyQt5.QtCore import Qt
import sys
from House.house import House
app = QApplication([])
scene = QGraphicsScene()
house_1 = House(Qt.blue, Qt.yellow)
scene.addItem(house_1)
scene.setSceneRect(0, 0, 500, 500)
view = QGraphicsView(scene)
view.show()
view.resize(600, 600)
sys.exit(app.exec_())
house.py
from PyQt5.QtWidgets import QGraphicsItemGroup,QGraphicsProxyWidget,QGraphicsRectItem,QGraphicsPolygonItem, \
QWidget, QVBoxLayout, QLabel, QComboBox, QPushButton
from PyQt5.QtCore import QTimeLine, QPointF, QRectF, Qt
from PyQt5.QtGui import QColor, QPolygonF, QTransform
class House(QGraphicsItemGroup):
def __init__(self, roofcolor: QColor, facadecolor: QColor, parent=None):
super(House, self).__init__(parent)
self._front = True
self._timeline = None
self._graphics = None
self._configpage = None
self._facade = None
self._roof = None
self.roof_polygon = QPolygonF()
self.roof_polygon << QPointF(0, 200) << QPointF(100, 0) << QPointF(200, 200)
self.body_rect = QRectF(0, 200, 200, 200)
self.create_roof(roofcolor)
self.create_front(facadecolor)
self.create_back(roofcolor, facadecolor)
# ==========================
# self.setHandlesChildEvents(False) # obsolete since Qt 4.7
# ==========================
def mousePressEvent(self, event):
if not self._timeline:
self._timeline = QTimeLine(1000)
self._timeline.setCurveShape(QTimeLine.EaseInOutCurve)
self._timeline.valueChanged.connect(self.rotate_house)
self._timeline.finished.connect(self.reset)
self._timeline.start()
def reset(self):
self._timeline = None
def rotate_house(self, pos):
angle = int(pos * 180)
if self._front is True:
angle += 180
transform = QTransform()
transform.translate(100, 0)
transform.rotate(angle, Qt.YAxis)
transform.translate(-100, 0)
self.setTransform(transform)
if pos == 1.0:
self._front = not self._front
config = angle < 90 or angle >= 270
self._configpage.setVisible(config)
self._facade.setVisible(not config)
def update_roof_color(self):
combo = self.sender()
color = combo.itemData(combo.currentIndex()).value()
self._roof.setBrush(color)
def update_house_color(self):
combo = self.sender()
color = combo.itemData(combo.currentIndex()).value()
self._facade.setBrush(color)
def create_roof(self, color: QColor):
self._roof = QGraphicsPolygonItem(self.roof_polygon)
self.addToGroup(self._roof)
self._roof.setBrush(color)
def create_front(self, color: QColor):
self._facade = QGraphicsRectItem(self.body_rect)
self._facade.setBrush(color)
self.addToGroup(self._facade)
def create_back(self, roof_color: QColor, facade_color: QColor):
self._configpage = QGraphicsProxyWidget()
self._configpage.setWidget(self.create_config_widget(roof_color, facade_color))
self._configpage.setGeometry(self.body_rect)
self.addToGroup(self._configpage)
self._configpage.hide()
# ==== Problem: Widgets (comboboxes) do not receive events ====
def create_config_widget(self, roof_color: QColor, facade_color: QColor) -> QWidget:
res = QWidget()
layout = QVBoxLayout(res)
label = QLabel('Roof color:')
layout.addWidget(label)
self.roof_combo = self.create_color_combobox(roof_color) # combobox does not receive events
layout.addWidget(self.roof_combo)
self.roof_combo.activated.connect(self.update_roof_color)
label = QLabel('House color:')
layout.addWidget(label)
self.facade_combo = self.create_color_combobox(facade_color)
layout.addWidget(self.facade_combo)
self.facade_combo.activated.connect(self.update_house_color)
bt = QPushButton('Test')
bt.setCheckable(True)
layout.addWidget(bt)
layout.addStretch(1)
return res
def create_color_combobox(self, color: QColor) -> QComboBox:
res = QComboBox()
res.addItem('red', QColor(Qt.red))
res.addItem('blue', QColor(Qt.blue))
res.addItem('green', QColor(Qt.green))
res.addItem('white', QColor(Qt.white))
res.setCurrentIndex(res.findData(color))
return res
Related
I am writing a little program, but the buttons don't work
as I expected.
I wish the text to change state when I click on the button of that text.
Here is the code to test it:
from sys import exit
from sys import argv
from PyQt6.QtWidgets import QApplication, QHBoxLayout, QPushButton, QVBoxLayout, QLabel, QWidget, QLineEdit
class MainWindow(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.screenWidth = 1920
self.screenHeight = 1080
self.windowWidth = 1000
self.windowHeight = 800
self.setWindowTitle("test 19")
self.setGeometry((self.screenWidth - self.windowWidth) // 2, (self.screenHeight - self.windowHeight) // 2, self.windowWidth, self.windowHeight)
self.initUi()
def initUi(self) -> None:
mainLayout = QVBoxLayout()
headLayout = QHBoxLayout()
nameLabel = QLabel("Name")
headLayout.addWidget(nameLabel)
mainLayout.addItem(headLayout)
row1 = Row("google.com")
mainLayout.addItem(row1.returnValues())
row2 = Row("yahoo.com")
mainLayout.addItem(row2.returnValues())
for i in range(20):
rowi = Row(f"{i}")
mainLayout.addItem(rowi.returnValues())
self.setLayout(mainLayout)
class Row():
def __init__(self, name) -> None:
super().__init__()
self.rowLayout = QHBoxLayout()
self.nameLineEdit = QLineEdit(f"{name}")
self.nameLineEdit.setDisabled(True)
self.rowLayout.addWidget(self.nameLineEdit)
self.hiddenOrShowButton = QPushButton("")
self.hiddenOrShowButton.clicked.connect(self.hiddenOrShow)
self.rowLayout.addWidget(self.hiddenOrShowButton)
def returnValues(self) -> QHBoxLayout:
return self.rowLayout
def hiddenOrShow(self) -> None:
if self.nameLineEdit.echoMode() == QLineEdit.EchoMode.Password:
self.nameLineEdit.setEchoMode(QLineEdit.EchoMode.Normal)
else:
self.nameLineEdit.setEchoMode(QLineEdit.EchoMode.Password)
if __name__ == "__main__":
app = QApplication(argv)
window = MainWindow()
window.show()
exit(app.exec())
I expect the text to change state when I click on the button of that text.
Your best bet is to subclass QHBoxLayout, as suggested by musicamante in the comments, and your Row class isn't that far off from doing just that.
The only changes that really need to be made are to add in the subclass reference in your Row to Row(QHBoxLayout), then would want to remove the self.rowLayout line because the Row class would become the layout. and you would change all the references to it to just self instead of self.rowLayout.
For example:
from sys import exit
from sys import argv
from PyQt6.QtWidgets import QApplication, QHBoxLayout, QPushButton, QVBoxLayout, QLabel, QWidget, QLineEdit
class MainWindow(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.screenWidth = 1920
self.screenHeight = 1080
self.windowWidth = 1000
self.windowHeight = 800
self.setWindowTitle("test 19")
self.setGeometry((self.screenWidth - self.windowWidth) // 2, (self.screenHeight - self.windowHeight) // 2, self.windowWidth, self.windowHeight)
self.initUi()
def initUi(self) -> None:
mainLayout = QVBoxLayout()
headLayout = QHBoxLayout()
nameLabel = QLabel("Name")
headLayout.addWidget(nameLabel)
mainLayout.addLayout(headLayout)
row1 = Row("google.com") # the row now is the layout
mainLayout.addLayout(row1) # so you add it directly to the main layout
row2 = Row("yahoo.com")
mainLayout.addLayout(row2) # use addLayout instead of addItem
for i in range(20):
rowi = Row(f"{i}")
mainLayout.addLayout(rowi)
self.setLayout(mainLayout)
class Row(QHBoxLayout): # use subclass declaration
def __init__(self, name):
super().__init__()
self.nameLineEdit = QLineEdit(f"{name}")
self.nameLineEdit.setDisabled(True)
self.addWidget(self.nameLineEdit) # add the widget to self
self.hiddenOrShowButton = QPushButton("")
self.hiddenOrShowButton.clicked.connect(self.hiddenOrShow)
self.addWidget(self.hiddenOrShowButton) # same thing here
# the returnValues method can be removed.
def hiddenOrShow(self) -> None:
if self.nameLineEdit.echoMode() == QLineEdit.EchoMode.Password:
self.nameLineEdit.setEchoMode(QLineEdit.EchoMode.Normal)
else:
self.nameLineEdit.setEchoMode(QLineEdit.EchoMode.Password)
if __name__ == "__main__":
app = QApplication(argv)
window = MainWindow()
window.show()
exit(app.exec())
I've a list of rectangle make in this way:
import sys
from PyQt5.QtCore import Qt, QRect, QRectF
from PyQt5.QtGui import *
from PyQt5.QtWidgets import ( QGridLayout, QWidget, QApplication, QGraphicsScene,
QGraphicsView, QGraphicsProxyWidget, QGraphicsRectItem
)
class WidRect(QWidget):
def __init__(self, mIntMeasureId,mStrLabel=None):
super().__init__()
self.aIntMeasureId=mIntMeasureId
self.aStrLabel=mStrLabel
#self.setWidget(QLabel(mStrLabel))
def mousePressEvent(self, QMouseEvent):
if QMouseEvent.button() == Qt.LeftButton:
print("Left Button Clicked on measure "+ str(self.aIntMeasureId))
elif QMouseEvent.button() == Qt.RightButton:
#do what you want here
print("Right Button Clicked on measure "+ str(self.aIntMeasureId))
class MeasureGrid(QWidget):
#grid = None
#scene = None
def __init__(self, geometry, parent=None):
super(MeasureGrid, self).__init__(parent=parent)
#super().onclick.connec(self.click)
self.aLayMeasureGrid = QGridLayout(self)
self.aGsMeasureScene = QGraphicsScene(self)
self.aGsMeasureScene .setSceneRect(geometry.x(), geometry.y(), geometry.width(), geometry.height())
self.aGsMeasureScene .setBackgroundBrush(Qt.white)
self.aGvMeasureView = QGraphicsView()
self.aGvMeasureView.setScene(self.aGsMeasureScene )
self.aLayMeasureGrid.addWidget(self.aGvMeasureView)
for i in range(1,5):
rect = QRectF(100*i, 20, 80, 140)
self.addRect(rect,i)
def addRect(self, mRecMeasureBox,mIntMeasureNum):
#label = QLabel("World")
#label.setAlignment(Qt.AlignCenter | Qt.AlignCenter)
wrRectBox = WidRect(mIntMeasureNum,"Meas #" + str(mIntMeasureNum))
### CREA IL PROXY ###
gpqProxMeasure = QGraphicsProxyWidget()
gpqProxMeasure.setWidget(wrRectBox)
gpqProxMeasure.setGeometry(mRecMeasureBox)
self.aGsMeasureScene.addItem(gpqProxMeasure)
### CREA L'OGGETTO GENITORE ###
griMeasure = QGraphicsRectItem(mRecMeasureBox)
#rectangle.setFlag(QGraphicsItem.ItemIsMovable, True)
self.aGsMeasureScene.addItem(griMeasure)
gpqProxMeasure.setParentItem(griMeasure)
if __name__ == "__main__":
app = QApplication(sys.argv)
mgMeasureList = MeasureGrid(QRect(10, 10, 550, 280))
mgMeasureList.show()
app.exec_()
I would like to add:
a text into every rectangle
an icon into every rectangle
change the icon with another if I click with right button.
I try to add a QLabel but I cannot add a widget to a widget.
Can you tell me where is my error?
I create the rectangle using a proxy object because in the final version of the application they will be from 100 to 300 objects.
I'm trying to code a widget that slightly increases in size on mouse-over and decreases when the mouse leaves again.
This is what I have come up with so far:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from random import randrange
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedSize(500, 500)
cent_widget = QWidget()
self.setCentralWidget(cent_widget)
layout = QVBoxLayout()
cent_widget.setLayout(layout)
layout.addWidget(MyItem(), Qt.AlignCenter,
alignment=Qt.AlignCenter)
class MyItem(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setBaseSize(200, 250)
self.setMinimumSize(self.baseSize())
self.resize(self.baseSize())
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setStyleSheet('background: #{:02x}{:02x}{:02x}'.format(
randrange(255), randrange(255), randrange(255)
))
# Animation
self._enlarged = False
self.zoom_factor = 1.2
self.anim = QPropertyAnimation(self, b'size')
self.anim.setEasingCurve(QEasingCurve.InOutSine)
self.anim.setDuration(250)
def enterEvent(self, event: QEvent) -> None:
self.resize_anim()
self._enlarged = True
def leaveEvent(self, event: QEvent) -> None:
self.resize_anim()
self._enlarged = False
def resize_anim(self):
if self._enlarged:
new_size = self.baseSize()
else:
new_size = QSize(
int(self.baseSize().width() * self.zoom_factor),
int(self.baseSize().height() * self.zoom_factor)
)
self.anim.setEndValue(new_size)
self.anim.start()
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
It's almost working the way I want, my only problem is that the widget gets resized from the top-left corner instead of from the center.
How can I change that?
Instead of animating using the size property you should use the geometry property as it is relative to the parent widget, so you can animate its geometry making the center remain invariant.
from PyQt5.QtCore import (
QAbstractAnimation,
QEasingCurve,
QEvent,
QPropertyAnimation,
QRect,
Qt,
)
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QSizePolicy,
QVBoxLayout,
QWidget,
)
import random
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedSize(500, 500)
cent_widget = QWidget()
self.setCentralWidget(cent_widget)
layout = QVBoxLayout(cent_widget)
layout.addWidget(MyItem(), alignment=Qt.AlignCenter)
class MyItem(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setBaseSize(200, 250)
self.setMinimumSize(self.baseSize())
self.resize(self.baseSize())
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setStyleSheet(
"background: {}".format(QColor(*random.sample(range(255), 3)).name())
)
# Animation
self.zoom_factor = 1.2
self.anim = QPropertyAnimation(self, b"geometry")
self.anim.setEasingCurve(QEasingCurve.InOutSine)
self.anim.setDuration(250)
def enterEvent(self, event: QEvent) -> None:
initial_rect = self.geometry()
final_rect = QRect(
0,
0,
int(initial_rect.width() * self.zoom_factor),
int(initial_rect.height() * self.zoom_factor),
)
final_rect.moveCenter(initial_rect.center())
self.anim.setStartValue(initial_rect)
self.anim.setEndValue(final_rect)
self.anim.setDirection(QAbstractAnimation.Forward)
self.anim.start()
def leaveEvent(self, event: QEvent) -> None:
self.anim.setDirection(QAbstractAnimation.Backward)
self.anim.start()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
I'm writing a simple gui with a QGraphicsScene. Inside this scene i have 2 simple XY axis and I have a function to retrieve the position of my mouse click on the scene.
I added a simple movable ellipse to my scene and I'm retrieving it's position (at the moment it's only an on click detection, later I will implement a signal for its movement)
Here's the code:
View.py:
import math
import sys
from PySide2.QtCore import Signal, QPointF
from PySide2 import QtCore
from PySide2.QtGui import QColor, QPainterPath
from PySide2.QtWidgets import (QGraphicsItem,
QApplication,
QGraphicsScene,
QGraphicsView,
QHBoxLayout,
QMainWindow,
QPushButton,
QWidget,
QSizeGrip
)
from PySide2 import QtGui
from PySide2.QtGui import QIcon
class GraphicsScene(QGraphicsScene): # Used to indicate inheritance from parent class
clicked = Signal(QPointF)
def drawBackground(self, painter, rect):
l = min(rect.width(), rect.height()) / 30
x_left = QPointF(rect.left(), 0)
x_right = QPointF(rect.right(), 0)
painter.drawLine(x_left, x_right)
right_triangle = QPainterPath()
right_triangle.lineTo(-0.5 * math.sqrt(3) * l, 0.5 * l)
right_triangle.lineTo(-0.5 * math.sqrt(3) * l, -0.5 * l)
right_triangle.closeSubpath()
right_triangle.translate(x_right)
painter.setBrush(QColor("black"))
painter.drawPath(right_triangle)
y_top = QPointF(0, rect.top())
y_bottom = QPointF(0, rect.bottom())
painter.drawLine(y_top, y_bottom)
top_triangle = QPainterPath()
top_triangle.lineTo(.5*l, -0.5 * math.sqrt(3) * l)
top_triangle.lineTo(-.5*l, -0.5 * math.sqrt(3) * l)
top_triangle.closeSubpath()
top_triangle.translate(y_bottom)
painter.setBrush(QColor("black"))
painter.drawPath(top_triangle)
def mousePressEvent(self, event):
sp = event.scenePos()
self.clicked.emit(sp)
super().mousePressEvent(event)
class MyView(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("8D.me")
self.setFixedSize(800, 500)
self.btn = QPushButton("test2")
self.btn.setStyleSheet( "color: white; border-radius: 4px; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); background: rgb(66, 184, 221); min-width:100px;min-height:30px")
self.view = QGraphicsView()
print(self.view.size())
self.view.scale(1, -1)
self.scene = GraphicsScene()
self.view.setScene(self.scene)
self.setIcon()
self.ellipse=self.scene.addEllipse(10,10,10,10)
self.ellipse.setFlag(QGraphicsItem.ItemIsMovable)
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QHBoxLayout(central_widget)
layout.addWidget(self.btn)
layout.addWidget(self.view)
#flags = QtCore.Qt.WindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
#self.setWindowFlags(flags)
self.scene.clicked.connect(self.handle_clicked)
def handle_clicked(self, p):
print("position",self.ellipse.pos())
print("clicked", p.x(), p.y())
print(self.view.size())
# Insert methods for creating/adding elements to the default view.
# Mehods....
def setIcon(self):
appIcon = QIcon('logo')
self.setWindowIcon(appIcon)
#Insert here the public methods called by the Controller to update the view
main.py:
import View
#import Model
#import Controller
#Useful example: https://realpython.com/python-pyqt-gui-calculator/
def main():
"""Main function."""
# Create an instance of QApplication
app = QApplication([])
# Show the GUI
mainwindow =View.MyView()
mainwindow.show()
# Create instances of model and controller
#model = Model.myModel()
#Controller.myController(view=mainwindow,model=model)
return app.exec_()
if __name__ == '__main__':
main()
EDIT: I added also the main code. I have the View in a separate file since I am using a MVC design pattern.
My GUI looks like this:
And it works so far.
The problem is if I move the ellipse (which moves correctly) my XY axis moves too, and I can't figure out why.
For example this is how my GUI changes if I move the ellipse toward the upper right angle:
What's the problem? How do I "fix" the XY axis? I would like to keep my axis still and move only the ellipse inside the scene region.
I solved the problem following this: https://forum.qt.io/topic/48564/force-qgraphicsview-size-to-match-qgraphicsscene-size/7
Basically i added the following lines to my code:
self.view.setFixedSize(600,480)
self.view.setSceneRect(-300,-220,600,480)
self.view.fitInView(0,0,600,480, Qt.KeepAspectRatio)
So the new View.py is:
import math
import sys
from PySide2.QtCore import Signal, QPointF, Qt
from PySide2 import QtCore
from PySide2.QtGui import QColor, QPainterPath
from PySide2.QtWidgets import (QGraphicsItem,
QApplication,
QGraphicsScene,
QGraphicsView,
QHBoxLayout,
QMainWindow,
QPushButton,
QWidget,
QSizeGrip
)
from PySide2 import QtGui
from PySide2.QtGui import QIcon
class GraphicsScene(QGraphicsScene): # Used to indicate inheritance from parent class
clicked = Signal(QPointF)
def drawBackground(self, painter, rect):
#print('Pinto rect',rect.width(),rect.height())
l = min(rect.width(), rect.height()) / 30
#print('printo l',l)
#Qui c'è il bug. La funzione drawbackground viene chiamata a ogni modifica della scene.
#Lo starting point del drawing della line cambia nel tempo, ecco perchè mi si spostano gli assi
x_left = QPointF(rect.left(), 0)
x_right = QPointF(rect.right(), 0)
print('printo gli x',rect.left(),rect.height())
painter.drawLine(x_left, x_right)
right_triangle = QPainterPath()
right_triangle.lineTo(-0.5 * math.sqrt(3) * l, 0.5 * l)
right_triangle.lineTo(-0.5 * math.sqrt(3) * l, -0.5 * l)
right_triangle.closeSubpath()
right_triangle.translate(x_right)
painter.setBrush(QColor("black"))
painter.drawPath(right_triangle)
y_top = QPointF(0, rect.top())
y_bottom = QPointF(0, rect.bottom())
painter.drawLine(y_top, y_bottom)
top_triangle = QPainterPath()
top_triangle.lineTo(.5*l, -0.5 * math.sqrt(3) * l)
top_triangle.lineTo(-.5*l, -0.5 * math.sqrt(3) * l)
top_triangle.closeSubpath()
top_triangle.translate(y_bottom)
painter.setBrush(QColor("black"))
painter.drawPath(top_triangle)
def mousePressEvent(self, event):
sp = event.scenePos()
self.clicked.emit(sp)
super().mousePressEvent(event)
class MyView(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("8D.me")
self.setFixedSize(800, 500)
self.btn = QPushButton("test2")
self.btn.setStyleSheet( "color: white; border-radius: 4px; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); background: rgb(66, 184, 221); min-width:100px;min-height:30px")
self.view = QGraphicsView()
print(self.view.size())
self.view.setFixedSize(600,480)
self.view.setSceneRect(-300,-220,590,470)
self.view.fitInView(0,0,600,480, Qt.KeepAspectRatio)
self.view.scale(1, -1)
self.scene = GraphicsScene()
self.view.setScene(self.scene)
self.setIcon()
self.ellipse=self.scene.addEllipse(-5,-5,10,10)
self.ellipse.setFlag(QGraphicsItem.ItemIsMovable)
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QHBoxLayout(central_widget)
layout.addWidget(self.btn)
layout.addWidget(self.view)
#flags = QtCore.Qt.WindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
#self.setWindowFlags(flags)
self.scene.clicked.connect(self.handle_clicked)
def handle_clicked(self, p):
print("position",self.ellipse.pos())
print("clicked", p.x(), p.y())
print(self.view.size())
# Insert methods for creating/adding elements to the default view.
# Mehods....
def setIcon(self):
appIcon = QIcon('logo')
self.setWindowIcon(appIcon)
#Insert here the public methods called by the Controller to update the view
This question already exists:
How to make the picture zoom (QGraphicsItem), and make scrollbars change their size accordingly (QGraphicsScene)? [duplicate]
Closed 4 years ago.
How to add a pixmap drawn in QGraphicsItem to QGraphicsScene in the following example?
#!/usr/bin/env python
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsView, QGraphicsScene, QMainWindow
class TicTacToe(QGraphicsItem):
def __init__(self, helper):
super(TicTacToe, self).__init__()
self.mypixmap = QPixmap("exit.png")
def paint(self, painter, option, widget):
painter.setOpacity(1)
painter.drawPixmap(0,0, 300, 300, self.mypixmap)
def boundingRect(self):
return QRectF(0,0,300,300)
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
scene = QGraphicsScene(self)
self.tic_tac_toe = TicTacToe(self)
scene.addItem(self.tic_tac_toe)
self.setScene(scene)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.y = MyGraphicsView()
self.setCentralWidget(self.y)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Example()
w.show()
sys.exit(app.exec_())
Try it:
Drag and drop image,
Enlarge Image -> Qt.Key_Right,
Reduce image <- Qt.Key_Left.
from PyQt5.QtCore import (QLineF, QPointF, QRectF,
pyqtSignal, QStandardPaths, Qt)
from PyQt5.QtGui import (QIcon, QBrush, QColor, QPainter, QPixmap)
from PyQt5.QtWidgets import (QAction, QMainWindow, QApplication,
QGraphicsObject,
QGraphicsView, QGraphicsScene, QGraphicsItem,
QGridLayout, QVBoxLayout, QHBoxLayout,
QFileDialog, QLabel, QLineEdit, QPushButton)
from PyQt5.QtOpenGL import QGLFormat
class MyGraphicsView(QGraphicsView):
backgroundColor = QColor(28, 31, 34) # Background color
def __init__(self):
super(MyGraphicsView, self).__init__()
self.resize(800, 600)
self.setBackgroundBrush(self.backgroundColor)
self.setCacheMode(self.CacheBackground)
self.setRenderHints(
QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
if QGLFormat.hasOpenGL():
self.setRenderHint(QPainter.HighQualityAntialiasing)
self.setViewportUpdateMode(self.SmartViewportUpdate)
self._scene = QGraphicsScene(-400, -300, 800, 600, self)
self.setScene(self._scene)
self._itemImage = None
def keyReleaseEvent(self, event):
""" Button processing event """
self._scaleImage(event)
super(MyGraphicsView, self).keyReleaseEvent(event)
def closeEvent(self, event):
""" Clear all items in the scene when the window is `closed` """
self._scene.clear()
self._itemImage = None
super(MyGraphicsView, self).closeEvent(event)
def _scaleImage(self, event):
""" Image zoom operation """
if not self._itemImage:
return
scale = self._itemImage.scale()
if event.key() == Qt.Key_Right:
# Enlarge Image -> Qt.Key_Right
if scale >= 0.91:
return
self._itemImage.setScale(scale + 0.1)
elif event.key() == Qt.Key_Left:
# Reduce image <- Qt.Key_Left
if scale <= 0.11:
return
self._itemImage.setScale(scale - 0.1)
def loadImage(self):
path, _ = QFileDialog.getOpenFileName(
self, 'Please select an image',
QStandardPaths.writableLocation(QStandardPaths.DesktopLocation),
'Image(*.jpg *.png)')
if not path:
return
if self._itemImage:
# Delete previous item
self._scene.removeItem(self._itemImage)
del self._itemImage
self._itemImage = self._scene.addPixmap(QPixmap(path))
self._itemImage.setFlag(QGraphicsItem.ItemIsMovable)
self._itemImage.setScale(0.1) # Default load factor
size = self._itemImage.pixmap().size()
# Adjust the image in the middle
self._itemImage.setPos(
-size.width() * self._itemImage.scale() / 2,
-size.height() * self._itemImage.scale() / 2
)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = MyGraphicsView()
w.show()
ww = QPushButton('Select a file', clicked=w.loadImage)
ww.show()
sys.exit(app.exec_())