Coloring QPolygonItem - python

How can I color my QPolygonF item? I have created triangle but don't know how to fill it with certain color.
I tried to find class in Qt library but didn't find any. Here is code where I create the triangle and add it to the scene. I tried to use setBrush() function, but QPolygonF doesn't have that class..
triangle = QtGui.QPolygonF()
triangle.append(QtCore.QPointF(0,550)) # Bottom-left
triangle.append(QtCore.QPointF(50, 550)) # Bottom-right
triangle.append(QtCore.QPointF(25, 525)) # Tip
self.scene.addPolygon(triangle)

When you use the addPolygon method this returns a QGraphicsPolygonItem, and that GraphicsPolygonItem inherits from QAbstractGraphicsShapeItem, and that class gives the ability to change the fill color using the setBrush() method and the border color with setPen():
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
triangle = QtGui.QPolygonF()
triangle.append(QtCore.QPointF(0, 550)) # Bottom-left
triangle.append(QtCore.QPointF(50, 550)) # Bottom-right
triangle.append(QtCore.QPointF(25, 525)) # Tip
triangle_item = self.scene().addPolygon(triangle)
triangle_item.setBrush(QtGui.QBrush(QtGui.QColor("salmon")))
triangle_item.setPen(QtGui.QPen(QtGui.QColor("gray")))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = GraphicsView()
w.resize(320, 240)
w.show()
sys.exit(app.exec_())

Related

How to know the exact position of a camera viewbox in Qt?

I am working with OpenGL in python and trying to attach 2d images to a canvas (the images will change according to a certain frequence).
I managed to achieve that but to continue my task i need two things:
the major problem: I need to get the image position (or bounds), sorry if i don't have the correct term, i am new to this. basically i just need to have some kind of positions to know where my picture is in the canvas. i tried to look into the methods and attributes of self.view.camera I could not find anything to help.
one minor problem: i can move the image with the mouse along the canvas and i zoom it. i wonder if it is possible to only allow the zoom but not allow the right/left move [this is resolved in the comments section]
here is my code:
import sys
from PySide2 import QtWidgets, QtCore
from vispy import scene
from PySide2.QtCore import QMetaObject
from PySide2.QtWidgets import *
import numpy as np
import dog
import time
import imageio as iio
class CameraThread(QtCore.QThread):
new_image = QtCore.Signal(object)
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
def run(self):
try:
while True:
frame = iio.imread(dog.getDog(filename='randog'))
self.new_image.emit(frame.data)
time.sleep(10.0)
finally:
print('end!')
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 400)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.groupBox = QGroupBox(self.centralwidget)
self.groupBox.setObjectName("groupBox")
self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
QMetaObject.connectSlotsByName(MainWindow)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# OpenGL drawing surface
self.canvas = scene.SceneCanvas(keys='interactive')
self.canvas.create_native()
self.canvas.native.setParent(self)
self.setWindowTitle('MyApp')
self.view = self.canvas.central_widget.add_view()
self.view.bgcolor = '#ffffff' # set the canvas to a white background
self.image = scene.visuals.Image(np.zeros((1, 1)),
interpolation='nearest',
parent= self.view.scene,
cmap='grays',
clim=(0, 2 ** 8 - 1))
self.view.camera = scene.PanZoomCamera(aspect=1)
self.view.camera.flip = (0, 1, 0)
self.view.camera.set_range()
self.view.camera.zoom(1000, (0, 0))
self._camera_runner = CameraThread(parent=self)
self._camera_runner.new_image.connect(self.new_image, type=QtCore.Qt.BlockingQueuedConnection)
self._camera_runner.start()
#QtCore.Slot(object)
def new_image(self, img):
try:
self.image.set_data(img)
self.image.update()
except Exception as e:
print(f"problem sending image: {e}")
def main():
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('my_gui')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Do you want to know the coordinates of the picture in the viewport (the window), or do you want the coordinates of the picture on the canvas? Vispy actually puts the image at (0,0) by default inside the Vispy canvas. When you move around the canvas you actually aren't moving the canvas around, you are just moving the camera which is looking at the canvas so the coordinates of the picture stay at (0,0) regardless if you move around the viewport or the camera or not. Also the coordinates of the Vispy canvas correspond one to one with the pixel length and width of your image. One pixel is one unit in Vispy. You can check this by adding this method to your MainWindow class:
def my_handler(self,event):
transform = self.image.transforms.get_transform(map_to="canvas")
img_x, img_y = transform.imap(event.pos)[:2]
print(img_x, img_y)
# optionally do the below to tell other handlers not to look at this event:
event.handled = True
and adding this to your __init__ method:
self.canvas.events.mouse_move.connect(self.my_handler)
You can see that when you hover over the top left corner of your image, it should print roughly (0,0).
def my_handler(self,event):
transform = self.image.transforms.get_transform(map_to="canvas")
img_x, img_y = transform.imap(event.pos)[:2]
print(img_x, img_y)
# optionally do the below to tell other handlers not to look at this event:
event.handled = True

