I'm trying to make fading looped rectangle area. I used base code from here
Just decided expand it.
Its just blinking rectangle, but I need smooth fade-in and fade-out effects. So I decided to make method which will calulate new opacity percent and set it to painter. But it doesnt work in cycle.
This is my class now
class HighlightRect(QFrame):
board_width = 400 # width of frame
board_height = 400 #height of frame
def __init__(self, parent, x, y, width=50, height=50, blink_speed=1000):
super().__init__(parent)
self.blink_speed = blink_speed
self.opacity_timer = self.blink_speed
self.board_height = self.parent().height()
self.board_width = self.parent().width()
self.square_height = height
self.square_width = width
self.highlight_x = x
self.highlight_y = y
#self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.timer_draw = QtCore.QTimer(self)
self.timer_draw.timeout.connect(self.draw)
self.timer_draw.start(self.blink_speed)
self.color = QtCore.Qt.red
self.is_draw = False
self.x_apple = 0
self.y_apple = 0
self.draw()
def blink(self, painter):
self.color = QtCore.Qt.red
while self.opacity_timer >= 0:
self.opacity_timer -= 1 / 10 # просто подбор
percents = round(int(self.opacity_timer / self.blink_speed * 100)/100, 1)
print(percents)
painter.setOpacity(percents)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
print ("Paint Event?")
if self.is_draw == True:
print ("Draw")
#self.color = QtCore.Qt.red
self.blink_thread = threading.Thread(name='background', target=lambda: self.blink(painter))
self.blink_thread.start()
else:
self.opacity_timer = self.blink_speed
print ("Do not draw")
self.color = QtCore.Qt.transparent
threading.SystemExit = SystemExit
painter.setPen(self.color)
painter.drawRect(self.rect)
def draw(self):
self.is_draw = not self.is_draw
self.rect = QRect(self.highlight_x, self.highlight_y, self.square_width, self.square_height)
self.update()
Changind of opacity inside blink function but outside while loop works as well, but its static. No changes. Changing opacity in loop isn't work.
Whats wrong?
Maybe somewhere here is another more correct way to get what I want?
One possible solution is to create a QProperty that handles opacity and then use QPropertyAnimation to make the change smooth.
import random
import sys
from PySide6.QtCore import Property, Signal, QPropertyAnimation, QRect, Qt
from PySide6.QtGui import QPainter
from PySide6.QtWidgets import QFrame, QApplication
class Board(QFrame):
rect_opacity_changed = Signal(name="rectOpacityChanged")
def __init__(self, parent=None):
super(Board, self).__init__(parent)
self._rect_opacity = 1.0
self._rect = QRect(0, 0, 50, 50)
self._opacity_animation = QPropertyAnimation(
targetObject=self, propertyName=b"rect_opacity", duration=3000
)
for p, v in ((0.0, 0.0), (0.3, 1.0), (0.7, 1.0), (1.0, 0.0)):
self._opacity_animation.setKeyValueAt(p, v)
self._opacity_animation.finished.connect(self.change)
self.change()
#Property(float, notify=rect_opacity_changed)
def rect_opacity(self):
return self._rect_opacity
#rect_opacity.setter
def rect_opacity(self, opacity):
self._rect_opacity = opacity
self.rect_opacity_changed.emit()
self.update()
def change(self):
x = random.randint(0, self.width() - self._rect.width())
y = random.randint(0, self.height() - self._rect.height())
self._rect.moveTo(x, y)
self._opacity_animation.start()
def paintEvent(self, event):
painter = QPainter(self)
painter.setOpacity(self.rect_opacity)
painter.setPen(Qt.red)
painter.drawRect(self._rect)
def main():
app = QApplication([])
board = Board()
board.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Related
How to make an Angled arrow-type border in PyQt QFrame? In My code, I Have two QLabels and respective frames. My aim is to make an arrow shape border on right side of every QFrame.For clear-cut idea, attach a sample picture.
import sys
from PyQt5.QtWidgets import *
class Angle_Border(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Angle Border")
self.lbl1 = QLabel("Python")
self.lbl2 = QLabel("PyQt")
self.frame1 = QFrame()
self.frame1.setProperty("type","1")
self.frame1.setFixedSize(200,50)
self.frame1.setStyleSheet("background-color:red;color:white;"
"font-family:Trebuchet MS;font-size: 15pt;text-align: center;"
"border-top-right-radius:25px solid ; border-bottom-right-radius:25px solid ;")
self.frame2 = QFrame()
self.frame2.setFixedSize(200, 50)
self.frame2.setStyleSheet("background-color:blue;color:white;"
"font-family:Trebuchet MS;font-size: 15pt;text-align: center;"
"border-top:1px solid transparent; border-bottom:1px solid transparent;")
self.frame_outer = QFrame()
self.frame_outer.setFixedSize(800, 60)
self.frame_outer.setStyleSheet("background-color:green;color:white;"
"font-family:Trebuchet MS;font-size: 15pt;text-align: center;")
self.frame1_layout = QHBoxLayout(self.frame1)
self.frame2_layout = QHBoxLayout(self.frame2)
self.frame_outer_layout = QHBoxLayout(self.frame_outer)
self.frame_outer_layout.setContentsMargins(5,0,0,0)
self.frame1_layout.addWidget(self.lbl1)
self.frame2_layout.addWidget(self.lbl2)
self.hbox = QHBoxLayout()
self.layout = QHBoxLayout()
self.hbox.addWidget(self.frame1)
self.hbox.addWidget(self.frame2)
self.hbox.addStretch()
self.hbox.setSpacing(0)
# self.layout.addLayout(self.hbox)
self.frame_outer_layout.addLayout(self.hbox)
self.layout.addWidget(self.frame_outer)
self.setLayout(self.layout)
def main():
app = QApplication(sys.argv)
ex = Angle_Border()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Sample Picture
Since the OP didn't ask for user interaction (mouse or keyboard), a possible solution could use the existing features of Qt, specifically QSS (Qt Style Sheets).
While the currently previously accepted solution does follow that approach, it's not very effective, most importantly because it's basically "static", since it always requires knowing the color of the following item in order to define the "arrow" colors.
This not only forces the programmer to always consider the "sibling" items, but also makes extremely (and unnecessarily) complex the dynamic creation of such objects.
The solution is to always (partially) "redo" the layout and update the stylesheets with the necessary values, which consider the current size (which shouldn't be hardcoded), the following item (if any) and carefully using the layout properties and "spacer" stylesheets based on the contents.
The following code uses a more abstract, dynamic approach, with basic functions that allow adding/insertion and removal of items. It still uses a similar QSS method, but, with almost the same "line count", it provides a simpler and much more intuitive approach, allowing item creation, deletion and modification with single function calls that are much easier to use.
A further benefit of this approach is that implementing "reverse" arrows is quite easy, and doesn't break the logic of the item creation.
Considering all the above, you can create an actual class that just needs basic calls such as addItem() or removeItem().
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ArrowMenu(QWidget):
vMargin = -1
hMargin = -1
def __init__(self, items=None, parent=None):
super().__init__(parent)
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addStretch()
self.items = []
if isinstance(items, dict):
self.addItems(items.items())
elif items is not None:
self.addItems(items)
def addItems(self, items):
for item in items:
if isinstance(item, str):
self.addItem(item)
else:
self.addItem(*item)
def addItem(self, text, background=None):
self.insertItem(len(self.items), text, background)
def insertItem(self, index, text, background=None):
label = QLabel(text)
if background is None:
background = self.palette().window().color()
background.setAlpha(0)
else:
background = QColor(background)
# human eyes perceive "brightness" in different ways, let's compute
# that value in order to decide a color that has sufficient contrast
# with the background; see https://photo.stackexchange.com/q/10412
r, g, b, a = background.getRgbF()
brightness = r * .3 + g * .59 + b * .11
foreground = 'black' if brightness >= .5 else 'white'
label.setStyleSheet('color: {}; background: {};'.format(
foreground, background.name(background.HexArgb)))
layout = self.layout()
if index < len(self.items):
i = 0
for _label, _spacer, _ in self.items:
if i == index:
i += 1
layout.insertWidget(i * 2, _label)
layout.insertWidget(i * 2 + 1, _spacer)
i += 1
layout.insertWidget(index * 2, label)
spacer = QWidget(objectName='menuArrow')
layout.insertWidget(index * 2 + 1, spacer)
self.items.insert(index, (label, spacer, background))
self.updateItems()
def removeItem(self, index):
label, spacer, background = self.items.pop(index)
label.deleteLater()
spacer.deleteLater()
layout = self.layout()
for i, (label, spacer, _) in enumerate(self.items):
layout.insertWidget(i * 2, label)
layout.insertWidget(i * 2 + 1, spacer)
self.updateItems()
self.updateGeometry()
def updateItems(self):
if not self.items:
return
size = self.fontMetrics().height()
if self.vMargin < 0:
vSize = size * 2
else:
vSize = size + self.vMargin * 2
spacing = vSize / 2
self.setMinimumHeight(vSize)
if self.hMargin >= 0:
labelMargin = self.hMargin * 2
else:
labelMargin = size // 2
it = iter(self.items)
prevBackground = prevSpacer = None
while True:
try:
label, spacer, background = next(it)
label.setContentsMargins(labelMargin, 0, labelMargin, 0)
spacer.setFixedWidth(spacing)
except StopIteration:
background = QColor()
break
finally:
if prevBackground:
if background.isValid():
cssBackground = background.name(QColor.HexArgb)
else:
cssBackground = 'none'
if prevBackground.alpha():
prevBackground = prevBackground.name(QColor.HexArgb)
else:
mid = QColor(prevBackground)
mid.setAlphaF(.5)
prevBackground = '''
qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop:0 {}, stop:1 {})
'''.format(
prevBackground.name(QColor.HexArgb),
mid.name(QColor.HexArgb),
)
prevSpacer.setStyleSheet('''
ArrowMenu > .QWidget#menuArrow {{
background: transparent;
border-top: {size}px solid {background};
border-bottom: {size}px solid {background};
border-left: {spacing}px solid {prevBackground};
}}
'''.format(
size=self.height() // 2,
spacing=spacing,
prevBackground=prevBackground,
background=cssBackground
))
prevBackground = background
prevSpacer = spacer
def resizeEvent(self, event):
self.updateItems()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
items = (
('Python', 'green'),
('Will delete', 'chocolate'),
('PyQt5', 'red'),
('Java', 'blue'),
('ASP.Net', 'yellow'),
)
ex = ArrowMenu(items)
ex.show()
QTimer.singleShot(2000, lambda: ex.addItem('New item', 'aqua'))
QTimer.singleShot(5000, lambda: ex.removeItem(1))
sys.exit(app.exec_())
And here is the result:
import sys
from PyQt5.QtWidgets import QWidget,QHBoxLayout,QLabel,QFrame,QApplication,QSizePolicy
from PyQt5.QtCore import Qt
class MyFrame(QWidget):
def __init__(self,base_color,top_color,width,edge,text,text_color):
super().__init__()
self.base_color = base_color
self.top_color = top_color
self.width = width
self.edge = edge
self.text = text
self.text_color = text_color
self.lbl = QLabel()
self.lbl.setText(self.text)
self.lbl.setFixedHeight(self.width*2)
self.lbl.setMinimumWidth((QSizePolicy.MinimumExpanding)+100)
self.lbl.setContentsMargins(0,0,0,0)
self.lbl.setAlignment(Qt.AlignCenter)
self.lbl.setStyleSheet(f"QLabel"
f"{{background-color: {self.base_color};"
f"color:{self.text_color};"
f"font-family:Trebuchet MS;"
f"font-size: 15pt;}}")
self.frame_triangle = QFrame()
self.frame_triangle.setFixedSize(self.width, self.width * 2)
self.frame_triangle.setContentsMargins(0,0,0,0)
self.hbox = QHBoxLayout()
self.hbox.setSpacing(0)
self.hbox.setContentsMargins(0,0,0,0)
self.setLayout(self.hbox)
if self.edge == "right":
self.border = "border-left"
self.hbox.addWidget(self.lbl)
self.hbox.addWidget(self.frame_triangle)
elif self.edge == "left":
self.border = "border-right"
self.hbox.addWidget(self.frame_triangle)
self.hbox.addWidget(self.lbl)
elif self.edge == "none":
self.border = "border-right"
self.hbox.addWidget(self.lbl)
self.lbl.setMinimumWidth((QSizePolicy.MinimumExpanding) + 150)
self.frame_triangle.setStyleSheet(f"QFrame"
f"{{background-color: {self.base_color};"
f"border-top:100px solid {self.top_color};"
f"{self.border}:100px solid {self.base_color};"
f"border-bottom:100px solid {self.top_color};"
f"}}")
class Main_Frame(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Angled Frame")
triangle_size = 50
self.frame1 = MyFrame("lightgrey","green",triangle_size,"right","","lightgrey")
self.frame2 = MyFrame("green","red",triangle_size,"right","Python","white")
self.frame3 = MyFrame("red","blue",triangle_size,"right","PyQt5","white")
self.frame4 = MyFrame("blue","yellow",triangle_size,"right","Java","white")
self.frame5 = MyFrame("yellow","lightgrey",triangle_size,"right","ASP.Net","black")
self.frame_overall = QFrame()
self.frame_overall.setStyleSheet("background-color:lightgrey;")
self.frame_overall.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Maximum)
self.frame_overall_layout = QHBoxLayout(self.frame_overall)
self.frame_overall_layout.setSpacing(0)
# self.frame_overall_layout.addWidget(self.frame1)
self.frame_overall_layout.addWidget(self.frame2)
self.frame_overall_layout.addWidget(self.frame3)
self.frame_overall_layout.addWidget(self.frame4)
self.frame_overall_layout.addWidget(self.frame5)
self.vbox = QHBoxLayout()
self.vbox.setContentsMargins(0,0,0,0)
self.vbox.setSpacing(0)
self.vbox.addStretch()
self.vbox.addWidget(self.frame_overall)
self.vbox.addStretch()
self.setLayout(self.vbox)
def main():
app = QApplication(sys.argv)
ex = Main_Frame()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
It seems that this link can anwser your question. However, I adopt a python version for you.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QColor, QPainter, QPen, QPainterPath, QBrush
class Angle_Border(QWidget):
def __init__(self, firstButtonX, firstButtonY, buttonWidth, buttonHeight, triangleWidth, labels, colors):
super().__init__()
self.firstButtonX = firstButtonX
self.firstButtonY = firstButtonY
self.buttonWidth = buttonWidth
self.buttonHeight = buttonHeight
self.triangleWidth = triangleWidth
self.labels = labels
self.colors = colors
self.button_lists = []
for i, text_i in enumerate(self.labels):
button_i = QPushButton(text_i, self)
self.button_lists.append(button_i)
button_i.setGeometry(self.firstButtonX + (self.buttonWidth+self.triangleWidth)*i, self.firstButtonY,
self.buttonWidth, self.buttonHeight)
button_i.setStyleSheet("background-color: %s;border-style: outset;border-width: 0px;" % (QColor(self.colors[i]).name()))
# button_i.setStyleSheet("border-style: outset;border-width: 0px;")
def paintEvent(self, event):
super().paintEvent(event)
painter = QPainter(self)
for i, button_i in enumerate(self.button_lists):
x = button_i.pos().x()
y = button_i.pos().y()
w = button_i.width()
h = button_i.height()
r = QRect(x+w, y, self.triangleWidth, h)
#
# _____p1
# | \ p3
# |_____ /
# p2
point3X = x + w + self.triangleWidth
point3Y = y + h/2
point1X = x + w
point1Y = y
point2X = x + w
point2Y = y + h
path = QPainterPath()
path.moveTo(point1X, point1Y)
path.lineTo(point2X, point2Y)
path.lineTo(point3X, point3Y)
painter.setPen(QPen(Qt.NoPen))
if i != len(self.button_lists) - 1:
painter.fillRect(r, QBrush(self.colors[i+1]))
painter.fillPath(path, QBrush(self.colors[i]))
def main():
app = QApplication(sys.argv)
firstButtonX = 0
firstButtonY = 0
buttonWidth = 50
buttonHeight = 30
triangleWidth = 30
labels = ["step1", "step2", "step3"]
colors = [Qt.red, Qt.blue, Qt.yellow]
ex = Angle_Border(firstButtonX, firstButtonY, buttonWidth, buttonHeight, triangleWidth, labels, colors)
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Which gives:
You can use QTabBar and override its paint event.
For better display of the last tab, we also override the size hint functions in order to have enough space to show the last arrow without clipping it or drawing over the text.
class ArrowTabBar(QTabBar):
def sizeHint(self):
hint = super().sizeHint()
if self.count():
hint.setWidth(hint.width() + hint.height() * .2)
return hint
def minimumTabSizeHint(self, index):
hint = super().minimumTabSizeHint(index)
if index == self.count() - 1:
hint.setWidth(hint.width() + hint.height() * .2)
return hint
def tabSizeHint(self, index):
hint = super().tabSizeHint(index)
if index == self.count() - 1:
hint.setWidth(hint.width() + hint.height() * .2)
return hint
def paintEvent(self, event):
count = self.count()
if not count:
return
qp = QPainter(self)
qp.setRenderHint(qp.Antialiasing)
bottom = self.height()
midY = bottom // 2
midX = midY / 2.5
bottom -= 1
palette = self.palette()
textColor = palette.windowText().color()
normal = palette.mid()
current = palette.dark()
for i in range(count):
rect = self.tabRect(i)
path = QPainterPath()
x = rect.x()
right = rect.right()
if i:
path.moveTo(x - midX, bottom)
path.lineTo(x + midX, midY)
path.lineTo(x - midX, 0)
else:
path.moveTo(x, bottom)
path.lineTo(x, 0)
path.lineTo(right - midX, 0)
path.lineTo(right + midX, midY)
path.lineTo(right - midX, bottom)
if i == self.currentIndex():
qp.setBrush(current)
else:
qp.setBrush(normal)
qp.setPen(Qt.NoPen)
qp.drawPath(path)
qp.setPen(textColor)
qp.drawText(rect, Qt.AlignCenter|Qt.TextShowMnemonic,
self.tabText(i))
app = QApplication([])
panel = ArrowTabBar()
for i in range(5):
panel.addTab('Item {}'.format(i + 1))
panel.show()
app.exec()
I am coding an app to solve electrical circuits and I need to model the circuit. To do so, I need to draw Resistors and other shapes on a scene, being able to move it and so on.
The thing is that I am tryng to show a resistor en the scene and I can't find the way. I am tryng by using QGraphicsPathItem but looking at the documentation I am not capable of doing it. I'll show a bit of the code I am writing to solve this part of the app:
1. The first code I show is the Resistor as it should be shown
2. The second part is the way I want to do it, but instead an ellipsis, I want to show a Resistor
### 1st Code
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtCore import Qt
x0 = 100
y0 = 50
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.x1 = 12
self.y1 = 33
self.y2 = 18
self.y3 = 15
self.y4 = 9
self.y5 = 3
self.p1 = QtCore.QPoint(0, 0 + self.y1)
self.p2 = QtCore.QPoint(0, 0 + self.y2)
self.p3 = QtCore.QPoint(self.x1, self.y3)
self.p4 = QtCore.QPoint(-self.x1, self.y4)
self.p5 = QtCore.QPoint(self.x1, self.y5)
self.p6 = QtCore.QPoint(-self.x1, -self.y5)
self.p7 = QtCore.QPoint(self.x1, -self.y4)
self.p8 = QtCore.QPoint(-self.x1, -self.y3)
self.p9 = QtCore.QPoint(0, 0 - self.y2)
self.p10 = QtCore.QPoint(0, 0 - self.y1)
def draw_resistor(self,angle=0, x0=0, y0=0):
self.x0 = x0
self.y0 = y0
self.label = QtWidgets.QLabel()
self.canvas = QtGui.QPixmap(200, 100) # This is to create the canvas
self.canvas.fill() # To set the canvas background color to white. If not, we will only see a black frame
self.label.setPixmap(self.canvas)
self.setCentralWidget(self.label)
self.painter = QtGui.QPainter(self.label.pixmap())
self.painter.translate(self.x0,self.y0) # To change the axis origin
self.painter.rotate(angle)
self.painter.drawLines(self.p1,self.p2,self.p2,self.p3,self.p3,self.p4,self.p4,self.p5,self.p5,
self.p6,self.p6,self.p7,self.p7,self.p8,self.p8,self.p9,self.p9,self.p10)
self.painter.end()
def rotate(self,angle=0):
self.painter.rotate(angle)
self.painter.drawLines(self.p1,self.p2,self.p2,self.p3,self.p3,self.p4,self.p4,self.p5,self.p5,
self.p6,self.p6,self.p7,self.p7,self.p8,self.p8,self.p9,self.p9,self.p10)
self.label.update() # Research about this, it could be the key
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
window.draw_resistor(45,x0,y0)
app.exec_()
###################
###################
###################
###################
### 2nd Code
import sys
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QGraphicsPathItem, QGraphicsView, QGraphicsScene, QGraphicsEllipseItem, QLabel
from PyQt5.QtCore import Qt, QPointF, QRectF, QPoint
from PyQt5.QtGui import QPixmap, QPainter
class MovingObject(QGraphicsEllipseItem):
def __init__(self, x, y, r):
super().__init__(0, 0, r, r)
self.setPos(x, y)
self.setBrush(Qt.blue)
self.setAcceptHoverEvents(True)
# Mouse hover events
def hoverEnterEvent(self, event):
app.instance().setOverrideCursor(Qt.OpenHandCursor)
def hoverLeaveEvent(self, event):
app.instance().restoreOverrideCursor()
# Mouse click events
def mousePressEvent(self, event):
pass
def mouseMoveEvent(self, event):
orig_cursor_position = event.lastScenePos()
updated_cursor_position = event.scenePos()
orig_position = self.scenePos()
updated_cursor_x = updated_cursor_position.x() - orig_cursor_position.x() + orig_position.x()
updated_cursor_y = updated_cursor_position.y() - orig_cursor_position.y() + orig_position.y()
self.setPos(QPointF(updated_cursor_x, updated_cursor_y))
def mouseReleaseEvent(self, event):
print("x: {0}, y: {1}".format(self.pos().x(), self.pos().y()))
class GraphicView(QGraphicsView):
def __init__(self):
super().__init__()
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.setSceneRect(0, 0, 1200, 1000)
self.moveObject = MovingObject(50, 50, 40)
self.moveObject2 = MovingObject(100, 100, 100)
self.scene.addItem(self.moveObject)
self.scene.addItem(self.moveObject2)
app = QApplication(sys.argv)
view = GraphicView()
view.show()
sys.exit(app.exec_())
I solved it, this is the solution:
class Resistor(QGraphicsPathItem):
def __init__(self, x, y):
super(Resistor, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.setAcceptHoverEvents(True)
self.isSelected = False
self.setPath(self.create_path())
self.setPos(x, y)
def create_path(self):
path = QPainterPath()
path.moveTo(0, 33)
path.lineTo(0, 18)
path.lineTo(12, 15)
path.lineTo(-12, 9)
path.lineTo(12, 3)
path.lineTo(-12, -3)
path.lineTo(12, -9)
path.lineTo(-12, -15)
path.lineTo(0, -18)
path.lineTo(0, -33)
return path
I want to draw some lines and points by PySide2 and I followed the documentations and provide code below, but it is not showing any thing after I call the function.
class Window2(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Deformed Structure")
self.DrawWindows = QtGui.QWindow()
self.DrawButton23 = QPushButton('Draw', self)
self.DrawButton23.setStyleSheet("Background-color: orange")
self.DrawButton23.move(100, 200)
self.DrawButton23.show()
self.DrawButton23.clicked.connect(self.PaintEvent)
def PaintEvent(self, painter):
painter = QtGui.QPainter()
painter.begin(self)
pen = QPen(Qt.green)
painter.setPen(pen)
for i in range(0, 10):
x0 = i * 30
y0 = i * 30
x1 = 100 + i * 50
y1 = 100 + i * 50
point1 = QPointF(x0, y0)
point2 = QPointF(x1, y1)
line1 = QLineF(point1, point2)
painter.drawPoint(point1)
painter.drawLine(line1)
print("OK123") #Just to check the loop, it prints 10 time
painter.end()
You must understand that:
Python and C++ are case sensitive so paintEvent is different from PaintEvent.
You should not invoke paintEvent directly but using the update() or repaint() method.
From what I understand is that you want the painting to be executed when you press the button but you cannot control the painting directly, the logic is to activate a certain part of the painting using some flag.
Considering the above, the solution is:
from PySide2 import QtCore, QtGui, QtWidgets
class Window2(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Deformed Structure")
self.flag = False
self.draw_button = QtWidgets.QPushButton("Draw", self)
self.draw_button.setStyleSheet("Background-color: orange")
self.draw_button.move(100, 200)
self.draw_button.clicked.connect(self.on_clicked)
def on_clicked(self):
self.flag = True
self.update()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
if self.flag:
pen = QtGui.QPen(QtCore.Qt.green)
painter.setPen(pen)
for i in range(0, 10):
x0 = i * 30
y0 = i * 30
x1 = 100 + i * 50
y1 = 100 + i * 50
point1 = QtCore.QPointF(x0, y0)
point2 = QtCore.QPointF(x1, y1)
line1 = QtCore.QLineF(point1, point2)
painter.drawPoint(point1)
painter.drawLine(line1)
if __name__ == "__main__":
app = QtWidgets.QApplication()
w = Window2()
w.show()
app.exec_()
I'm writing a desktop widget that performs the function of a system monitor. Using a QPainter I draw an arc which represents a graphical representation of a cpu usage level. Every second a paintevent redraw this arc with a span angle based on a cpu_percent() function value.
The result is a jerk transition between new and previous level representations. I'd like to use a QPropertyAnimation to create a smooth easing arc animation. Unfortunately I don't know the propeties I should use. I'd be glad if you tell me how to do it in a proper way.
Here's a widget class that I use:
from PySide2 import QtWidgets, QtCore, QtGui
from psutil import cpu_percent
class cpu_diagram(QtWidgets.QWidget):
def __init__(self, parent=None):
super(cpu_diagram, self).__init__()
self.resize(600, 600) # todo
# color constants
self.dark = "#3B3A44"
self.light = "#4A4953"
self.color = "#75ECB5"
# text constants
self.module_name = "CPU"
self.postfix = "average"
# timer with an interval of 1 sec
self.timer = QtCore.QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.update)
self.timer.start()
def paintEvent(self, event:QtGui.QPaintEvent):
# get cpu usage
self.percent = cpu_percent()
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# draw base
basic_rect = self.rect().adjusted(20, 20, -20, -20)
painter.setBrush(QtGui.QBrush(QtGui.QColor(self.dark)))
painter.drawEllipse(basic_rect)
# draw arc
pen = QtGui.QPen(QtGui.QColor(self.light))
pen.setWidth(40)
painter.setPen(pen)
arc_rect = basic_rect.adjusted(40, 40, -40, -40)
painter.drawEllipse(arc_rect)
# draw active arc
pen.setColor(QtGui.QColor(self.color))
start_angle = 90
span_angle = self.percent_to_angle(self.percent)
painter.setPen(pen)
painter.drawArc(arc_rect, start_angle * 16, span_angle * 16)
# draw text
# draw module name
painter.setPen(QtGui.QPen(QtGui.QColor(QtCore.Qt.white)))
font = QtGui.QFont()
font.setPixelSize(110)
painter.setFont(font)
arc_rect.moveTop(-25)
painter.drawText(arc_rect, QtCore.Qt.AlignCenter, self.module_name)
# draw postfix
font = QtGui.QFont()
font.setPixelSize(60)
painter.setFont(font)
arc_rect.moveTop(-125)
painter.drawText(arc_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, self.postfix)
# draw percents
arc_rect.moveBottom(460)
painter.setPen(QtGui.QPen(self.color))
painter.drawText(arc_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, f"{str(self.percent)} %")
def percent_to_angle(self, percent):
return -percent / 100 * 360
You have to create a QProperty that represents the percentage and use it in the QPropertyAnimation.
from PySide2 import QtWidgets, QtCore, QtGui
from psutil import cpu_percent
class CpuDiagram(QtWidgets.QWidget):
percentChanged = QtCore.Signal(float)
def __init__(self, parent=None):
super().__init__(parent)
self.resize(600, 600) # todo
# color constants
self.dark = "#3B3A44"
self.light = "#4A4953"
self.color = "#75ECB5"
# text constants
self.module_name = "CPU"
self.postfix = "average"
# timer with an interval of 1 sec
self.timer = QtCore.QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.onTimeout)
self.timer.start()
self._percent = 0
self._animation = QtCore.QPropertyAnimation(self, b"percent", duration=400)
self.percentChanged.connect(self.update)
#QtCore.Slot()
def onTimeout(self):
start_value = self.percent
end_value = cpu_percent()
self._animation.setStartValue(start_value)
self._animation.setEndValue(end_value)
self._animation.start()
def get_percent(self):
return self._percent
def set_percent(self, p):
if self._percent != p:
self._percent = p
self.percentChanged.emit(p)
percent = QtCore.Property(
float, fget=get_percent, fset=set_percent, notify=percentChanged
)
def paintEvent(self, event: QtGui.QPaintEvent):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# draw base
basic_rect = self.rect().adjusted(20, 20, -20, -20)
painter.setBrush(QtGui.QBrush(QtGui.QColor(self.dark)))
painter.drawEllipse(basic_rect)
# draw arc
pen = QtGui.QPen(QtGui.QColor(self.light))
pen.setWidth(40)
painter.setPen(pen)
arc_rect = basic_rect.adjusted(40, 40, -40, -40)
painter.drawEllipse(arc_rect)
# draw active arc
pen.setColor(QtGui.QColor(self.color))
start_angle = 90
span_angle = self.percent_to_angle(self.percent)
painter.setPen(pen)
painter.drawArc(arc_rect, start_angle * 16, span_angle * 16)
# draw text
# draw module name
painter.setPen(QtGui.QPen(QtGui.QColor(QtCore.Qt.white)))
font = QtGui.QFont()
font.setPixelSize(110)
painter.setFont(font)
arc_rect.moveTop(-25)
painter.drawText(arc_rect, QtCore.Qt.AlignCenter, self.module_name)
# draw postfix
font = QtGui.QFont()
font.setPixelSize(60)
painter.setFont(font)
arc_rect.moveTop(-125)
painter.drawText(
arc_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom, self.postfix
)
# draw percents
arc_rect.moveBottom(460)
painter.setPen(QtGui.QPen(self.color))
painter.drawText(
arc_rect,
QtCore.Qt.AlignCenter | QtCore.Qt.AlignBottom,
f"{self.percent:.2f} %",
)
def percent_to_angle(self, percent):
return -percent / 100 * 360
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = CpuDiagram()
w.show()
sys.exit(app.exec_())
I need to adjust verticalScrollBar() on mouse wheel event. Trying to get the same behavior as horisontalScrollBar(). I mean it should remain in center of vertical scroll area.
Here is the code:
#!/usr/bin/env python
from PySide.QtGui import *
class windowClass(QWidget):
def __init__(self):
super(windowClass, self).__init__()
self.ly = QVBoxLayout(self)
self.view = viewClass()
self.ly.addWidget(self.view)
self.resize(500, 200)
class sceneClass(QGraphicsScene):
def __init__(self):
super(sceneClass, self).__init__()
self.setSceneRect(-1000, -1000, 2000, 2000)
self.grid = 30
class viewClass(QGraphicsView):
def __init__(self):
super(viewClass, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
# self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.s = sceneClass()
self.setScene(self.s)
self.scaleY = 1
self.scaleX = 1
def wheelEvent(self, event):
self.setSceneScale(event.delta())
super(viewClass, self).wheelEvent(event)
def setSceneScale(self, delta):
if delta > 0:
self.scale(self.scaleX + 0.1, self.scaleY + 0.1)
else:
self.scale(self.scaleX - 0.1, self.scaleY - 0.1)
if __name__ == '__main__':
app = QApplication([])
w = windowClass()
w.show()
app.exec_()
As you can see I already used setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) but it did not worked because it just hides scroll bars
I've got the answer.
In constructor of QGraphicsView :
self.vscr = (self.size().height()/2)*-1
In wheelEvent :
self.verticalScrollBar().setValue(self.vscr)