I would like to rotate a widget using QGraphicsScene in PyQt (QLineEdit and QPushButton) - pyqt5

I would like to rotate a widget in pyqt5, I have developed this code but it doesn't work. The angle doesn't update and it returns a False. Do anyone know how to update this angle to make the widget rotate? If someone can help, thank you.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
robotx=200
roboty=100
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My Awesome Rotating Widget")
self.setGeometry(0, 0, 640, 480)
self.R=Robot()
self.angle=0
self.AngleLineEdit=QLineEdit(self)
self.AngleLineEdit.setGeometry(50,50,160,35)
self.AngleLineEdit.setStyleSheet("background-color: rgba(50,50,50,20); color:black; font-weight: bold; font-size: 8pt; font-family: Helvetica; border-radius:5px;")
self.AcceptButton=QPushButton(self)
self.AcceptButton.setText("Accept")
self.AcceptButton.setGeometry(50,100,160,35)
self.AcceptButton.setStyleSheet("QPushButton{color:black; font-weight: bold; font-size: 8pt; font-family: Helvetica; background-color:rgb(255,255,255,20); border-radius:5px}""QPushButton:hover{background-color : rgb(255,255,255,100);}")
container = RotatableContainer(self,self.R, 0)
container.move(robotx,roboty)
container.resize(150,150)
container.setStyleSheet("background-color:transparent;")
self.AcceptButton.clicked.connect(lambda: self.RotateWidget())
self.AcceptButton.clicked.connect(container.rotate)
self.show()
def RotateWidget(self):
self.angle=int(self.AngleLineEdit.text())
print(self.angle)
class RotatableContainer(QGraphicsView):
def __init__(self, parent, widget, angle):
super().__init__(parent)
scene = QGraphicsScene(self)
self.setScene(scene)
self.proxy = QGraphicsProxyWidget()
self.proxy.setWidget(widget)
self.proxy.setTransformOriginPoint(self.proxy.boundingRect().center())
self.proxy.setRotation(angle)
scene.addItem(self.proxy)
def rotate(self, angle):
print(angle)
self.proxy.setRotation(angle)
class Robot(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(0,0,100,100)
def paintEvent(self, event):
p=QPainter(self)
r=QRect(0,0,99,99)
c=QColor(0,0,0)
p.setBrush(QBrush(c))
p.drawRect(r)
app = QApplication([])
window = MainWindow()
app.exec_()
I have seen an example of how to rotate a widget using a qslider but I don't know how to adapt it using a QLineEdit and a QPushButton.

The first argument of clicked is always its check state, which by default is False for non checkable/checked buttons.
Since you've connected the signal to the rotate function, the argument is that of the button signal, and since False also means 0, you're practically doing self.proxy.setRotation(0).
Set the container as an instance attribute and call its rotate function from there:
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.container = RotatableContainer(self,self.R, 0)
self.container.move(robotx,roboty)
self.container.resize(150,150)
self.container.setStyleSheet("background-color:transparent;")
self.AcceptButton.clicked.connect(self.RotateWidget)
self.show()
def RotateWidget(self):
angle = self.AngleLineEdit.text()
if angle.isdigit():
self.angle = angle
self.container.rotate(self.angle)
Note: you should always use layout managers, and only classes and constants should have capitalized names (see the Style Guide for Python Code).

Related

Indicating that a QOpenGLWidget widget is to have a translucent background does not appear to work

There is no effect when I set the WA_TranslucentBackground attribute on a widget (derived from QOpenGLWidget). I have to set the attribute on the main window instead, which doesn't seem right: the documentation clearly states that the attribute applies to the widget itself:
Indicates that the widget should have a translucent background, i.e.,
any non-opaque regions of the widgets will be translucent because the
widget will have an alpha channel
What is the correct way to make the widget itself have a translucent background? Here is the code:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QOpenGLWidget
from PyQt5.QtCore import Qt, QPointF, QLineF
from PyQt5.QtGui import QColor, QPen, QPainter, QPaintEvent
class LineWidget(QOpenGLWidget):
def __init__(self, parent, start : QPointF, end : QPointF, colour : str = 'black'):
super(LineWidget, self).__init__(parent)
self.colour = colour
self.line = QLineF(start, end)
# self.setAttribute(Qt.WA_TranslucentBackground)
def paintEvent(self, a0: QPaintEvent) -> None:
painter = QPainter(self)
painter.begin(self)
painter.setPen(QPen(QColor(self.colour), 5))
painter.drawLine(self.line)
painter.end()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.lineWidget = LineWidget(self, QPointF(0, 0), QPointF(400, 400), 'red')
self.setCentralWidget(self.lineWidget)
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())