Move the displayed PyQt5 scene

I have a QGraphicSscene in a QGraphicsView object. In my scene you can draw ROIs, so I track the mouse position all the time. Since the objects are often not very big you can zoom in on them, I would like to move the displayed scene section when the mouse is at the edge of the displayed scene. With event.scenePos() I get the position of my mouse pointer, but how can I check if I am at the edge of the scene or not?
Zooming in and out functions in my code as follows:
def zoomIn(self):
self.view.scale(1.1, 1.1)
# some other stuff
def zoomOut(self):
# The initial zoom is always adapted to an image so that it is always larger or equal to the
# size of the GraphicsViews Object (therefore the image fills all areas).
if.self.currentZoom > 1:
self.view.scale(0.9, 0.9)
# some other stuff
To determine if a point is on the edge, you have to verify that the point is inside the rectangle of the QGraphicsView viewport but outside of a smaller rectangle displaced from the previous rectangle by some pixels on all edges:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(self.view)
self.view.viewport().setMouseTracking(True)
self.view.scene().installEventFilter(self)
def eventFilter(self, obj, event):
if (
obj is self.view.scene()
and event.type() == QtCore.QEvent.GraphicsSceneMouseMove
):
vp = self.view.mapFromScene(event.scenePos())
if self.check_if_the_point_is_on_the_edge(vp, delta=10):
print("on the border", event.scenePos())
return super().eventFilter(obj, event)
def check_if_the_point_is_on_the_edge(self, point, delta=1):
rect = self.view.viewport().rect()
internal_rect = rect.adjusted(delta, delta, -delta, -delta)
return rect.contains(point) and not internal_rect.contains(point)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
w.resize(640, 480)
sys.exit(app.exec_())

Grayscale QPushButton with QIcon until hovered over

