I've been working on this for some time now and I can't figure out what I'm doing wrong. I hope someone here can help.
I'm trying to get hover events to work when I mouse over an Svg item that's in a QGraphicsScene. Here's the code that I've been playing with.
#!/usr/bin/python
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtSvg import *
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
hbox = QHBoxLayout()
self.setLayout(hbox)
self.view = MyView(self)
self.scene = QGraphicsScene()
self.view.setScene(self.scene)
hbox.addWidget(self.view)
class MyView(QGraphicsView):
def __init__(self, parent):
super(MyView, self).__init__(parent)
self.parent = parent
def mousePressEvent(self, event):
super(MyView, self).mousePressEvent(event)
test = MySvg()
self.parent.scene.addItem(test.image)
class MySvg(QGraphicsSvgItem):
def __init__(self):
super(MySvg, self).__init__()
self.image = QGraphicsSvgItem('ubuntu.svg')
self.image.setFlags(QGraphicsItem.ItemIsSelectable|
QGraphicsItem.ItemIsMovable)
self.setAcceptsHoverEvents(True)
def hoverEnterEvent(self, event):
print 'Enter'
def hoverLeaveEvent(self, event):
print 'Leave'
def hoverMoveEvent(self, event):
print 'Moving'
def runMain():
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
runMain()
Hope someone can help.
You are monitoring hover events for MySvg but you are adding another QGraphicsSvgItem to the view that is just an instance (MySvg.image) in MySvg. Your MySvg is not even in the view. Try like this:
#!/usr/bin/python
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtSvg import *
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
hbox = QHBoxLayout()
self.setLayout(hbox)
self.view = MyView(self)
self.scene = QGraphicsScene()
self.view.setScene(self.scene)
hbox.addWidget(self.view)
class MyView(QGraphicsView):
def __init__(self, parent):
super(MyView, self).__init__(parent)
self.parent = parent
def mousePressEvent(self, event):
super(MyView, self).mousePressEvent(event)
test = MySvg()
self.parent.scene.addItem(test)
class MySvg(QGraphicsSvgItem):
def __init__(self):
super(MySvg, self).__init__('ubuntu.svg')
self.setFlags(QGraphicsItem.ItemIsSelectable|
QGraphicsItem.ItemIsMovable)
self.setAcceptsHoverEvents(True)
def hoverEnterEvent(self, event):
print 'Enter'
def hoverLeaveEvent(self, event):
print 'Leave'
def hoverMoveEvent(self, event):
print 'Moving'
def runMain():
app = QApplication(sys.argv)
ex = Main()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
runMain()
Related
I would like to capture mouse clicks onto the MainWindow in a child class of the application.
I've tried the following but without success:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from abc import ABCMeta, abstractmethod
class BaseView(object):
def __init__(self,parent, page=None):
self.parent = parent
self.page = page
#abstractmethod
def preprare_view(self):
pass
#abstractmethod
def clean_up_view(self):
pass
class FooView(BaseView):
def __init__(self, parent, page):
super(FooView, self).__init__(parent, page)
self.parent = parent
def mousePressEvent(self, QMouseEvent):
print(parent.QMouseEvent.pos())
def mouseReleaseEvent(self, QMouseEvent):
cursor = QtGui.QCursor()
print(parent.cursor.pos())
class Example(QtWidgets.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.foo = "fooview"
self.Foo = FooView(self,self.foo)
qbtn = QtWidgets.QPushButton('Quit', self)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(0, 0, 1024, 768)
self.setWindowTitle('Quit button')
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I'm wondering how I can capture the mouse clicks in the FooView child class - is that even possible?
There is terminology that the OP uses that is confusing, for example FooView is a child class of BaseView but that has nothing to do with Qt so it is irrelevant for this case so I will omit that class and show the example of how another class can obtain information about the click event of a widget.
The logic is to create a class that inherits from QObject and apply an event filter to the other widget, then override the eventFilter method where the events of the widget are obtained.
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MouseObserver(QtCore.QObject):
def __init__(self, widget):
super(MouseObserver, self).__init__(widget)
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, obj, event):
if obj is self.widget:
if event.type() == QtCore.QEvent.MouseButtonPress:
print(event.pos(), QtGui.QCursor.pos())
elif event.type() == QtCore.QEvent.MouseButtonRelease:
print(event.pos(), QtGui.QCursor.pos())
return super(MouseObserver, self).eventFilter(obj, event)
class Example(QtWidgets.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.observer = MouseObserver(self)
qbtn = QtWidgets.QPushButton("Quit", self)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(0, 0, 1024, 768)
self.setWindowTitle("Quit button")
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.show()
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have a number of QTreeWidget. Here, there are two trees.
the left one has "a" , "b".
I want to drag this item into the right tree.
I have no error but the item become empty.
How should I do for dragging the left data to the right tree?
and why?
data is this.
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x02\x00a\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x02\x00b'
from PySide import QtCore
from PySide import QtGui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=None)
self.sequoia = Sequoia()
self.baobab = Baobab()
self.c_widget = QtGui.QWidget()
h_boxlayout = QtGui.QHBoxLayout()
h_boxlayout.addWidget(self.sequoia, 30)
h_boxlayout.addWidget(self.baobab, 70)
self.c_widget.setLayout(h_boxlayout)
self.setCentralWidget(self.c_widget)
class Sequoia(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(Sequoia, self).__init__(parent=None)
self.setColumnCount(2)
self.setAcceptDrops(True)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.sampleitem = QtGui.QTreeWidgetItem()
self.sampleitem.setText(0, "a")
self.sampleitem.setText(1, "b")
self.addTopLevelItem(self.sampleitem)
class Baobab(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(Baobab, self).__init__(parent=None)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setColumnCount(2)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'):
event.accept()
return QtGui.QTreeWidget.dragEnterEvent(self, event)
def dragMoveEvent(self, event):
if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist') and not isinstance(event, QtGui.QDropEvent):
event.accept()
return QtGui.QTreeWidget.dragMoveEvent(self, event)
def dropEvent(self, event):
if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'):
bytearray = event.mimeData().data('application/x-qabstractitemmodeldatalist')
datastream = QtCore.QDataStream(bytearray, QtCore.QIODevice.ReadOnly)
print(3306, bytearray.data())
item = QtGui.QTreeWidgetItem()
item.setFlags(QtCore.Qt.ItemFlag.ItemIsEditable|QtCore.Qt.ItemFlag.ItemIsEnabled|QtCore.Qt.ItemFlag.ItemIsSelectable|QtCore.Qt.ItemIsDragEnabled|QtCore.Qt.ItemIsDropEnabled)
item.read(datastream)
self.addTopLevelItem(item)
def main():
try:
QtGui.QApplication([])
except Exception as e:
print(e)
mw = MainWindow()
mw.show()
sys.exit(QtGui.QApplication.exec_())
if __name__ == "__main__":
main()
It is not necessary to implement your own drag-and-drop method between in QTreeWidget, you just have to configure it correctly:
from PySide import QtCore, QtGui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=None)
self.sequoia = Sequoia()
self.baobab = Baobab()
self.c_widget = QtGui.QWidget()
h_boxlayout = QtGui.QHBoxLayout(self.c_widget)
self.setCentralWidget(self.c_widget)
h_boxlayout.addWidget(self.sequoia, 30)
h_boxlayout.addWidget(self.baobab, 70)
class Sequoia(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(Sequoia, self).__init__(parent=None)
self.setColumnCount(2)
self.setDefaultDropAction(QtCore.Qt.CopyAction)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setAcceptDrops(True)
self.sampleitem = QtGui.QTreeWidgetItem()
self.sampleitem.setText(0, "a")
self.sampleitem.setText(1, "b")
self.addTopLevelItem(self.sampleitem)
class Baobab(QtGui.QTreeWidget):
def __init__(self, parent=None):
super(Baobab, self).__init__(parent=None)
self.setColumnCount(2)
self.setAcceptDrops(True)
def main():
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
If you still want to implement it manually then if we use your perspective a possible solution is:
def dropEvent(self, event):
if event.mimeData().hasFormat(
"application/x-qabstractitemmodeldatalist"
):
ba = event.mimeData().data(
"application/x-qabstractitemmodeldatalist"
)
ds = QtCore.QDataStream(
ba, QtCore.QIODevice.ReadOnly
)
i = 0
item = QtGui.QTreeWidgetItem()
while not ds.atEnd():
row = ds.readInt32()
column = ds.readInt32()
map_items = ds.readInt32()
self.addTopLevelItem(item)
for _ in range(map_items):
role = ds.readInt32()
value = ds.readQVariant()
item.setData(i, role, value)
i = (i + 1) % self.columnCount()
But the above is forced, a better solution is to use the dropMimeData method of the model:
def dropEvent(self, event):
if event.mimeData().hasFormat(
"application/x-qabstractitemmodeldatalist"
):
parent = self.indexAt(event.pos())
self.model().dropMimeData(
event.mimeData(), event.dropAction(), 0, 0, parent
)
I have to activate some function, when the cursor is moving. So, I used self.setMouseTracking(True) in MainWidget. But in this way mouseMoveEvent() works only when there is an empty form under cursor. I tried to create another widget over main, but it doesnt work at all.
class ClickButton(QPushButton):
def __init__(self, text, window):
...
def run(self):
...
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 1000, 1000)
self.setMouseTracking(True)
self.clickers = [ClickButton('OK', self) for i in range(8)]
def mouseMoveEvent(self, ev):
for e in self.clickers:
e.run()
Whats to do?
If you want to detect the position of the mouse even when the mouse is on top of a child, a possible option is to use an event filter.
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
w_ = QtWidgets.QWidget()
lay_w = QtWidgets.QHBoxLayout(w_)
for c in (QtWidgets.QPushButton(), QtWidgets.QLineEdit()):
lay_w.addWidget(c)
lay = QtWidgets.QVBoxLayout(self)
for w in (QtWidgets.QPushButton(), QtWidgets.QLineEdit(), QtWidgets.QTextEdit(), w_):
lay.addWidget(w)
for ws in self.findChildren(QtWidgets.QWidget) + [self]:
ws.setMouseTracking(True)
ws.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseMove:
p_respect_to_window = self.mapFromGlobal(obj.mapToGlobal(event.pos()))
print(p_respect_to_window)
return super(Widget, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
On the other hand if you only want to do it in only one type of custom widget it is better to overwrite the mouseMoveEvent method of the custom widget:
from PyQt5 import QtCore, QtGui, QtWidgets
class ClickButton(QtWidgets.QPushButton):
def __init__(self, text, parent=None):
super(ClickButton, self).__init__(text=text, parent=parent)
self.setMouseTracking(True)
def mouseMoveEvent(self, event):
self.run()
super(ClickButton, self).mouseMoveEvent(event)
def run(self):
print("call to run function in button{} and time: {}".format(self.text(),
QtCore.QDateTime.currentDateTime().toString()))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
for i in range(10):
w = ClickButton(str(i), self)
lay.addWidget(w)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I'm making a program to specify a specific area using pyqt5.
If I click on the capture button, I will hide the MainWindow and display a screen to specify the area. I want to clear the screen and return to the mainwindow by pressing the esc key. But MainWindow does not show again. How should I work it?
form_class = uic.loadUiType("prototype.ui")[0]
class MainWindow(QtWidgets.QMainWindow, form_class) :
def __init__(self):
super().__init__()
self.setupUi(self)
self.Capture_Button.clicked.connect(self.Capture_Btn_clicked)
self.CaptureWindow = CaptureWidget()
self.CaptureWindow.hide()
def Capture_Btn_clicked(self) :
self.hide()
self.CaptureWindow.close()
self.CaptureWindow.__init__()
self.CaptureWindow.show()
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
def enterEvent(self, QEvent):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.setWindowOpacity(1)
This is the class that specifies the area (some of the code is omitted).
class CaptureWidget(QtWidgets.QDialog):
def __init__(self):
super().__init__()
root = tk.Tk()
self.setupUi(self)
def setupUi(self) :
self.screen_width = root.winfo_screenwidth()
self.screen_height = root.winfo_screenheight()
self.setGeometry(0, 0, self.screen_width, self.screen_height)
self.setWindowTitle(' ')
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.setWindowOpacity(0.3)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.WindowStaysOnTopHint)
print('Capture the screen...')
self.is_set_region = False
self.is_mouse_click = False
self.show()
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Escape:
print('esc')
self.close()
elif key == Qt.Key_F1:
self.close()
self.__init__()
First of all, instead of overwriting the keyPressEvent method, it is easier to use QShortcut. On the other hand for this case it is better to create a signal that indicates when the escape key is pressed connecting it to the show method.
from PyQt5 import QtCore, QtGui, QtWidgets, uic
class CaptureWidget(QtWidgets.QDialog):
escape_pressed = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
self.setupUi()
def setupUi(self):
self.setWindowOpacity(0.3)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
shorcut_scaped = QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self)
shorcut_scaped.activated.connect(self.escape_pressed)
shorcut_scaped.activated.connect(self.close)
shorcut = QtWidgets.QShortcut(QtCore.Qt.Key_F1, self)
shorcut.activated.connect(self.close)
form_class, _ = uic.loadUiType("prototype.ui")
class MainWindow(QtWidgets.QMainWindow, form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
self.Capture_Button.clicked.connect(self.Capture_Btn_clicked)
self.CaptureWindow = CaptureWidget()
self.CaptureWindow.escape_pressed.connect(self.show)
#QtCore.pyqtSlot()
def Capture_Btn_clicked(self):
self.hide()
self.CaptureWindow.showFullScreen()
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
def enterEvent(self, QEvent):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.setWindowOpacity(1)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
The title basically says it all. I want to create an image that I can drag around inside a window. My code so far does all that but for some reason, the image appears really small even though, I used the scaled() function to resize it. Also, if I change the values inside the scaled() function, the image disappears.
from PySide.QtGui import *
from PySide.QtCore import *
import sys
class Label(QLabel):
def __init__(self, title, parent):
super(Label, self).__init__(title, parent)
self.setup()
def setup(self):
folder_pic = QPixmap("path")
folder_size = folder_pic.scaled(64, 64)
self.label = QLabel(self)
self.label.setPixmap(folder_size)
def mouseMoveEvent(self, e):
if e.buttons() != Qt.LeftButton:
return
mimeData = QMimeData(self)
drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())
dropAction = drag.start(Qt.MoveAction)
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setAcceptDrops(True)
self.button = Label("", self)
self.button.move(100, 65)
self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 280, 150)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
position = e.pos()
self.button.move(position)
e.setDropAction(Qt.MoveAction)
e.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
mywin = Example()
mywin.show()
sys.exit(app.exec_())
My code is based on this tutorial http://zetcode.com/gui/pysidetutorial/dragdrop/.
You set a pixmap on the drag object:
drag.setPixmap(self.pixmap())
But for that to work, you will also need this fix:
def setup(self):
folder_pic = QPixmap("path")
folder_size = folder_pic.scaled(64, 64)
self.setPixmap(folder_size)