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
Related
I think there is nothing wrong in my code, but no way to make that rectangle (last line) appear in my scene...
Here is the main file:
import sys
from PyQt5.QtWidgets import *
from node_editor import NodeEditor
if __name__ == '__main__':
app = QApplication(sys.argv)
window = NodeEditor()
sys.exit(app.exec_())
NodeEditor() is this file:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from node_graphics_scene import QDMGraphicsScene
class NodeEditor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.initUI()
def initUI(self):
self.setGeometry(200, 200, 800, 600)
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
#Graphics scene
self.grScene = QDMGraphicsScene()
#Graphics view
self.view = QGraphicsView(self)
self.view.setScene(self.grScene)
self.layout.addWidget(self.view)
self.setWindowTitle("KOMAGOMA")
self.show()
self.addDebugContent()
def addDebugContent(self):
greenBrush = QBrush(Qt.green)
outlinePen = QPen(Qt.black)
outlinePen.setWidth(2)
rect = self.grScene.addRect(-100, -100, 80, 100, outlinePen, greenBrush)
rect.setFlag(QGraphicsItem.ItemIsMovable)
QDMGraphicsScene is this file:
import math
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class QDMGraphicsScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
#Settings
self.gridSize = 20
self.gridSquares = 5
self._color_background = QColor('#1E1E1E')
self._color_dark = QColor('#151414')
self._color_light = QColor('#151414')
self._pen_light = QPen(self._color_light)
self._pen_light.setWidth(1)
self._pen_dark = QPen(self._color_dark)
self._pen_dark.setWidth(2)
self.scene_width, self.scene_height = 64000, 64000
self.setSceneRect(-self.scene_width//2, self.scene_height//2, self.scene_width, self.scene_height)
self.setBackgroundBrush(self._color_background)
def drawBackground(self, painter, rect):
super().drawBackground(painter, rect)
#Create grid
left = int(math.floor(rect.left()))
right = int(math.ceil(rect.right()))
top = int(math.floor(rect.top()))
bottom = int(math.ceil(rect.bottom()))
first_left = left - (left % self.gridSize)
first_top = top - (top % self.gridSize)
#Compute lines to be drawn
lines_dark, lines_light = [], []
for x in range(first_left, right, self.gridSize):
if(x % (self.gridSize * self.gridSquares) != 0): lines_light.append(QLine(x, top, x, bottom))
else: lines_dark.append(QLine(x, top, x, bottom))
for y in range(first_top, bottom, self.gridSize):
if(y % (self.gridSize * self.gridSquares) != 0): lines_light.append(QLine(left, y, right, y))
else: lines_dark.append(QLine(left, y, right, y))
#Draw the lines
painter.setPen(self._pen_light)
painter.drawLines(*lines_light)
painter.setPen(self._pen_dark)
painter.drawLines(*lines_dark)
I'm following a tutorial to learn PyQt5.
I follow all along but in my case it doesn't work.
Maybe it had an update and this is not the right way of doing it now?
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()
I am trying to create a basic 25 key keyboard in pyqt, I have laid out the 15 white keys but am struggling with how to add the 10 remaining black keys,
This is how I made my keys
from PyQt5.QtWidgets import QApplication, QPushButton
app = QApplication([])
top_win = QWidget()
set_color(top_win, Qt.cyan)
top_win.setAutoFillBackground(True)
top_win.show()
top_win.resize(1920,1080)
top_win.setWindowTitle("Synth-01")
top_vlayout = QVBoxLayout()
top_win.setLayout(top_vlayout)
keyboard = QWidget()
keyboard.setMaximumWidth(1410)
top_vlayout.addWidget(keyboard)
keyboard_layout = QHBoxLayout()
keyboard.setAutoFillBackground(True)
keyboard.setLayout(keyboard_layout)
for i in range(15):
name = "key_" + str(i)
name = QPushButton()
name.setMaximumWidth(94)
name.setMaximumHeight(349)
keyboard_layout.addWidget(name)
I now want to add the black keys inbetween like this
In this case, it is most preferable to use QGraphicsScene with QGraphicsItem, for this case I will use svg:
from PyQt5 import QtCore, QtGui, QtWidgets, QtSvg
class PianoKey(QtWidgets.QGraphicsRectItem):
def __init__(self, black=False, rect = QtCore.QRectF(), parent=None):
super(PianoKey, self).__init__(rect, parent)
self.m_pressed = False
self.m_selectedBrush = QtGui.QBrush()
self.m_brush = QtGui.QBrush(QtCore.Qt.black) if black else QtGui.QBrush(QtCore.Qt.white)
self.m_black = black
def setPressedBrush(self, brush):
self.m_selectedBrush = brush
def paint(self, painter, option, widget):
rendered = QtSvg.QSvgRenderer("key.svg")
black_pen = QtGui.QPen(QtCore.Qt.black, 1)
gray_pen = QtGui.QPen(QtGui.QBrush(QtCore.Qt.gray), 1,
QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
if self.m_pressed:
if self.m_selectedBrush.style() != QtCore.Qt.NoBrush:
painter.setBrush(self.m_selectedBrush)
else:
painter.setBrush(QtWidgets.QApplication.palette().highlight())
else:
painter.setBrush(self.m_brush);
painter.setPen(black_pen)
painter.drawRoundedRect(self.rect(), 15, 15, QtCore.Qt.RelativeSize)
if self.m_black:
rendered.render(painter, self.rect())
else:
points = [
QtCore.QPointF(self.rect().left()+1.5, self.rect().bottom()-1),
QtCore.QPointF(self.rect().right()-1, self.rect().bottom()-1),
QtCore.QPointF(self.rect().right()-1, self.rect().top()+1)
]
painter.setPen(gray_pen)
painter.drawPolyline(QtGui.QPolygonF(points))
def mousePressEvent(self, event):
self.m_pressed = True
self.update()
super(PianoKey, self).mousePressEvent(event)
event.accept()
def mouseReleaseEvent(self, event):
self.m_pressed = False
self.update()
super(PianoKey, self).mouseReleaseEvent(event)
KEYWIDTH, KEYHEIGHT = 18, 72
class PianoKeyBoard(QtWidgets.QGraphicsView):
def __init__(self, num_octaves=2, parent=None):
super(PianoKeyBoard, self).__init__(parent)
self.initialize()
self.m_numOctaves = num_octaves
scene = QtWidgets.QGraphicsScene(QtCore.QRectF(0, 0, KEYWIDTH * self.m_numOctaves * 7, KEYHEIGHT), self)
self.setScene(scene)
numkeys = self.m_numOctaves * 12
for i in range(numkeys):
octave = i//12*7
j = i % 12
if j >= 5: j += 1
if j % 2 == 0:
x = (octave + j/2)*KEYWIDTH
key = PianoKey(rect=QtCore.QRectF(x, 0, KEYWIDTH, KEYHEIGHT), black=False)
else:
x = (octave + j//2) * KEYWIDTH + KEYWIDTH * 6//10 + 1
key = PianoKey(rect=QtCore.QRectF(x, 0, KEYWIDTH * 8//10 - 1, KEYHEIGHT * 6//10 ), black=True)
key.setZValue(1)
key.setPressedBrush(QtWidgets.QApplication.palette().highlight())
self.scene().addItem(key)
def initialize(self):
self.setAttribute(QtCore.Qt.WA_InputMethodEnabled, False)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
self.setViewportUpdateMode(QtWidgets.QGraphicsView.MinimalViewportUpdate)
self.setRenderHints(QtGui.QPainter.Antialiasing|
QtGui.QPainter.TextAntialiasing |
QtGui.QPainter.SmoothPixmapTransform)
self.setOptimizationFlag(QtWidgets.QGraphicsView.DontClipPainter, True)
self.setOptimizationFlag(QtWidgets.QGraphicsView.DontSavePainterState, True)
self.setOptimizationFlag(QtWidgets.QGraphicsView.DontAdjustForAntialiasing, True)
self.setBackgroundBrush(QtWidgets.QApplication.palette().base())
def resizeEvent(self, event):
super(PianoKeyBoard, self).resizeEvent(event)
self.fitInView(self.scene().sceneRect(), QtCore.Qt.KeepAspectRatio)
def sizeHint(self):
return self.mapFromScene(self.sceneRect()).boundingRect().size()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle('fusion')
w = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(w)
lay.addWidget(QtWidgets.QLabel("Piano Keyboard", alignment=QtCore.Qt.AlignCenter))
lay.addWidget(PianoKeyBoard())
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
The complete code + key.svg can be found on this link
I tried to create a 2D plot using QGraphicsItem, I was successful in doing that but when I drag the QGraphicsItem there is a delay and the view is distorted.
Searching for a solution, I came across this QGraphicsItem paint delay. I applied the mouseMoveEvent to my QGraphicsView but it did not resolve the problem.
Could someone tell me what is causing the problem and how can I fix it?
Here is my code:
from PyQt4 import QtGui, QtCore
import sys
import numpy as np
class MyGraphicsItem(QtGui.QGraphicsItem):
def __init__(self,dataX,dataY):
super(MyGraphicsItem,self).__init__()
self.Xval = dataX
self.Yval = dataY
self.Xvalmin = np.min(self.Xval)
self.Xvalmax = np.max(self.Xval)
self.Yvalmin = np.min(self.Yval)
self.Yvalmax = np.max(self.Yval)
self.rect = QtCore.QRectF(0,0,100,2)
self.points = []
self._picture = None
def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None):
if self._picture is None:
self._picture = QtGui.QPicture()
QPainter.begin(self._picture)
startPoint = QtCore.QPointF(0, 0)
cubicPath = QtGui.QPainterPath()
cubicPath.moveTo(startPoint)
for i in range(len(self.points) - 2):
points_ = self.points[i:i+3]
cubicPath.cubicTo(*points_)
QPainter.setPen(QtGui.QPen(QtCore.Qt.red))
QPainter.drawLine(0,10,100,10)
QPainter.drawLine(0,-10,0,10)
QPainter.setPen(QtGui.QPen(QtCore.Qt.black))
QPainter.drawPath(cubicPath)
QPainter.end()
else:
self._picture.play(QPainter)
def boundingRect(self):
return self.rect
class mygraphicsview(QtGui.QGraphicsView):
def __init__(self):
super(mygraphicsview,self).__init__()
def mouseMoveEvent(self, event):
QtGui.QGraphicsView.mouseMoveEvent(self,event)
if self.scene().selectedItems():
self.update()
class Mainwindow(QtGui.QMainWindow):
def __init__(self):
super(Mainwindow,self).__init__()
self.main_widget = QtGui.QWidget()
self.vl = QtGui.QVBoxLayout()
self.scene = QtGui.QGraphicsScene()
self.view = mygraphicsview()
self.Xval = np.linspace(0,100,1000)
self.Yval = np.sin(self.Xval)
self.painter = QtGui.QPainter()
self.style = QtGui.QStyleOptionGraphicsItem()
self.item = MyGraphicsItem(self.Xval, self.Yval)
self.item.paint(self.painter, self.style,self.main_widget)
self.item.setFlag(QtGui.QGraphicsItem.ItemIsMovable,True)
self.trans = QtGui.QTransform()
self.trans.scale(5,5)
self.item.setTransform(self.trans)
self.scene = QtGui.QGraphicsScene()
self.scene.addItem(self.item)
self.view.setScene(self.scene)
self.vl.addWidget(self.view)
self.main_widget.setLayout(self.vl)
self.setCentralWidget(self.main_widget)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Mainwindow()
window.show()
sys.exit(app.exec_())
I fixed the issue of dragging delay.
The reason for the occurrence of such a delay is due to the boundinRect() function. The boudingRect was too tight around the item designed.
Adding some marigin to the boundingRect(), solved the problem.
self.rect = QtCore.QRectF(-10,-10,120,25)
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)