I have a QPushButton that has a QIcon set. I would like to grayscale the icon's colors until it is hovered over or selected, without writing 2 separate image files for each icon. If use QPushButton.setDisabled(True) the icon's colors do in fact turn to grayscale, so I would like this same behavior being controlled through a enterEvent. Is this at all possible?
Yes, you can do exactly what you described. Enable the button in the enterEvent and disable it in the leaveEvent if it's not checked.
import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
class Button(QPushButton):
def __init__(self):
super().__init__()
self.setCheckable(True)
self.setDisabled(True)
self.setIcon(QIcon('icon.png'))
self.setIconSize(QSize(100, 100))
def enterEvent(self, event):
self.setEnabled(True)
def leaveEvent(self, event):
if not self.isChecked():
self.setDisabled(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
grid = QGridLayout(w)
grid.addWidget(Button(), 0, 0, Qt.AlignCenter)
w.show()
sys.exit(app.exec_())
I used a green check mark for the icon image. Result:
You also don't need to subclass QPushButton, this will work too.
btn = QPushButton()
btn.setCheckable(True)
btn.setIcon(QIcon('icon.png'))
btn.setIconSize(QSize(100, 100))
btn.setDisabled(True)
btn.enterEvent = lambda _: btn.setEnabled(True)
btn.leaveEvent = lambda _: btn.setEnabled(btn.isChecked())

Pyside2 how to get mouse position?

I want to get mouse position in my pyside2 application.(not desktop mouse position that QCursor gives) and I tried two way. Bellow is my code.
import sys
from PySide2 import QtGui, QtWidgets, QtCore
class Palette(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mousePressEvent(self, event):
print(event.pos()) # always return (0,0)
print(QtWidgets.QWidget.mapToGlobal(QtCore.QPoint(0, 0))) #makes parameter type error
print(QtWidgets.QWidget.mapToGlobal(QtWidgets.QWidget)) # makes parameter type error
print(QtWidgets.QWidget.mapToGlobal(QtWidgets.QWidget.pos())) # makes parameter type error
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
palette = Palette(self)
view = QtWidgets.QGraphicsView(palette, self)
view.resize(500, 500)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.resize(500, 500)
main_window.show()
app.exec_()
I very wonder how i can get my mouse pos...
From what I understand you want to get the position on the window if you click anywhere in a widget.
To solve the problem, the logic is as follows:
Get the mouse position with respect to the widget
Convert that position to a global position, that is, with respect to the screen.
Convert that global position to a position relative to the window.
For the first step if mousePressEvent() is used, event.pos() returns the position relative to the widget.
For the second step you must convert that position relative to the widget to global with mapToGlobal().
And for the third step mapFromGlobal() of window is used.
def mousePressEvent(self, event):
p = event.pos() # relative to widget
gp = self.mapToGlobal(p) # relative to screen
rw = self.window().mapFromGlobal(gp) # relative to window
print("position relative to window: ", rw)
super(Widget, self).mousePressEvent(event)
Update:
The QGraphicsScene is not a widget, it is not a visual element, although it is part of the representation of a visual element: the QGraphicsView. For you to understand I will explain you with an analogy, let's say that there is a cameraman recording a scene, in that example the QGraphicsScene is the scene and the QGraphicsView is what the camera records, that is, it shows a piece of the QGraphicsScene, so there could be another cameraman recording the scene from another point, so it would show the same scene from another perspective, so the position of the scene depends on the camera, so if your current question would be equivalent to saying which is the position of the point P respect to the camera i-th, and that from the scene is impossible, you should get it from the camera.
So in conclusion you should not use QGraphicsScene but QGraphicsView, the following solutions implement the same logic using 2 different methods:
1. Creating a custom class of QGraphicsView:
import sys
from PySide2 import QtGui, QtWidgets, QtCore
class GraphicsView(QtWidgets.QGraphicsView):
def mousePressEvent(self, event):
p = event.pos() # relative to widget
gp = self.mapToGlobal(p) # relative to screen
rw = self.window().mapFromGlobal(gp) # relative to window
print("position relative to window: ", rw)
super(GraphicsView, self).mousePressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
scene = QtWidgets.QGraphicsScene(self)
view = GraphicsView(scene, self)
self.setCentralWidget(view)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.resize(500, 500)
main_window.show()
sys.exit(app.exec_())
2. Using eventfilter:
import sys
from PySide2 import QtGui, QtWidgets, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
scene = QtWidgets.QGraphicsScene(self)
self._view = QtWidgets.QGraphicsView(scene, self)
self.setCentralWidget(self._view)
self._view.installEventFilter(self)
def eventFilter(self, obj, event):
if obj is self._view and event.type() == QtCore.QEvent.MouseButtonPress:
p = event.pos() # relative to widget
gp = self.mapToGlobal(p) # relative to screen
rw = self.window().mapFromGlobal(gp) # relative to window
print("position relative to window: ", rw)
return super(MainWindow, self).eventFilter(obj, event)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.resize(500, 500)
main_window.show()
sys.exit(app.exec_())
On the other hand mapToGlobal() is a method that must be called by an instance, when you use QtWidgets.QWidget.mapToGlobal() there is no instance, my question is, what widget do you have the position? the position you have with respect to self, so you must use self.mapToGlobal() , that works for the objects belonging to a class that inherit from QWidget as QGraphicsView, but not in QGraphicsScene since it does not inherit from QWidget, it is not a widget as indicated in the lines above.
I have recently found a more universal way of getting the cursor position, if you don't want to go through sub classing and events.
# get cursor position
cursor_position = QtGui.QCursor.pos()
print cursor_position
### Returns PySide2.QtCore.QPoint(3289, 296)

How do I set layout's fixed height in PyQt5?

I am trying to set a fixed height for QHBoxLayout. To elaborate more on that, I need to set a specific height for my Horizontal layout. However, I cannot find the proper way to do so. What should I do to make this happen?
hbox1 = QHBoxLayout()
As noted by #ekhumuro in QHBoxLayout you can not set the fixed height, you must do that to the widget where it will be contained as I show below:
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.setFixedHeight(300)
lay = QtWidgets.QHBoxLayout(self)
for letter in "ABCDEFG":
label = QtWidgets.QLabel(letter, alignment=QtCore.Qt.AlignCenter)
color = QtGui.QColor(*[random.randint(0, 255) for _ in range(3)])
label.setStyleSheet("background-color: {}".format(color.name()))
lay.addWidget(label)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Categories

Resources