I have a QGraphicsScene and I will display a contextmenu. But nothing happend.
Here is my Code:
class graphicsScene(QtGui.QGraphicsScene):
def __init__ (self, parent = None):
super(graphicsScene, self).__init__ (parent)
def contextMenuEvent(self, event):
self.popMenu = QtGui.QMenu()
self.popMenu.addAction(QtGui.QAction('test0', None))
self.popMenu.addAction(QtGui.QAction('test1', None))
self.popMenu.addSeparator()
self.popMenu.addAction(QtGui.QAction('test2', None))
self.popMenu.exec_(event.globalPos())
def mousePressEvent(self, event):
super(graphicsScene, self).mousePressEvent(event)
pos = event.scenePos()
item = self.itemAt(pos)
if event.button() == QtCore.Qt.LeftButton:
#do something
elif event.button() == QtCore.Qt.RightButton:
self.contextMenuEvent(event)
I have no idea how to fix this problem.
Thank you for your help!!!
This may help:
class Table(QtGui.QGraphicsView):
def __init__(self, parent=None):
super(Table, self).__init__(parent)
self.setScene(QtGui.QGraphicsScene())
self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
def contextMenuEvent(self, event):
menu = QtGui.QMenu()
menu.addAction('sample')
menu.exec_(event.globalPos())
Related
I have a scene where I would like to draw a line between two points(mouse press should be the start point and mouse release as the endpoint) using the QPainterpath.
Here is a demonstration of how I want it to be.
Here is what's happening with my current code.
Below is the code I have tried
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, *args, **kwargs):
super(Scene, self).__init__(*args, **kwargs)
self.point = QtCore.QPointF(0.0, 0.0)
self.path = QtGui.QPainterPath()
self.start_point = None
self.end_point = None
self.start = False
def mousePressEvent(self, event):
self.start_point = event.scenePos()
self.start = True
self.path = QtGui.QPainterPath(self.start_point)
# self.addLine(self.line.setP1(self.start_point))
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.start:
self.path.lineTo(event.scenePos())
# self.path.moveTo(event.scenePos())
self.addPath(self.path, QtGui.QPen(QtCore.Qt.red))
super(Scene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.start:
print(self.path)
self.path.lineTo(event.scenePos())
# self.path.moveTo(event.scenePos())
self.addPath(self.path, QtGui.QPen(QtCore.Qt.red))
self.start = False
super(Scene, self).mouseReleaseEvent(event)
def main():
app = QtWidgets.QApplication(sys.argv)
view = QtWidgets.QGraphicsView()
view.setRenderHint(QtGui.QPainter.Antialiasing)
view.setMouseTracking(True)
scene = Scene()
view.setScene(scene)
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Every time lineTo is used then a new line is created where the starting point is the last point added and the end point is the one that is passed to the function, so you see the curves since they are the union of those lines. The solution is to have 2 variables that store the start point and the end point, and be updated when necessary, then use that information to update the QGraphicsPathItem, not the QPainterPath. The same concept can be applied for QGraphicsLineItem with QLineF.
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, *args, **kwargs):
super(Scene, self).__init__(*args, **kwargs)
self.path_item = self.addPath(QtGui.QPainterPath())
self.start_point = QtCore.QPointF()
self.end_point = QtCore.QPointF()
def mousePressEvent(self, event):
self.start_point = event.scenePos()
self.end_point = self.start_point
self.update_path()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() & QtCore.Qt.LeftButton:
self.end_point = event.scenePos()
self.update_path()
super(Scene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.end_point = event.scenePos()
self.update_path()
super(Scene, self).mouseReleaseEvent(event)
def update_path(self):
if not self.start_point.isNull() and not self.end_point.isNull():
path = QtGui.QPainterPath()
path.moveTo(self.start_point)
path.lineTo(self.end_point)
self.path_item.setPath(path)
Thx to #eyllanesc for anyone trying to achieve this using QLineF and QGraphicsLineItem
here is the code.
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, *args, **kwargs):
super(Scene, self).__init__(*args, **kwargs)
self.setSceneRect(QtCore.QRectF(0, 0, 500, 500))
self.line = None
self.graphics_line = None
self.start_point = QtCore.QPointF()
self.end_point = QtCore.QPointF()
def mousePressEvent(self, event):
self.start_point = event.scenePos()
self.end_point = self.start_point
self.line = QtCore.QLineF(self.start_point, self.end_point)
self.graphics_line = QtWidgets.QGraphicsLineItem(self.line)
self.update_path()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() & QtCore.Qt.LeftButton:
self.end_point = event.scenePos()
self.update_path()
super(Scene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.end_point = event.scenePos()
self.update_path()
super(Scene, self).mouseReleaseEvent(event)
def update_path(self):
if not self.start_point.isNull() and not self.end_point.isNull():
self.line.setP2(self.end_point)
self.graphics_line.setLine(self.line)
self.addItem(self.graphics_line)
def main():
app = QtWidgets.QApplication(sys.argv)
view = QtWidgets.QGraphicsView()
view.setRenderHint(QtGui.QPainter.Antialiasing)
view.setMouseTracking(True)
scene = Scene()
view.setScene(scene)
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I want to execute other command on click and double click. When doubleclick is executed, click does not want to be executed.
from PySide2 import QtWidgets, QtCore, QtGui
class TestView(QtWidgets.QTableView):
custom_clicked = QtCore.Signal(QtCore.QModelIndex)
custom_double_clicked = QtCore.Signal(QtCore.QModelIndex)
def __init__(self, parent=None):
super(TestView, self).__init__(parent=parent)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(250)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.timeout)
self.click_number = 0
def mousePressEvent(self, event):
super(TestView, self).mousePressEvent(event)
if event.button() == QtCore.Qt.LeftButton:
self.index = self.indexAt(event.pos())
self.click_number += 1
if not self.timer.isActive():
self.timer.start()
def timeout(self):
if self.click_number < 2:
self.custom_clicked.emit(self.index)
elif self.click_number > 1:
self.custom_double_clicked.emit(self.index)
self.click_number = 0
def click_command(index):
print "single", index
def double_click_command(index):
print "double", index
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
tableView = TestView()
model = QtGui.QStandardItemModel()
item = QtGui.QStandardItem()
item.setData("test", QtCore.Qt.DisplayRole)
model.setItem(0, item)
tableView.setModel(model)
tableView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
tableView.custom_clicked[QtCore.QModelIndex].connect(click_command)
tableView.custom_double_clicked[QtCore.QModelIndex].connect(double_click_command)
tableView.show()
sys.exit(app.exec_())
This code works well when there is no item.
However, it will break if you double-click the item.
Timer seems to be the cause, but I'm looking for a reason.
To detect the double click you should only use the mouseDoubleClickEvent so you only have to detect that it was not pressed a second time in a certain time:
class TestView(QtWidgets.QTableView):
custom_clicked = QtCore.Signal(QtCore.QModelIndex)
custom_double_clicked = QtCore.Signal(QtCore.QModelIndex)
def __init__(self, parent=None):
super(TestView, self).__init__(parent)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.timeout)
self.timer.setInterval(
QtGui.QGuiApplication.styleHints().mouseDoubleClickInterval()
)
self.timer.setSingleShot(True)
def mousePressEvent(self, event):
super(TestView, self).mousePressEvent(event)
if event.button() == QtCore.Qt.LeftButton:
self.index = self.indexAt(event.pos())
self.timer.start()
def mouseDoubleClickEvent(self, event):
super(TestView, self).mouseDoubleClickEvent(event)
if event.button() == QtCore.Qt.LeftButton:
index = self.indexAt(event.pos())
self.custom_double_clicked.emit(index)
self.timer.stop()
def timeout(self):
self.custom_clicked.emit(self.index)
Today , I got stuck with QPixmap. My class is inheriting from QtWidgets.QGraphicsPixmapItem and like in example below.
class graphicButton(QtWidgets.QGraphicsPixmapItem):
def __init__(self):
pixmapA = QtGui.QPixmap(r"img.png")
QtWidgets.QGraphicsPixmapItem.__init__(self, pixmapA)
self.setFlags(
self.flags( )
| QtWidgets.QGraphicsItem.ItemIsSelectable
| QtWidgets.QGraphicsItem.ItemIsMovable
)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
print("mouse left press")
event.accept()
elif event.button() == QtCore.Qt.RightButton:
print("mouse right press")
event.accept()
elif event.button() == QtCore.Qt.MidButton:
print("mouse middle press")
event.accept()
It's working fine, but what if I want to put more then one picture? In most cases which I found in google, you need to make multiple QGraphicsPixmapItems. In that case I can't anymore inherit from QGraphicsPixItem and I have to go for QGraphicsItem or am I missing something ?
The simple solution is to load an image associated with its position, for example in the following case I place the images in the vertices of a pentagon:
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsButton(QtWidgets.QGraphicsPixmapItem):
def __init__(self, name, pixmap, parent=None):
super(GraphicsButton, self).__init__(pixmap, parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self._name = name
#property
def name(self):
return self._name
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
print("mouse left press")
elif event.button() == QtCore.Qt.RightButton:
print("mouse right press")
elif event.button() == QtCore.Qt.MidButton:
print("mouse middle press")
print(self.name)
super(GraphicsButton, self).mousePressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
# coordinates of the pentagon
datas = [
("name1", "img0.png", QtCore.QPointF(0, -200)),
("name2", "img1.png", QtCore.QPointF(-190, -62)),
("name3", "img2.png", QtCore.QPointF(-118, 162)),
("name4", "img3.png", QtCore.QPointF(118, 162)),
("name5", "img0.png", QtCore.QPointF(190, -62)),
]
for name, path, position in datas:
item = GraphicsButton(name, QtGui.QPixmap(path))
scene.addItem(item)
item.setPos(position)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I want mouse events to reach the appropriate QGraphicsItem but they get no further than the QGraphicsView.
I have reduced my code to 41 lines, commented out event handlers but to no avail. It is either handled by QGraphicsView if that has a handler or, if not, is not caught at all.
I'm sure I'm missing something obvious but I can't see it.
from PyQt5.QtWidgets import *
class MyFrame(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setScene(QGraphicsScene())
self.scene().addItem(Group())
def mouseReleaseEvent(self, event):
print('in QGraphicsView')
return QGraphicsView.mouseReleaseEvent(self, event)
class Group(QGraphicsItemGroup):
def __init__(self, parent=None):
super().__init__()
item = MyEllipse(0, 0, 20, 20)
self.addToGroup(item)
def mouseReleaseEvent(self, event):
print('in QGraphicsItemGroup')
return QGraphicsItemGroup.mouseReleaseEvent(self, event)
class MyEllipse(QGraphicsEllipseItem):
def mouseReleaseEvent(self, event):
print('in QGraphicsEllipseItem')
return QGraphicsEllipseItem.mouseReleaseEvent(self, event)
if __name__ == '__main__':
app = QApplication([])
f = MyFrame()
f.show()
app.exec_()
mouseReleaseEvent is called if and only if the event that the mousePressEvent handles is accepted, so with the following code the event will arrive at QGraphicsItemGroup:
class Group(QGraphicsItemGroup):
def __init__(self, parent=None):
super().__init__()
item = MyEllipse(0, 0, 20, 20)
self.addToGroup(item)
def mousePressEvent(self, event):
QGraphicsItemGroup.mousePressEvent(self, event)
event.accept()
def mouseReleaseEvent(self, event):
print('in QGraphicsItemGroup')
QGraphicsItemGroup.mouseReleaseEvent(self, event)
But as #ekhumoro points out, the QGraphicsItemGroup acts as a single element so events are not transported to the items they handle.
If you want to detect when the item is pressed you can use the following method:
class Group(QGraphicsItemGroup):
def __init__(self, parent=None):
super().__init__()
self._item = MyEllipse(0, 0, 20, 20)
self.addToGroup(self._item)
def mousePressEvent(self, event):
QGraphicsItemGroup.mousePressEvent(self, event)
event.accept()
def mouseReleaseEvent(self, event):
print('in QGraphicsItemGroup')
if self._item.mapToParent(self._item.boundingRect()).containsPoint(event.pos(), Qt.OddEvenFill):
print("_item")
QGraphicsItemGroup.mouseReleaseEvent(self, event)
i am trying to capture the key_tab event, but no luck. i realized that it only works if there are no other widgets so the cursor has no where to go only then can i get the event to return. here is a simplified code sample.
class MyCombo(QComboBox):
def __init__(self, parent=None):
super(MyCombo, self).__init__(parent)
self.setEditable(True)
def keyPressEvent(self, event):
if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Tab):
print "tab pressed"
elif event.key() == Qt.Key_Return:
print "return pressed"
else:
QComboBox.keyPressEvent(self, event)
class Form_1(QDialog):
def __init__(self, parent=None):
super(Form_1, self).__init__(parent)
self.combo = MyCombo()
self.line = QLineEdit()
layout = QVBoxLayout()
layout.addWidget(self.combo)
layout.addWidget(self.line)
self.setLayout(layout)
app = QApplication(sys.argv)
form = Form_1()
form.show()
app.exec_()
if i comment out the following two lines
self.line = QLineEdit()
layout.addWidget(self.line)
then it works fine, because there is only one widget left on the form.
where am i going wrong?
Cheers, Joe
Apparently the Key_Tab press event is never passed to any handler but to the setFocus() so in order to intercept the Key_Tab event, we need to implement the event() method itself.
so here is the new code:
class MyCombo(QComboBox):
def __init__(self, parent=None):
super(MyCombo, self).__init__(parent)
self.setEditable(True)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return:
print "return pressed"
else:
QComboBox.keyPressEvent(self, event)
def event(self, event):
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
print "tab pressed"
return False
return QWidget.event(self, event)
class Form_1(QDialog):
def __init__(self, parent=None):
super(Form_1, self).__init__(parent)
self.combo = MyCombo()
self.line = QLineEdit()
layout = QVBoxLayout()
layout.addWidget(self.combo)
layout.addWidget(self.line)
self.setLayout(layout)