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_())
Related
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)
I want my PyQt5 program to be frameless and always be on top of the screen. When I use the WindowStayOnTopHint it works fine, but when I use it with the FramelessWindowHint it becomes frameless, but does not stay on top of the screen. After doing some research I found this, and it said to try to use setMask, but I could not get it to work.
Here is my code:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QLabel
class Invisible(QLabel):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.__press_pos = None
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMask() #This is where I use the setMask function, but it raises an error
self.setAttribute(Qt.WA_TranslucentBackground)
self.setText("Drag me...")
self.setFont(QFont("Times", 50, QFont.Bold))
self.adjustSize()
self.move(QApplication.instance().desktop().screen().rect().center()
- self.rect().center())
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = None
def mouseMoveEvent(self, event):
if self.__press_pos:
self.move(self.pos() + (event.pos() - self.__press_pos))
def main():
app = QApplication(sys.argv)
w = Invisible()
w.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
This code gives me the error:
TypeError: arguments did not match any overloaded call:
setMask(self, QBitmap): not enough arguments
setMask(self, QRegion): not enough arguments
It is asking for more arguments, but when I give it more arguments it says that there are too many arguments.
How do I fix this?
The problem in your case is that you must activate both properties with the operator |
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
Complete code:
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QLabel, QStyle
class Invisible(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__press_pos = QPoint()
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setText("Drag me...")
self.setFont(QFont("Times", 50, QFont.Bold))
self.adjustSize()
self.setGeometry(
QStyle.alignedRect(
Qt.LeftToRight,
Qt.AlignCenter,
self.size(),
QApplication.instance().desktop().availableGeometry()
)
)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.__press_pos = QPoint()
def mouseMoveEvent(self, event):
if not self.__press_pos.isNull():
self.move(self.pos() + (event.pos() - self.__press_pos))
def main():
app = QApplication(sys.argv)
w = Invisible()
w.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
setMask() serves to give a different border to the widget, for example for an example I got the following:
I'm trying to drag and drop a Qlabel on another Qlabel with PyQt5:
from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QHBoxLayout, QVBoxLayout, QGridLayout,QFrame, QComboBox, QLabel, QLineEdit
from PyQt5.QtGui import QIcon, QFont, QPixmap, QImage
import sys
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasFormat("text/plain"):
event.accept()
else:
event.ignore()
def dropEvent(self,event):
self.setText(event.mimeData().text())
class application(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label = my_label("drop there",self)
label.resize(100,100)
label.move(190,65)
label_to_drag = QLabel("drag this",self)
#label_to_drag.setDragEnabled(True) #doesn't work with QLabel
self.show()
def closeEvent(self,event):
message = QMessageBox.question(self,"Message","Quit ?",QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
if message == QMessageBox.Yes:
event.accept()
else:
event.ignore()
app = QApplication(sys.argv)
fenetre = application()
sys.exit(app.exec_())
I wanted to make my first label "draggable",by calling the setDragEnabled(True) method (as i was doing for QLineEdit),and drop it on the second label to change his text. Dropping text from another app is working fine,but I can't find how to drag the QLabel inside my own app...
What did I miss ?
EDIT : modified the code to try to Drag&Drop Images :
class DraggableLabel(QLabel):
def __init__(self,parent,image):
super(QLabel,self).__init__(parent)
self.setPixmap(QPixmap(image))
self.show()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasFormat("text/plain"):
print("event accepted")
event.accept()
else:
print("event rejected")
event.ignore()
def dropEvent(self,event):
if event.mimeData().hasImage():
self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))
class application(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label_to_drag = DraggableLabel(self,"index.jpg")
label = my_label("drop there",self)
label.resize(100,100)
label.move(190,65)
self.show()
When I drop the DraggableLabel (displaying the image) on my_label,the event is accepted, but hasImage() always returns false...Is the way I set the image up wrong ?
In the case of QLabel you must create everything from the beginning, for this you can follow the examples of the docs.
In the following example I have placed an example where one class only accepts the drop and the other the drag so that you can see each part and understand better.
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QWidget
from PyQt5.QtGui import QDrag, QPixmap, QPainter, QCursor
from PyQt5.QtCore import QMimeData, Qt
class DraggableLabel(QLabel):
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
class DropLabel(QLabel):
def __init__(self, *args, **kwargs):
QLabel.__init__(self, *args, **kwargs)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasText():
event.acceptProposedAction()
def dropEvent(self, event):
pos = event.pos()
text = event.mimeData().text()
self.setText(text)
event.acceptProposedAction()
class Widget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label = DropLabel("drop there",self)
label.setGeometry(190, 65, 100,100)
label_to_drag = DraggableLabel("drag this",self)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Update:
Do not confuse the QDrag with the QMimeData, if you want to use imageData() you must set it with setImageData() as indicated by the docs:
class DraggableLabel(QLabel):
def __init__(self,parent,image):
super(QLabel,self).__init__(parent)
self.setPixmap(QPixmap(image))
self.show()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.drag_start_position = event.pos()
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.LeftButton):
return
if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
return
drag = QDrag(self)
mimedata = QMimeData()
mimedata.setText(self.text())
mimedata.setImageData(self.pixmap().toImage())
drag.setMimeData(mimedata)
pixmap = QPixmap(self.size())
painter = QPainter(pixmap)
painter.drawPixmap(self.rect(), self.grab())
painter.end()
drag.setPixmap(pixmap)
drag.setHotSpot(event.pos())
drag.exec_(Qt.CopyAction | Qt.MoveAction)
class my_label(QLabel):
def __init__(self,title,parent):
super().__init__(title,parent)
self.setAcceptDrops(True)
def dragEnterEvent(self,event):
if event.mimeData().hasImage():
print("event accepted")
event.accept()
else:
print("event rejected")
event.ignore()
def dropEvent(self,event):
if event.mimeData().hasImage():
self.setPixmap(QPixmap.fromImage(QImage(event.mimeData().imageData())))
I have some trouble to distinguish between a single and a double mouse click event. I have created an event filter but a mouse double click also gives me a single signal back. In my code I have to separate both events to connect to different functions. Can anybody suggest me how to do this?
Here is an example. What I want is, if a double mouse click happen, only the MouseButtonDblClick should give a signal and not the LeftButton and MouseButtonDblClick:
# coding: utf-8
import sys
from PyQt4 import QtCore, QtGui
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.button1 = QtGui.QPushButton("Button 1")
self.button2 = QtGui.QPushButton("Button 2")
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.button1)
hbox.addWidget(self.button2)
self.setLayout(hbox)
self.button1.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.LeftButton:
#If image is left clicked, display a red bar.
print 'one left'
elif event.button() == QtCore.Qt.RightButton:
print 'one right'
elif event.type() == QtCore.QEvent.MouseButtonDblClick:
#If image is double clicked, remove bar.
print 'two'
return super(MyDialog, self).eventFilter(obj, event)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = MyDialog()
w.show()
sys.exit(app.exec_())
Thank you in advance!
Stefanie
It's kind of a hack, but it should do the trick.
Also, I used new-style signals instead of your event filter, something you should consider.
Here, the ClickHandler class counts the number of clicks between the first click and the timeout event of its timer.
from PyQt4 import QtCore, QtGui
class ClickHandler():
def __init__(self, time):
self.timer = QtCore.QTimer()
self.timer.setInterval(time)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.timeout)
self.click_count = 0
def timeout(self):
if self.click_count == 1:
print('Single click')
elif self.click_count > 1:
print('Double click')
self.click_count = 0
def __call__(self):
self.click_count += 1
if not self.timer.isActive():
self.timer.start()
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.button1 = QtGui.QPushButton("Button 1")
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.button1)
self.setLayout(hbox)
self.click_handler = ClickHandler(300)
self.button1.clicked.connect(self.click_handler)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MyDialog()
w.show()
sys.exit(app.exec_())
EDIT : A second cleaner version with a CustomButton class that handles left and right click signals:
from PyQt4 import QtCore, QtGui
class CustomButton(QtGui.QPushButton):
left_clicked= QtCore.pyqtSignal(int)
right_clicked = QtCore.pyqtSignal(int)
def __init__(self, *args, **kwargs):
QtGui.QPushButton.__init__(self, *args, **kwargs)
self.timer = QtCore.QTimer()
self.timer.setInterval(250)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.timeout)
self.left_click_count = self.right_click_count = 0
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.left_click_count += 1
if not self.timer.isActive():
self.timer.start()
if event.button() == QtCore.Qt.RightButton:
self.right_click_count += 1
if not self.timer.isActive():
self.timer.start()
def timeout(self):
if self.left_click_count >= self.right_click_count:
self.left_clicked.emit(self.left_click_count)
else:
self.right_clicked.emit(self.right_click_count)
self.left_click_count = self.right_click_count = 0
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.button1 = CustomButton("Button 1")
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.button1)
self.setLayout(hbox)
self.button1.left_clicked[int].connect(self.left_click)
self.button1.right_clicked[int].connect(self.right_click)
def left_click(self, nb):
if nb == 1: print('Single left click')
else: print('Double left click')
def right_click(self, nb):
if nb == 1: print('Single right click')
else: print('Double right click')
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MyDialog()
w.show()
sys.exit(app.exec_())
You should have a look at this thread on Double-Click-Capturing.
A Timer might do the job. However, it is probably a bad idea to have unrelated single clicks and double clicks (see Bill's answer to "Distinguish between single and double click events in Qt").
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())