How to stretch QLabel in PyQt5

How to change the following code to get the QLabel stretch to width of the window ?
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 200, 100)
self.label = QLabel('Hello World!', self)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet('font-size: 12pt; background-color: red')
self.show()
app = QApplication([])
win = Window()
app.exec()
As the documentation about QMainWindow says, you must set a central widget for it:
Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
The problem is that you need a layout manager in order to properly adapt widget sizes inside a parent, and just manually setting widget geometries is generally discouraged.
You created the label as a direct child, so it will have no knowledge about its parents size changes.
Just set the label as central widget.
self.setCentralWidget(self.label)
Otherwise, you can use a container widget, set a layout and add the label to it, but you still must set the central widget.
central = QWidget()
layout = QVBoxLayout(central)
layout.addWidget(self.label)
self.setCentralWidget(central)
The alternative is to directly use a QWidget instead of QMainWindow as you did in your answer.
you can use sizePolicy
self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
Thank you very much for your answers. The problem is now solved by the following code changes
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 200, 100)
self.label = QLabel('Hello World!', self)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet('font-size: 12pt; background-color: red')
self.box_layout = QHBoxLayout()
self.box_layout.addWidget(self.label)
self.setLayout(self.box_layout)
self.show()
app = QApplication([])
win = Window()
app.exec()
Edit: laytout -> box_layout

QGraphicsPixmapItem is not being positioned correctly

