PyQt Node interface - Parrent to ItemIsMovable object - python

So I am building a node based interface using PyQt for a project I am working on and I am having some issues getting objects that belong to the base not to follow it in space. I would like when the user drags the base node, the child objects (inputs and output boxes) to follow it. I have a drag-able node that works but the child objects are not following properly. Any ideas?
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
This is the base py file for the GUI
"""
import sys
from PyQt4 import QtGui, QtCore
from array import *
"""
Base class for a node. Contains all the initialization, drawing, and containing inputs and outputs
"""
class node():
width = 100
height = 100
color = 1
x = 90
y = 60
inputs=[]
outputs=[]
def __init__(self, nWidth, nHeight):
self.width = nWidth
self.height = nHeight
self.iniNodeData()
"""
This is where inputs and outputs will be created
"""
def iniNodeData(self):
for j in range(5):
this = self
x = input(this,90, 0+(j*10))
self.inputs.append(x)
"""Draw the node then the input and output objects"""
def draw(self, drawObj):
item = drawObj.addRect(self.x, self.y, self.width, self.height)
item.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
for curInput in self.inputs:
curInput.draw(drawObj)
print("(", self.x, ",", self.y, ")")
"""
Nodes will evaluate from the last node to the first node, therefore inputs are evaluted
"""
class input():
currentConnectedNode = None
currentConnectedOutput = None
parentNode = None
width = 10
height = 10
x = 1
y = 1
color = 1
def __init__(self, pnode, posX, posY):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
def draw(self, drawObj):
item = drawObj.addRect(self.x+self.parentNode.x, self.y+self.parentNode.y, self.width, self.height)
class output():
parentNode = None
class MainWindow(QtGui.QGraphicsView):
nodes = []
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
for j in range(1):
x = node(100,100)
self.nodes.append(x)
self.setScene(QtGui.QGraphicsScene())
self.setWindowTitle('RIS RIB Generator')
self.setGeometry(800, 600, 800, 850)
self.initNodes()
self.show()
def initNodes(self):
for curNode in self.nodes:
curNode.draw(self.scene())
def main():
app = QtGui.QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
app.exec_()
if __name__ == '__main__':
main()

Ok so after a week I figured it out. You need to do a few things.
ensure the flags are correct:
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
Once those are set you can use the built in event handelers but these over ride the original ones. so from inside your user defined one, you need to call the event handeler from the base class. Example:
def mousePressEvent(self, e):
print("Square got mouse press event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
Here is my working example of my progress.
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
This is the base py file for the GUI
Todo list
-----------------
- Pop up menu for adding new Nodes
- node connectivity
- create data structure for storing
"""
import sys
from PyQt4 import QtGui, QtCore
from array import *
"""
Base class for a node. Contains all the inilization, drawing, and containing inputs and outputs
"""
class node(QtGui.QGraphicsRectItem):
width = 100
height = 100
color = 1
x = 90
y = 60
inputs=[]
outputs=[]
viewObj = None
def __init__(self, n_x, n_y, n_width,n_height):
QtGui.QGraphicsRectItem.__init__(self, n_x, n_y, n_width, n_height)
self.width = n_width
self.height = n_height
self.x = n_x
self.y = n_y
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
self.iniNodeData()
def mousePressEvent(self, e):
print("Square got mouse press event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
def mouseReleaseEvent(self, e):
print("Square got mouse release event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsRectItem.mouseReleaseEvent(self, e)
"""
This is where inputs and outputs will be created based on node type
"""
def iniNodeData(self):
print('making node data')
for j in range(5):
this = self
x = input(this,0, 0+(j*10))
self.inputs.append(x)
for k in range(5):
this = self
x = output(this,self.x+self.width, self.y+(k*10))
self.outputs.append(x)
def mouseMoveEvent(self, event):
print('Dragging#')
QtGui.QGraphicsRectItem.mouseMoveEvent(self, event)
def mousePressEvent(self, event):
print('moving!')
"""
Nodes will evaluate from the last node to the first node, therefore inputs are evaluted
"""
class input(QtGui.QGraphicsRectItem):
currentConnectedNode = None
currentConnectedOutput = None
parentNode = None
width = 10
height = 10
x = 1
y = 1
color = 1
drawItem = None
def __init__(self, pnode, posX, posY):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
QtGui.QGraphicsRectItem.__init__(self, self.x+self.parentNode.x, self.y+self.parentNode.y, self.width, self.height, self.parentNode)
'''
Output value from a node
'''
class output(node):
parentNode = None
width = 10
height = 10
x = 1
y = 1
def __init__(self, pnode, posX, posY):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
QtGui.QGraphicsRectItem.__init__(self, self.x-self.width, self.y, self.width, self.height, self.parentNode)
'''
Check Click events on the scene Object
'''
class Scene(QtGui.QGraphicsScene):
nodes = []
def mousePressEvent(self, e):
print("Scene got mouse press event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsScene.mousePressEvent(self, e)
def mouseReleaseEvent(self, e):
print("Scene got mouse release event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsScene.mouseReleaseEvent(self, e)
def dragMoveEvent(self, e):
print('Scene got drag move event')
def addNode(self):
newNode = self.addItem(node(250,250,100,150))
self.nodes.append(newNode)
'''
Main Window Object
'''
class MainWindowUi(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setWindowTitle('RIS RIB Generator')
self.scene = Scene(0, 0, 800, 850, self)
self.view = QtGui.QGraphicsView()
self.setCentralWidget(self.view)
self.view.setScene(self.scene)
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close)
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'New Node', self)
newNodeAction.setStatusTip('Add a blank node')
newNodeAction.triggered.connect(self.scene.addNode)
self.statusBar()
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newNodeAction)
fileMenu.addAction(exitAction)
'''
Start Point
'''
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindowUi()
win.show()
sys.exit(app.exec_())

Related

Transparent widget problem over a vlc video on

I am trying to display transparent label over video output using PyQt and vlc library. Although I can put my label on video, the part under the label is not visible as if there is no video underneath. By the way, target platform is Windows. Also, I think the problem is setting media player to video frame window id. However, if I remove the line 'self.mediaplayer.set_hwnd(int(self.videoframe.winId()))', the video would be displayed in it's own window not my player window.
My attempt looks like this:
.
My code is attached below:
import platform
import sys
import vlc
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
class Player(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Media Player")
# Create a basic vlc instance
self.instance = vlc.Instance()
self.media = None
# Create an empty vlc media player
self.mediaplayer = self.instance.media_player_new()
self.widget = QtWidgets.QWidget(self)
self.setCentralWidget(self.widget)
self.lbl = QtWidgets.QLabel()
font = QFont('Helvetica', 16, QFont.Bold)
self.lbl.setFont(font)
self.lbl.setText("This is my\ntransparent\nlabel.")
self.lbl.setAlignment(Qt.AlignCenter)
self.lbl.setFixedSize(200,200)
self.videoframe = QtWidgets.QFrame(frameShape=QtWidgets.QFrame.Box, frameShadow=QtWidgets.QFrame.Raised)
self.videoframe.setFixedSize(600,600)
self.mediaplayer.set_hwnd(int(self.videoframe.winId()))
fileName = "C:\\Users\\...\\test.mp4" #dir of the video
self.media = self.instance.media_new(fileName)
# Put the media in the media player
self.mediaplayer.set_media(self.media)
# Parse the metadata of the file
self.media.parse()
self.vboxlayout = QtWidgets.QGridLayout()
self.vboxlayout.addWidget(self.videoframe,0,0)
self.vboxlayout.addWidget(self.lbl,0,0)
self.widget.setLayout(self.vboxlayout)
self.mediaplayer.play()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
player = Player()
player.show()
sys.exit(app.exec_())
I just had the same problem and my solution is: JUST FOUR THICK LABELS THAT DRAW A SQUARE.
here is my code:
class MouseTracker(QtCore.QObject):
positionChanged = QtCore.pyqtSignal(QtCore.QEvent)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.setMouseTracking(True)
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, o, e):
if e.type() == QtCore.QEvent.MouseButtonPress:
# print("pressed")
self.positionChanged.emit(e)
elif e.type() == QtCore.QEvent.MouseButtonRelease:
# print("release")
self.positionChanged.emit(e)
if o is self.widget and e.type() == QtCore.QEvent.MouseMove:
self.positionChanged.emit(e)
return super().eventFilter(o, e)
then the mainclass...
class MainWindow(QMainWindow):
def __init__(self, master=None):
QMainWindow.__init__(self, master)
self.setWindowTitle("Media Player")
# creating a basic vlc instance
self.instance = vlc.Instance()
# creating an empty vlc media player
self.mediaplayer = self.instance.media_player_new()
self.initUI()
self.isPaused = False
def initUI(self):
self.x0 = None
self.y0 = None
self.x1 = None
self.y1 = None
# Set up the user interface, signals & slots
self.statusBar().showMessage("Ready")
self.resize(640, 480)
self.widget = QWidget(self)
self.setCentralWidget(self.widget)
# In this widget, the video will be drawn
if sys.platform == "darwin": # for MacOS
from PyQt5.QtWidgets import QMacCocoaViewContainer
self.video_label = QMacCocoaViewContainer(0)
else:
self.video_label = QLabel()
tracker = MouseTracker(self.video_label)
tracker.positionChanged.connect(self.on_positionChanged)
self.positionslider = QSlider(Qt.Horizontal, self)
self.positionslider.setToolTip("Position")
self.positionslider.setMaximum(1000)
self.positionslider.sliderMoved.connect(self.setPosition)
#########Buttons#################
self.height_of_BTNS = 30
self.playbutton = QPushButton("Play")
self.playbutton.clicked.connect(self.PlayPause)
self.stopbutton = QPushButton("Stop")
self.stopbutton.clicked.connect(self.Stop)
self.volumeslider = QSlider(Qt.Horizontal, self)
self.volumeslider.setMaximum(100)
self.volumeslider.setValue(self.mediaplayer.audio_get_volume())
self.volumeslider.setToolTip("Volume")
self.volumeslider.valueChanged.connect(self.setVolume)
#################################
self.marker_label_top = QLabel(self)
self.marker_label_top.hide()
self.marker_label_top.raise_()
#################################
self.marker_label_btm = QLabel(self)
self.marker_label_btm.hide()
self.marker_label_btm.raise_()
#################################
self.marker_label_r = QLabel(self)
self.marker_label_r.hide()
self.marker_label_r.raise_()
#################################
self.marker_label_l = QLabel(self)
self.marker_label_l.hide()
self.marker_label_l.raise_()
self.hbuttonbox = QHBoxLayout()
self.hbuttonbox.addWidget(self.playbutton)
self.hbuttonbox.addWidget(self.stopbutton)
self.hbuttonbox.addWidget(self.volumeslider)
self.hbuttonbox.addStretch(1)
self.vboxlayout = QVBoxLayout()
self.vboxlayout.addWidget(self.video_label)
self.vboxlayout.addWidget(self.positionslider)
self.vboxlayout.addLayout(self.hbuttonbox)
self.widget.setLayout(self.vboxlayout)
##############################
self.label_position = QtWidgets.QLabel(self.video_label, alignment=QtCore.Qt.AlignCenter)
self.label_position.setStyleSheet('background-color: white; border: 1px solid black')
##############################
open = QAction("&Open", self)
open.triggered.connect(self.OpenFile)
exit = QAction("&Exit", self)
exit.triggered.connect(sys.exit)
menubar = self.menuBar()
filemenu = menubar.addMenu("&File")
filemenu.addAction(open)
filemenu.addSeparator()
filemenu.addAction(exit)
self.timer = QTimer(self)
self.timer.setInterval(200)
self.timer.timeout.connect(self.updateUI)
self.timer_1 = QTimer(self)
self.timer_1.setInterval(10000)
self.timer_1.timeout.connect(self.updateUI_info)
# Below functions must be FALSE in order to work mousetracker
self.mediaplayer.video_set_mouse_input(False)
self.mediaplayer.video_set_key_input(False)
self.show()
then the function that draw the square when the mouse button released. It actually draw 4 thick labels above the video_label. IMPORTANT TO SET FALSE the self.mediaplayer.video_set_mouse_input(False) and self.mediaplayer.video_set_key_input(False)
#QtCore.pyqtSlot(QtCore.QEvent)
def on_positionChanged(self, e):
if e.type() == QtCore.QEvent.MouseButtonPress:
self.statusBar().showMessage("Ready")
self.marker_label_top.hide()
self.marker_label_r.hide()
self.marker_label_l.hide()
self.marker_label_btm.hide()
print("Mouse press")
self.x0 = e.x()
self.y0 = e.y()
elif e.type() == QtCore.QEvent.MouseButtonRelease and self.x0 is not None and self.y0 is not None:
self.x1 = e.x()
self.y1 = e.y()
print("Mouse release")
if abs(self.x0 - self.x1) > 30 and abs(self.y0 - self.y1) > 30:
marker_width = self.x1 - self.x0
marker_height = self.y1 - self.y0
self.marker_label_top.setStyleSheet("border: 2px solid black;")
self.marker_label_top.resize(marker_width, 2)
self.marker_label_top.move(self.x0, self.y0)
self.marker_label_top.show()
self.marker_label_l.setStyleSheet("border: 2px solid black;")
self.marker_label_l.resize(2, marker_height)
self.marker_label_l.move(self.x0, self.y0)
self.marker_label_l.show()
self.marker_label_r.setStyleSheet("border: 2px solid black;")
self.marker_label_r.resize(2, marker_height)
self.marker_label_r.move(self.x1, self.y0)
self.marker_label_r.show()
self.marker_label_btm.setStyleSheet("border: 2px solid black;")
self.marker_label_btm.resize(marker_width, 2)
self.marker_label_btm.move(self.x0, self.y1)
self.marker_label_btm.show()
print(f"Values UI MOUSE TRACKER X: {self.x0} ,Y: {self.y0}, X1: {self.x1}, Y1: {self.y1}")
else:
self.statusBar().showMessage("Make it bigger")
# self.worker1.reset_values()
self.x0 = None
self.y0 = None
self.x1 = None
self.y1 = None
self.label_position.move(e.x(), e.y())
self.label_position.setText("(%d, %d)" % (e.x(), e.y()))
self.label_position.adjustSize()
self.label_position.show()
and the rest of the code
def PlayPause(self):
# Toggle play/pause status
if self.mediaplayer.is_playing():
self.mediaplayer.pause()
self.playbutton.setText("Play")
self.isPaused = True
else:
if self.mediaplayer.play() == -1:
self.OpenFile()
return
self.mediaplayer.play()
self.playbutton.setText("Pause")
self.timer.start()
self.timer_1.start()
self.isPaused = False
def Stop(self):
self.mediaplayer.stop()
self.playbutton.setText("Play")
def OpenFile(self, filename=None):
if filename == False:
filename = QFileDialog.getOpenFileName(self, "Open File", os.path.expanduser('~'))[0]
if not filename:
return
# # create the media
if sys.version < '3':
filename = vlc.unicode(filename)
self.media = self.instance.media_new(filename)
# put the media in the media player
self.mediaplayer.set_media(self.media)
# parse the metadata of the file
self.media.parse()
if sys.platform.startswith('linux'): # for Linux using the X Server
self.mediaplayer.set_xwindow(self.video_label.winId())
elif sys.platform == "win32": # for Windows
self.mediaplayer.set_hwnd(self.video_label.winId())
print(self.video_label.winId())
elif sys.platform == "darwin": # for MacOS
self.mediaplayer.set_nsobject(int(self.video_label.winId()))
self.PlayPause()
def setVolume(self, Volume):
self.mediaplayer.audio_set_volume(Volume)
def setPosition(self, position):
# setting the position to where the slider was dragged
self.mediaplayer.set_position(position / 1000.0)
def updateUI(self):
# updates the user interface
# setting the slider to the desired position
self.positionslider.setValue(int(self.mediaplayer.get_position() * 1000))
# self.mediaplayer.video_take_snapshot(num=0, psz_filepath='a.png', i_width=1920, i_height=1080)
if not self.mediaplayer.is_playing():
# no need to call this function if nothing is played
self.timer.stop()
if not self.isPaused:
# after the video finished, the play button stills shows
# "Pause", not the desired behavior of a media player
# this will fix it
self.Stop()
def updateUI_info(self):
fps = self.mediaplayer.get_fps()
sec = self.mediaplayer.get_time() / 1000
frame_no = int(round(sec * fps))
print(
f" Frame number Now: {frame_no} \n Frame Rate per Second: {fps} \n Current position in seconds {self.mediaplayer.get_time() / 1000} \n -------------------")
if __name__ == "__main__":
App = QApplication(sys.argv)
Root = MainWindow()
if sys.argv[1:]:
Root.OpenFile(sys.argv[1])
sys.exit(App.exec_())
enjoy :)

Problem with resizing custom item inside QGraphicsScene (Pyqt)

I have built a custom widget that is placed inside a custom scene. The custom scene rect resizes when the itemBoundingRect crosses the scene rect. In the beginning, the scene rectangle is set to (0, 0, 2000, 2000). I am able to resize my widgets properly inside this rectangle. The problem arises when I try to move the item against the top (i,e when the item moves in negative y-axis), The item altogether decreases the size on the y-axis when I try to increase it.
Here is the demonstration of the problem:
(Note: the scene resizes when the item is placed at any of the edges by a factor of 500. eg:- after first resize the scene rect would be (-500, -500, 2500, 2500) )
Here is the code:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QGraphicsItem, QStyle, QGraphicsView
# ---------class size grip use to increase widget size----------#
class SizeGrip(QtWidgets.QSizeGrip):
def __init__(self, parent):
super().__init__(parent)
parent.installEventFilter(self)
self.setFixedSize(30, 30)
self.polygon = QtGui.QPolygon([
QtCore.QPoint(10, 20),
QtCore.QPoint(20, 10),
QtCore.QPoint(20, 20),
])
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.Resize:
geo = self.rect()
geo.moveBottomRight(source.rect().bottomRight())
self.setGeometry(geo)
return super().eventFilter(source, event)
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setPen(QtCore.Qt.white)
qp.setBrush(QtCore.Qt.gray)
qp.drawPolygon(self.polygon)
class Container(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sizeGrip = SizeGrip(self)
self.startPos = None
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(6, 6, 6, 30)
self.setStyleSheet('''
Container {
background: lightblue;
border: 0px;
border-radius: 4px;
}
''')
def resizeEvent(self, event):
super(Container, self).resizeEvent(event)
# ------------------ Creating custom item to place in scene--------------------#
class GraphicsFrame(QtWidgets.QGraphicsWidget):
def __init__(self):
super().__init__()
graphic_layout = QtWidgets.QGraphicsLinearLayout(Qt.Vertical, self)
self.container = Container()
proxyWidget = QtWidgets.QGraphicsProxyWidget()
proxyWidget.setWidget(self.container)
graphic_layout.addItem(proxyWidget)
self.pen = QtGui.QPen()
self.pen.setColor(Qt.red)
self.container.setMinimumSize(150, 150)
self.container.setMaximumSize(400, 800)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.container.resizeEvent = lambda _: self.resize()
self.container.startPos = None
def addWidget(self, widget):
self.container.layout().addWidget(widget)
def paint(self, qp, opt, widget):
qp.save()
self.pen.setWidth(3)
p = QtGui.QPainterPath()
p.addRoundedRect(self.boundingRect().adjusted(0, 0, -.5, -.5), 4, 4)
if self.isSelected():
self.pen.setColor(Qt.yellow)
qp.setBrush(Qt.transparent)
qp.setPen(self.pen)
qp.drawPath(p)
qp.setClipPath(p)
opt.state &= ~QStyle.State_Selected
super().paint(qp, opt, widget)
qp.restore()
def resize(self):
width = self.container.size().width()
height = self.container.size().height()
rect = QtCore.QRectF(self.pos().x(), self.pos().y(), width + 22, height + 22)
self.setGeometry(rect)
# -------------------- Custom view to hold the items -----------------#
class View(QtWidgets.QGraphicsView):
context_menu_signal = QtCore.pyqtSignal()
def __init__(self, bg_color=Qt.white):
super().__init__()
self.scene = Scene()
self.setRenderHints(QtGui.QPainter.Antialiasing)
self.setDragMode(self.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
self.setCacheMode(self.CacheBackground)
self.setMouseTracking(True)
self.setScene(self.scene)
self.scene.selectionChanged.connect(self.selection_changed)
self._current_selection = []
texture = QtGui.QImage(30, 30, QtGui.QImage.Format_ARGB32)
qp = QtGui.QPainter(texture)
qp.setBrush(bg_color)
qp.setPen(QtGui.QPen(QtGui.QColor(189, 190, 191), 2))
qp.drawRect(texture.rect())
qp.end()
self.scene.setBackgroundBrush(QtGui.QBrush(texture))
self.setViewportUpdateMode(self.FullViewportUpdate) # This will avoid rendering artifacts
testFrame = GraphicsFrame()
newFrame = GraphicsFrame()
testFrame.addWidget(QtWidgets.QLineEdit())
newFrame.addWidget(QtWidgets.QLabel('Bruh'))
self.scene.addItem(testFrame)
self.scene.addItem(newFrame)
def wheelEvent(self, event):
# Save the scene pos
oldPos = self.mapToScene(event.pos())
if event.modifiers() == Qt.ControlModifier:
delta = event.angleDelta().y()
if delta > 0:
self.on_zoom_in()
elif delta < 0:
self.on_zoom_out()
# Get the new position
newPos = self.mapToScene(event.pos())
# Move scene to old position
delta = newPos - oldPos
self.translate(delta.x(), delta.y())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._mousePressed = True
if self._isPanning:
self.viewport().setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
else:
super().mousePressEvent(event)
elif event.button() == Qt.MidButton:
self._mousePressed = True
self._isPanning = True
self.viewport().setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._mousePressed and self._isPanning:
newPos = event.pos()
diff = newPos - self._dragPos
self._dragPos = newPos
self.horizontalScrollBar().setValue(
self.horizontalScrollBar().value() - diff.x()
)
self.verticalScrollBar().setValue(
self.verticalScrollBar().value() - diff.y()
)
event.accept()
else:
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if self._isPanning:
self.viewport().setCursor(Qt.OpenHandCursor)
else:
self._isPanning = False
self.viewport().unsetCursor()
self._mousePressed = False
zoomed = False if self.transform().m11() == 1.0 else True
self.scene.adjust(zoomed) # adjust the item scene rectangle
elif event.button() == Qt.MiddleButton:
self._isPanning = False
self.viewport().unsetCursor()
self._mousePressed = False
super().mouseReleaseEvent(event)
def select_items(self, items, on):
pen = QtGui.QPen(
QtGui.QColor(245, 228, 0) if on else Qt.white,
0.5,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin,
)
for item in items:
item.pen = pen
def selection_changed(self):
try:
self.select_items(self._current_selection, False)
self._current_selection = self.scene.selectedItems()
self.select_items(self._current_selection, True)
except RuntimeError:
pass
def on_zoom_in(self):
if self.transform().m11() < 2.25:
self.scale(1.5, 1.5)
def on_zoom_out(self):
if self.transform().m11() > 0.7:
self.scale(1.0 / 1.5, 1.0 / 1.5)
def resizeEvent(self, event):
super().resizeEvent(event)
# ------------Custom scene which resizes the scene rect on when the item boundary hits the scene rect---------#
class Scene(QtWidgets.QGraphicsScene):
def __init__(self):
super(Scene, self).__init__()
self.setSceneRect(0, 0, 2000, 2000)
self.sceneRect().adjust(-20, -20, 20, 20)
self.old_rect = self.itemsBoundingRect()
def adjust(self, zoomed):
w = self.sceneRect().width()
h = self.sceneRect().height()
x = self.sceneRect().x()
y = self.sceneRect().y()
adjust_factor = 500
adjust_factor2 = 200
smaller = self.is_smaller()
self.old_rect = self.itemsBoundingRect()
if not self.sceneRect().contains(self.old_rect):
self.setSceneRect(-adjust_factor + x, -adjust_factor + y, adjust_factor + w, adjust_factor + h)
print(f'sceneRect: {self.sceneRect()}')
if not zoomed and smaller:
print('yes')
self.setSceneRect(adjust_factor2 + x, adjust_factor2 + y, abs(adjust_factor2 - w),
abs(adjust_factor2 - h))
def is_smaller(self):
x = self.old_rect.x()
y = self.old_rect.y()
h = self.old_rect.height()
w = self.old_rect.width()
if ((x <= self.itemsBoundingRect().x()) and (y <= self.itemsBoundingRect().y())
and (h > self.itemsBoundingRect().height()) and (w > self.itemsBoundingRect().width())):
return True
return False
# -----------main---------#
import sys
app = QtWidgets.QApplication(sys.argv)
w = View()
w.show()
sys.exit(app.exec_())
I know the code is lengthy but any help is appreciated.
The problem is a (partial) recursion caused by a wrong implementation of the object structure.
When the widget is resized using the size grip, it receives a resizeEvent that in your code is overridden by the GraphicsFrame.resize(): if you use setGeometry(), it will cause the graphics layout to resize its children, which in turn will call again a resize on the proxy widget. It's just a partial recursion because layout adjustments often require more than an event loop "cycle" (and with proxy widgets it might be even more).
The solution obviously is to avoid this recursion, which could be done by simply adding the proxy widget to the scene. Since the multiple item selection is required, this cannot be possible, as QGraphicsProxyWidgets are also panels (see the ItemIsPanel flag): only one panel graphics item can be active at once, so the only actual solution is to create a QGraphicsItem as a parent for the proxy. From that point, the geometry of the parent is based on the proxy, and the painting can be easily implemented.
class GraphicsFrame(QtWidgets.QGraphicsItem):
def __init__(self):
super().__init__()
self.container = Container()
self.container.setMinimumSize(150, 150)
self.container.setMaximumSize(400, 800)
self.proxy = QtWidgets.QGraphicsProxyWidget(self)
self.proxy.setWidget(self.container)
self.setFlags(self.ItemIsSelectable | self.ItemIsMovable)
def addWidget(self, widget):
self.container.layout().addWidget(widget)
def boundingRect(self):
# use the proxy for the bounding rect, adding the preferred margin
return self.proxy.boundingRect().adjusted(-11, -11, 11, 11)
def paint(self, qp, opt, widget):
qp.save()
qp.setPen(QtGui.QPen(Qt.yellow if self.isSelected() else Qt.red, 3))
qp.drawRoundedRect(self.boundingRect().adjusted(0, 0, -.5, -.5), 4, 4)
qp.restore()

How do i create a button to get the following updates of a signal in PyQt5?

This is my code. It simulate a signal that i get from a oscilloscope. I'm trying to create a interface, but i am new at coding (and english), and i got some problems.
i already create a real time plot graph and a button,but when clicked, the signal stop to update and i just get the same signal over and over again. I just need to know how to keeping updating the signal while i'm in the loop, if it is possible.
import pyqtgraph as pg
import numpy as np
import sys
from PyQt5 import QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal
class plotarT(QThread):
signal = pyqtSignal(object)
def __init__(self):
QThread.__init__(self)
self.phase = 0
def __del__(self):
self.wait()
def update(self):
self.t = np.arange(0, 3.0, 0.01)
self.s = np.sin(2 * np.pi * self.t + self.phase) #sin function
self.phase += 0.1
QThread.msleep(2500) #To simulate the time that oscilloscope take to respond
def run(self):
while True:
self.update()
self.signal.emit(self.s) #this emit the vectors
class Window(QDialog):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super().__init__()
self.title = "PyQt5 GridLayout"
self.top = 100
self.left = 100
self.width = 1000
self.height = 600
self.InitWindow()
self.traces = dict()
pg.setConfigOptions(antialias=True)
def InitWindow(self):
self.setWindowIcon(QtGui.QIcon("icon.png"))
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.gridLayoutCreation()
vboxLayout = QVBoxLayout()
vboxLayout.addWidget(self.groupBox)
self.setLayout(vboxLayout)
self.show()
def gridLayoutCreation(self): #my interface
self.groupBox = QGroupBox("Grid Layout Example")
gridLayout = QGridLayout()
self.guiplot = pg.PlotWidget()
gridLayout.addWidget(self.guiplot,0,8,8,12)
BtnL = QPushButton('Test')
gridLayout.addWidget(BtnL, 0, 1)
self.tlg = QLineEdit('')
gridLayout.addWidget(self.tlg, 1,1)
self.groupBox.setLayout(gridLayout)
BtnL.clicked.connect(self.get_data)
def get_data(self):
raw1 = []
raw2 = []
for i in range(2): #the signal does not update while running the loop
raw1.append(self.s)
time.sleep(1)
print(self.s)
def plotar(self,s): #here i plot the vector
self.s = s
self.guiplot.clear()
self.guiplot.plot(s)
def teste(self):
self.get_thread = plotarT()
self.get_thread.signal.connect(self.plotar) #connect to function
self.get_thread.start()
def main():
app = QtGui.QApplication(sys.argv)
form = Window()
form.show()
form.teste()
app.exec_()
if __name__ == '__main__': #Run my aplicattion
main()
Try it:
import pyqtgraph as pg
import numpy as np
import sys
from PyQt5 import QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal, QTimer # + QTimer
class plotarT(QThread):
signal = pyqtSignal(object)
def __init__(self):
QThread.__init__(self)
self.phase = 0
def __del__(self):
self.wait()
def update(self):
self.t = np.arange(0, 3.0, 0.01)
self.s = np.sin(2 * np.pi * self.t + self.phase) #sin function
self.phase += 0.1
QThread.msleep(500) # 500
def run(self):
while True:
self.update()
self.signal.emit(self.s) #this emit the vectors
class Window(QDialog):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super().__init__()
self.title = "PyQt5 GridLayout"
self.top = 100
self.left = 100
self.width = 1000
self.height = 600
self.InitWindow()
self.traces = dict()
pg.setConfigOptions(antialias=True)
self.timer = QTimer(self, timeout=self.pipe_output, interval=1000) # +++
def InitWindow(self):
self.setWindowIcon(QtGui.QIcon("icon.png"))
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.width, self.height)
self.gridLayoutCreation()
vboxLayout = QVBoxLayout()
vboxLayout.addWidget(self.groupBox)
self.setLayout(vboxLayout)
self.show()
def gridLayoutCreation(self): #my interface
self.groupBox = QGroupBox("Grid Layout Example")
gridLayout = QGridLayout()
self.guiplot = pg.PlotWidget()
gridLayout.addWidget(self.guiplot,0,8,8,12)
BtnL = QPushButton('Test')
gridLayout.addWidget(BtnL, 0, 1)
self.tlg = QLineEdit('')
gridLayout.addWidget(self.tlg, 1,1)
self.groupBox.setLayout(gridLayout)
BtnL.clicked.connect(self.get_data)
def get_data(self):
# raw1 = []
# raw2 = []
# for i in range(2): #the signal does not update while running the loop
# raw1.append(self.s)
# time.sleep(1)
# print(self.s)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.timer.start()
self.raw1 = []
self.raw2 = []
self.i = 0
def pipe_output(self):
self.i += 1
self.raw1.append(self.s)
print("\n ----------- \n", self.s)
if self.i == 2:
self.timer.stop()
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def plotar(self, s): # here i plot the vector
print("def plotar(self, s):", type(s))
self.s = s
self.guiplot.clear()
self.guiplot.plot(self.s) #(s)
def teste(self):
self.get_thread = plotarT()
self.get_thread.signal.connect(self.plotar) #connect to function
self.get_thread.start()
def main():
app = QtGui.QApplication(sys.argv)
form = Window()
form.show()
form.teste()
app.exec_()
if __name__ == '__main__': #Run my aplicattion
main()

PyQt is slow in handling a large number of objects

I present here a simple pyqt code, but I developed my whole design and algo upon this. The problem, is that PyQt is unable to manage a large number of objects when created. This is simple code for drawing many rectangles inside a big one. And it has Zoom and pan feature too.
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
item = None
class Rectangle(QGraphicsItem):
def __init__(self, parent, x, y, width, height, scene = None, fill=None):
self.parent = parent
super(Rectangle, self).__init__()
self.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
rect = QRectF(x,y,width,height)
self.dimen = [x,y,width,height]
self.rect = rect
scene.addItem(self)
self.show_its_ui()
#Function to get relative position
def get_relative_location(self,loc):
self.loc = QPointF(loc[0],loc[1])
global item
self.loc = self.mapFromItem(item, self.loc)
self.loc = (self.loc.x(),self.loc.y())
self.loc = list(self.loc)
self.dimen[0] = self.loc[0]
self.dimen[1] = self.loc[1]
#Showing its UI in the form of a rectangle
def show_its_ui(self):
#First gets its relative location and set it.
if(item is not None):
self.get_relative_location([self.dimen[0],self.dimen[1]])
#Make a Final rectangle
rect = QRectF(self.dimen[0],self.dimen[1],self.dimen[2],self.dimen[3])
self.rect = rect
def boundingRect(self):
return self.rect.adjusted(-2, -2, 2, 2)
def parentWidget(self):
return self.scene().views()[0]
def paint(self, painter, option, widget):
pen = QPen(Qt.SolidLine)
pen.setColor(Qt.black)
pen.setWidth(1)
painter.setBrush(QtGui.QColor(255, 50, 90, 200))
painter.drawRect(self.rect)
class GraphicsView(QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setDragMode(QGraphicsView.RubberBandDrag)
self.setRenderHint(QPainter.Antialiasing)
self.setRenderHint(QPainter.TextAntialiasing)
def wheelEvent(self, event):
factor = 1.41 ** (event.delta() / 240.0)
self.scale(factor, factor)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self, parent)
self.Width = 800
self.Height = 500
self.view = GraphicsView()
self.scene = QGraphicsScene(self)
self.scene.setSceneRect(0, 0, self.Width, self.Height)
self.view.setScene(self.scene)
self.initUI()
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.view)
mainWidget = QtGui.QWidget()
mainWidget.setLayout(hbox)
self.setCentralWidget(mainWidget)
def initUI(self):
self.group = QGraphicsItemGroup()
self.group.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
global item
item = self.group
self.group.setFiltersChildEvents(True)
self.group.setHandlesChildEvents(False)
self._link1 = Rectangle(self, 10, 10, self.Width, self.Height,self.scene)
self._link1.setFiltersChildEvents(True)
self._link1.setHandlesChildEvents(False)
self.group.addToGroup(self._link1)
self.scene.addItem(self.group)
self.addObjects()
#Here I added objects in the big canvas
def addObjects(self):
xpos = 20
ypos = 20
#Change the below to 5 each
width = 50
height = 50
#Generate many rectangles
while(ypos < 450):
xpos = 20
while(xpos < 750):
t = Rectangle(self._link1,xpos,ypos,width,height,self.scene,True)
self.group.addToGroup(t)
xpos += (width+2)
ypos += (height+2)
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
method = mainWindow
mainWindow.show()
sys.exit(app.exec_())
Try changing, the width and height values present in addObjects function of the mainwindow class, say to 5 and 5 respectively from 50. Then the whole application run at very slow speed, when you pan/zoom. Please share your suggestions in rectifying this.

PyQt4 send signal on button click to other window

I have 2 windows created by 2 classes, both windows contains simulations. I want both of them started by click of the same button in first window. Should I use something like:
self.connect(self, SIGNAL("mysignal"), self.myValChanged)
param = 100
self.emit(SIGNAL("mysignal"), param)
in first window and
def myValChanged(val):
print "New spin val %d" % val
in the second window? Will it work? How to use that?
My code:
class Simulation(QMainWindow):
def __init__(self, filename, parent=None):
QMainWindow.__init__(self, parent)
self.ui = uic.loadUi("gui/mainui.ui", self)
self.ui.startButton.clicked.connect(self.onStartButtonClick)
self.pos = [0, 0]
self.v0 = 10.0
self.pts = []
self.isMoving = False
self.force = Force()
self.loadData(filename)
self.movement = Movement(self.force, self.pts, self)
def onStartButtonClick(self):
if(self.isMoving == False):
self.pos = [0, 0]
self.v0 = 10.0
self.isMoving = True
self.move()
#start differentmove() from second class
def move(self):
...
class Simulation2(QMainWindow):
def __init__(self, filename, parent=None):
QMainWindow.__init__(self, parent)
self.ui = uic.loadUi("gui/mainui2.ui", self)
self.ui.startButton.clicked.connect(self.onStartButtonClick)
self.pos = [400, 0]
self.v0 = 10.0
self.pts = []
self.isMoving = False
self.force = Force()
self.loadData(filename)
self.movement = Movement(self.force, self.pts, self)
def differentmove(self):
...

Categories

Resources