I am trying to drag a list item onto another list. The dragEnterEvent fires just fine, I do e.accept(), however the dropEvent never fires. Here's the code:
class LocalList(QtGui.QListWidget):
def __init__(self, parent):
super(LocalList, self).__init__(parent)
self.parent = parent
self.setDragEnabled(True)
def mouseMoveEvent(self, e):
mimeData = QtCore.QMimeData()
mimeData.setText(self.currentItem().text())
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
dropAction = drag.exec_()
class RemoteList(QtGui.QListWidget):
def __init__(self, parent):
super(RemoteList, self).__init__(parent)
self.parent = parent
self.setAcceptDrops(True)
def dragEnterEvent(self, e):
print "MimeText: " + e.mimeData().text()
e.accept()
def dropEvent(self, e):
print "DROPPED"
print self.parent.localdir + "/" + e.mimeData().text()
e.accept()
To clarify, I'm dragging from LocalList to RemoteList. The mousMoveEvent is being fired just fine, because the mimeData().text() prints out just fine in RemoteList's dragEnterEvent. I don't think it's accepting right though, because dropEvent is never fired, and when I'm hovering over the RemoteList it doesn't have the "drop here" icon.
You have to implement both dragEnterEvent and dragMoveEvent. See here for another similar question
This seems to do what you need rather short and elegant :)
class DragDropListWidget(QtGui.QListWidget):
def __init__(self, type, parent=None):
super(DragDropListWidget, self).__init__(parent)
self.setDefaultDropAction(QtCore.Qt.MoveAction)
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.setAcceptDrops(True)
Related
I have a 'nested', a 2-tiered qmenus in which I have created a context menu for renaming and deleting.
I created a subclass for the QMenus in hopes of making my code cleaner as I am unable to use eventFilter in my current code as it messes some functionalities..
For the renaming part, while it renames the first tiered, as soon as I tried to do the same for the second-tiered item, the prompt actually renames the first-tiered item.
The following is a code portion of the QMenu subclass that I did and if you do the following:
Right click and add in a new object called main
In main, create another object called sub
(This is an extra right-click) If you perform another right-mouse click on main and select rename options, and have it changed to 'newMain', this works
Perform the same action as detailed in point #3, but this time, rename on sub to newSub
If you open up the overall menu, noticed that newMain was changed to newSub, while sub remains unchanged.
Could someone kindly shed some light towards my QMenu subclass on where I have done it wrong?
import functools
import sys
from PyQt4 import QtGui, QtCore
class QAddAction(QtGui.QAction):
def __init__(self, icon=None, text="Add Item", parent=None):
if icon:
super(QAddAction, self).__init__(icon, text, parent)
else:
super(QAddAction, self).__init__(text, parent)
class QRenameAction(QtGui.QAction):
def __init__(self, icon=None, text="Rename Item", parent=None):
if icon:
super(QRenameAction, self).__init__(icon, text, parent)
else:
super(QRenameAction, self).__init__(text, parent)
class QDeleteAction(QtGui.QAction):
def __init__(self, icon=None, text="Delete Item", parent=None):
if icon:
super(QDeleteAction, self).__init__(icon, text, parent)
else:
super(QDeleteAction, self).__init__(text, parent)
class QCustomMenu(QtGui.QMenu):
"""Customized QMenu."""
def __init__(self, title, parent=None):
super(QCustomMenu, self).__init__(title=str(title), parent=parent)
self.setup_menu()
def setup_menu(self):
self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
def contextMenuEvent(self, event):
no_right_click = [QAddAction, QRenameAction, QDeleteAction]
if any([isinstance(self.actionAt(event.pos()), instance) for instance in no_right_click]):
return
self.show_adv_qmenu()
def show_adv_qmenu(self):
qmenu = QCustomMenu(self)
rename_menu_action = QRenameAction(text= "Rename Item", parent=self)
rename_slot = functools.partial(self.rename_menu_item)
rename_menu_action.triggered.connect(rename_slot)
qmenu.addAction(rename_menu_action)
delete_menu_action = QDeleteAction(text="Delete Item", parent=self)
delete_slot = functools.partial(self.delete_menu_item, delete_menu_action)
delete_menu_action.triggered.connect(delete_slot)
qmenu.addAction(delete_menu_action)
qmenu.exec_(QtGui.QCursor().pos())
def addAction(self, action):
super(QCustomMenu, self).addAction(action)
def rename_menu_item(self):
new_name, ok = QtGui.QInputDialog.getText(
self,
"Rename Menu Item ({0})".format(self.title()),
"New name:"
)
if ok:
self.setTitle(new_name)
def delete_menu_item(self, action):
reply = QtGui.QMessageBox.question(self, 'Message',
"Really Delete this item?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
parent = action.parent()
print parent
self.remove_menu_item(self)
else:
event.ignore()
def err_popup(self):
"""Prompts a notification popup."""
msg = QtGui.QMessageBox()
msg.setIcon(QtGui.QMessageBox.Critical)
msg.setText("Input name already exists. Please check.")
msg.setWindowTitle('Unable to add item')
msg.setStandardButtons(QtGui.QMessageBox.Ok)
msg.exec_()
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Context menu')
self.qmenu = QCustomMenu(title='', parent=self)
add_item_action = QtGui.QAction('Add new item', self,
triggered=self.add_new_item)
self.qmenu.addAction(add_item_action)
def contextMenuEvent(self, event):
action = self.qmenu.exec_(self.mapToGlobal(event.pos()))
def add_new_item(self):
main_menu_name, ok = QtGui.QInputDialog.getText(
self,
'Main Menu',
'Name of new Menu Item:'
)
if ok:
self._addMenuItemTest(main_menu_name)
def _addMenuItemTest(self, main_menu_name):
base_qmenu = QCustomMenu(title=main_menu_name, parent=self)
base_qmenu.setTearOffEnabled(True)
add_item_action = QAddAction(None, 'Add Item', base_qmenu)
slot = functools.partial(self.add_sub_item, base_qmenu)
add_item_action.triggered.connect(slot)
base_qmenu.addAction(add_item_action)
self.qmenu.addMenu(base_qmenu)
def add_sub_item(self, base_menu):
sub_menu_name, ok = QtGui.QInputDialog.getText(
self,
'Sub Menu',
'Name of new Sub Item:'
)
if ok:
action = QtGui.QAction(sub_menu_name, self)
slot = functools.partial(
self._callActionItem,
str(base_menu.title()),
str(sub_menu_name)
)
action.setCheckable(True)
action.setChecked(True)
action.toggled.connect(slot)
base_menu.addAction(action)
def _callActionItem(self):
pass
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Example()
window.show()
sys.exit(app.exec_())
I want some small text to pop up when I have my curser over a QGraphicsItem in my QGraphicsScene. I have a class that inherits from QGraphicsItem, and this represents my graphical items in the scene.
I tried using the QGraphicsItem.hoverEnterEvent and I also set the setAcceptHoverEvents(True), but I still can't enable that hover event. I also came across an event filter method but I'm not sure where to implement it.
Should I install the event filter in the QGraphicsItem class, or the scene? I tried both and I'm still not getting the desired result. I want to be able to hover over all the items in the scene.
UPDATE:
So I tried doing this but the hover event still isn't being detected.
class graphics_Object(QtGui.QGraphicsPixmapItem):
def __init__(self, parent=None):
super(graphics_Object, self).__init__(parent)
pixmap = QtGui.QPixmap("item.png")
self.graphics_pixItem = QtGui.QGraphicsPixmapItem(pixmap.scaled(40, 40, QtCore.Qt.KeepAspectRatio))
self.graphics_pixItem.setFlag(QtGui.QGraphicsPixmapItem.ItemIsSelectable)
self.graphics_pixItem.setFlag(QtGui.QGraphicsPixmapItem.ItemIsMovable)
self.graphics_pixItem.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event):
print 'hello'
class graphicsScene(QtGui.QGraphicsScene):
def __init__(self, parent=None):
super(graphicsScene, self).__init__(parent)
def mousePressEvent(self, event):
self.graphics_item = graphics_Object()
def mouseReleaseEvent(self, event)
self.addItem(self.graphics_item.graphics_pixItem)
self.graphics_item.graphics_pixItem.setPos(event.scenePos())
class Form(QtGui.QMainWindow):
def __init__(self):
super(Form, self).__init__()
self.ui = uic.loadUi('form.ui')
self.scene = graphicsScene()
self.ui.view.setScene(self.scene)
self.setMouseTracking(True)
You are making another pixmapItem inside you graphics_Object class, graphics_Object is already a subclass of graphicsPixmapItem so I don't see a purpose for this.
Then you only add that nested pixmapItem to the scene and your hoverEnterEvent is on the graphics_Object which is never added to the scene. That is why you are not receiving hover events.
One of many solutions would be to just add your graphics object to the scene instead of the nested graphicsPixmapItem and use setPixmap() in the init of graphics_Object
class graphics_Object(QtGui.QGraphicsPixmapItem):
def __init__(self, parent=None):
super(graphics_Object, self).__init__(parent)
pixmap = QtGui.QPixmap("item.png")
self.setPixmap(pixmap.scaled(40, 40, QtCore.Qt.KeepAspectRatio))
self.setFlag(QtGui.QGraphicsPixmapItem.ItemIsSelectable)
self.setFlag(QtGui.QGraphicsPixmapItem.ItemIsMovable)
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event):
print('hello')
class graphicsScene(QtGui.QGraphicsScene):
def __init__(self, parent=None):
super(graphicsScene, self).__init__(parent)
def mousePressEvent(self, event):
self.graphics_item = graphics_Object()
def mouseReleaseEvent(self, event):
print('adding to scene')
self.addItem(self.graphics_item)
self.graphics_item.setPos(event.scenePos())
When you inherit (subclass) from a pyqt class or any class in python, think of the new class as a "copy" of the inherited class. It will behave the exact same way as the base class until you overwrite a method, in this case, we override the "init" and "hoverEnterEvent" methods to do our custom stuff. Everything else about a QGraphicsPixmapItem stays the same
I need to open a QDialog in a different class of the QMainWindow, and after closing the QDialog, all the signals must be disconnected.
I can open the QDialog by pressing a combination of keys and then, when it is open instantly is connected to the button_pressedmethod which itself is connected to self.spanSelector_press and self.spanSelector_
This is the code so far:
class Window(QMainWindow): #This is a matplotlib figure
def __init__(self):
QMainWindow.__init__(self)
#A lot of stuff for the matplotlib figure
def button_pressed(self):
self.select_data = SelectData(self)
self.select_data.show()
self.cid_press = self.figure_canvas.mpl_connect('button_press_event',
self.spanSelector_press)
self.cid_release = self.figure_canvas.mpl_connect('button_release_event',
self.spanSelector_release)
def spanSelector_press(self, event):
if event.button ==1:
self.limite = "minimum"
self.clearMarker() #This is another method to erase the previous line drawn
self.marker = self.axes.axvline(Window.minimumCoords, linestyle='dashed',
linewidth = 2, color = "green")
self.figure_canvas.draw_idle()
Window.initial_marker = self.marker
self.xmin = event.xdata
def spanSelector_release(self, event):
pass
class SelectData(QDialog):
def __init__(self, parent):
QDialog.__init__(self, parent, Qt.WindowStaysOnTopHint)
def closeEvent(self, event):
#I need to disconnect all the signals when i close the QDialog
view = self.parent()
view.figure_canvas.mpl_disconnect(view.cid_press)
view.figure_canvas.mpl_disconnect(view.cid_release)
view.deselect()
event.accept()
How can i disconnect all the signals in the button_pressed method after closing the QDialog? Hope you can help me.
According to the matplotlib docs you can disconnect using the connection id, which you'll need to save by doing something like this in Window:
def button_pressed(self):
self.select_data = SelectData(self)
self.select_data.show()
self.cid_press = self.figure_canvas.mpl_connect('button_press_event', self.spanSelector_press)
self.cid_release = self.figure_canvas.mpl_connect('button_release_event', self.spanSelector_release)
Your SelectData class has a reference to its parent class (Window) that can be had by calling parent(), so you can use that to do the disconnection.
def closeEvent(self, event):
window = self.parent()
window.figure_canvas.mpl_disconnect(window.cid_press)
window.figure_canvas.mpl_disconnect(window.cid_release)
event.accept()
I haven't tested that, but it should be pretty close.
Edit: be sure to pass parent to QDialog constructor as such:
class SelectData(QDialog):
def __init__(self, parent):
QDialog.__init__(self, parent, Qt.WindowStaysOnTopHint)
I am working on set a click() event to QLineEdit, I already successfully did it. But I want to go back to Mainwindow when the QLine Edit is clicked because I need the data in Mainwindow to further process the data. But I failed to let it go back, neither nor to cite the Mainwindow as parent, I hope someone can point it out. Thank you so much.
MainWindow
{
...
self.tc = MyLineEdit(self.field[con.ConfigFields.VALUE])#self.tc = wx.TextCtrl(self.parent, -1, str(field[con.ConfigFields.VALUE]), pos=(x+220, y-3), size=(200, -1))
...
}
class MyLineEdit(QtGui.QLineEdit):
def __init__(self, parent=MainWindow):
super(MyLineEdit, self).__init__(parent)
#super(CustomQLineEidt, self).__init__()
def mousePressEvent(self, e):
self.mouseseleted()
def mouseseleted(self):
print "here"
MainWindow.mousePressEvent
I use the following to connect any method as the callback for a click event:
class ClickableLineEdit(QLineEdit):
clicked = pyqtSignal() # signal when the text entry is left clicked
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton: self.clicked.emit()
else: super().mousePressEvent(event)
To use:
textbox = ClickableLineEdit('Default text')
textbox.clicked.connect(someMethod)
Specifically for the op:
self.tc = ClickableLineEdit(self.field[con.ConfigFields.VALUE])
self.tc.clicked.connect(self.mouseseleted)
Just simply call the MainWindow mousePressEvent and give it the event variable the line edit received
class MyLineEdit(QtGui.QLineEdit):
def __init__(self, parent):
super(MyLineEdit, self).__init__(parent)
self.parentWindow = parent
def mousePressEvent(self, event):
print 'forwarding to the main window'
self.parentWindow.mousePressEvent(event)
Or you can connect a signal from the line edit
class MyLineEdit(QtGui.QLineEdit):
mousePressed = QtCore.pyqtProperty(QtGui.QMouseEvent)
def __init__(self, value):
super(MyLineEdit, self).__init__(value)
def mousePressEvent(self, event):
print 'forwarding to the main window'
self.mousePressed.emit(event)
Then just connect the signal in your main window where you created it
self.tc = MyLineEdit(self.field[con.ConfigFields.VALUE])#self.tc = wx.TextCtrl(self.parent, -1, str(field[con.ConfigFields.VALUE]), pos=(x+220, y-3), size=(200, -1))
self.tc.mousePressed[QtGui.QMouseEvent].connect(self.mousePressEvent)
This is what I used to do onClick for QLineEdits
class MyLineEdit(QtGui.QLineEdit):
def focusInEvent(self, e):
try:
self.CallBack(*self.CallBackArgs)
except AttributeError:
pass
super().focusInEvent(e)
def SetCallBack(self, callBack):
self.CallBack = callBack
self.IsCallBack = True
self.CallBackArgs = []
def SetCallBackArgs(self, args):
self.CallBackArgs = args
and in my MainGUI:
class MainGUI(..):
def __init__(...):
....
self.input = MyLineEdit()
self.input.SetCallBack(self.Test)
self.input.SetCallBackArgs(['value', 'test'])
...
def Test(self, value, test):
print('in Test', value, test)
I am trying to create tqo line edit and when i click on the line edit box i should be able to clear the current text.
I tried the below code but no success ,
Can someone point out what is wrong here?
OPTIONS = ['Enter IP Address','Number of iteration']
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, 'Details', parent=parent)
self.options = {}
for option in OptionBox.OPTIONS:
self.options[option] = (QtGui.QLineEdit(option))
self.connect(self.options[option], QtCore.SIGNAL("clicked()"), self.clicked)
self._gridOptions()
def clicked(self):
QLineEdit.clear()
You need to use an event filter on the QLineEdit to catch the click event in it (https://qt-project.org/doc/qt-5.1/qtcore/qobject.html#eventFilter). Here is an example on how the code should look like:
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, 'Details', parent=parent)
self.options = {}
for option in OptionBox.OPTIONS:
self.options[option] = QtGui.QLineEdit(option)
self.options[option].installEventFilter(self)
self._gridOptions()
def eventFilter(self, object, event):
if (object in self.options.values()) and (event.type() == QtCore.QEvent.MouseButtonPress):
object.clear()
return False # lets the event continue to the edit
return False
Edit: from what I understand, you just want a default text to appear in the QLineEdit which describe their role. This is a good opportunity to use the placeholderText. Here is the code modified to use it (there is no more need of the eventFilter method):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, 'Details', parent=parent)
self.options = {}
for option in OptionBox.OPTIONS:
self.options[option] = QtGui.QLineEdit()
self.options[option].setPlaceholderText(option)
self._gridOptions()