I need to move a QGraphicsPixmapItem through a circle that it is at the top left corner of the image. That is, when I grab with the mouse the circle, I need the top left corner of the image to follow the circle. I subclassed a QGraphicsEllipseItem and reimplemented the itemChange method but when I set the position of the image to that value, the image is not being positioned correctly. What should I modify in my code?
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsView
from PyQt5 import QtGui, QtWidgets
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.scene = Scene()
self.view = QGraphicsView(self)
self.setGeometry(10, 30, 850, 600)
self.view.setGeometry(20, 22, 800, 550)
self.view.setScene(self.scene)
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
# other stuff here
self.set_image()
def set_image(self):
image = Image()
self.addItem(image)
image.set_pixmap()
class Image(QtWidgets.QGraphicsPixmapItem):
def __init__(self, parent=None):
super(Image, self).__init__(parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
def set_pixmap(self):
pixmap = QtGui.QPixmap("image.jpg")
self.setPixmap(pixmap)
self.pixmap_controller = PixmapController(self)
self.pixmap_controller.set_pixmap_controller()
self.pixmap_controller.setPos(self.boundingRect().topLeft())
self.pixmap_controller.setFlag(QtWidgets.QGraphicsItem.ItemSendsScenePositionChanges, True)
def change_image_position(self, position):
self.setPos(position)
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
color = QtGui.QColor(0, 0, 0)
brush = QtGui.QBrush(color)
self.setBrush(brush)
def set_pixmap_controller(self):
self.setRect(-5, -5, 10, 10)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange:
self.pixmap.change_image_position(value)
return super(PixmapController, self).itemChange(change, value)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
When a graphics item has a parent, its coordinate system is based on that parent, not on the scene.
The problem is that when you try to move the PixmapController, the movement is in parent coordinates (the pixmap item). When you check for the ItemPositionChange you are you're changing the parent position but the item position is changed anyway, based on the parent coordinate system.
While you could just return an empty QPoint (which will not change the item position), this wouldn't be a good choice: as soon as you release the mouse and start to move it again, the pixmap will reset its position.
The solution is not to set the movable item flag, but filter for mouse movements, compute a delta based on the click starting position, and use that delta to move the parent item based on its current position.
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
# the item should *NOT* move
# self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
color = QtGui.QColor(0, 0, 0)
brush = QtGui.QBrush(color)
self.setBrush(brush)
def set_pixmap_controller(self):
self.setRect(-5, -5, 10, 10)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.startPos = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
delta = event.pos() - self.startPos
self.parentItem().setPos(self.parentItem().pos() + delta)
If you want to use your change_image_position function, you need to change those functions accordingly; the code below does the same thing as the last line in the example above:
class Image(QtWidgets.QGraphicsPixmapItem):
# ...
def change_image_position(self, delta):
self.setPos(self.pos() + delta)
class PixmapController(QtWidgets.QGraphicsEllipseItem):
# ...
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
delta = event.pos() - self.startPos
self.pixmap.change_image_position(delta)
Tip: do not add a child widget to a QMainWindow like that, as it will not resize correctly when the window is resized. Use self.setCentralWidget(self.view) instead; if you want to add margins, use a container QWidget, set that widget as the central widget, add a simple QHBoxLayout (or QVBoxLayout), add the view to that layout and then set the margins with layout.setContentsMargins(left, top, right, bottom)

How to set QFrame color in an eventFilter?

I have a simple QWidget that contains a frame and two labels. I want to use eventFilter to change QFrame background color on label hover. Can someone please check the below code and tell me why I can't change the QFrame background and if it is the correct way for doing it?
import sys
from PyQt5.QtWidgets import QWidget, QHBoxLayout, \
QGraphicsDropShadowEffect, QPushButton, QApplication, QComboBox, QFrame, QLabel
from PyQt5 import QtCore
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QHBoxLayout(self)
self.frame = QFrame(self)
self.setObjectName("frame")
self.frame_lay = QHBoxLayout()
self.one_label = QLabel(self.frame)
self.one_label.setText("one")
self.one_label.setObjectName("one")
self.two_label = QLabel(self.frame)
self.two_label.setText("two")
self.two_label.setObjectName("two")
self.one_label.installEventFilter(self)
self.two_label.installEventFilter(self)
self.frame_lay.addWidget(self.one_label)
self.frame_lay.addWidget(self.two_label)
self.frame.setStyleSheet("""QFrame{background-color: red;}""")
self.frame.setLayout(self.frame_lay)
self.frame_lay.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.frame)
self.setLayout(self.layout)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.Enter:
if type(obj) == QLabel:
if obj.objectName() in ["one", "two"]:
print(obj.objectName())
self.frame.setStyleSheet("""QFrame#frame{background-color: blue;}""")
return super(QWidget, self).eventFilter(obj, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Should the installEventFilter be applied to QWidget or QFrame? The labels are contained within the QFrame.
Thanks
You set the frame object name for the "MainWindow", but in the event filter you used the object name for a QFrame class.
Just set the object name for the frame instead:
self.frame.setObjectName("frame")
Note that QLabel inherits from QFrame, so, using QFrame{background-color: red;} technically applies the background for both the parent frame and any child label.
In case you want to be more specific, you either use the object name as you did in the event filter, or use the .ClassName selector, which applies the sheet only to the class and not its subclasses (note the full stop character before QFrame):
self.frame.setStyleSheet(""".QFrame{background-color: red;}""")

How to set QMdiAread TabBar Width

I want to set the QMdiArea TabBar width by setStyleSheetfunction,but it can not set the width, it can set the height.Now I have no idea how to solve the problem, can you give me a idea?
Thanks a lot.Here is my code.
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(0,0,600,600)
self.mdi = QMdiArea(self)
self.mdi.setGeometry(40, 40, 500, 500)
self.mdi.setViewMode(QMdiArea.TabbedView)
self.mdi.setTabShape(QTabWidget.Triangular)
self.mdi.setTabsClosable(True)
self.mdi.setDocumentMode(False)
self.mdi.setTabsMovable(True)
self.mdi.setTabPosition(QTabWidget.North)
self.mdi.setOption(QMdiArea.DontMaximizeSubWindowOnActivation)
# setStyleSheet can not set the width,but it can set the height
self.mdi.setStyleSheet("QTabBar::tab { height: 20px; width: 20px; }");
self.setWindowTitle("MDI demo")
sub = QMdiSubWindow()
sub.resize(200, 200)
sub.setWindowTitle("11")
self.mdi.addSubWindow(sub)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = MainWindow()
demo.show()
sys.exit(app.exec_())
I have solve the problem.The main code is below.
child = self.mdi.findChild(QTabBar)
child.setExpanding(False)