How to add logic to QListWidget drag/drop - pyqt

I'm a Python/PyQt (v4) newbie who is subclassing the QListWidget and making use of its internal drag and drop mechanism:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class AlgorithmListControl(QListWidget):
def __init__(self, parent = None):
super(AlgorithmListControl, self).__init__(parent)
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
I'm happy with the default behavior and appearance and I'm able to drag/drop items internally in the widget. Now, I'd like to add some custom logic that occurs when the drag operation begins and after the drop operation ends. The trouble I'm having is if I handle the events, for example the dragEnterEvent as follows:
def dragEnterEvent(self, event):
...
then I no longer get the nice default drag/drop behavior (I'm unable to drag an item to a new location, I don't get the horizontal insertion indicator, etc). How do I retain the standard drag-drop behavior but add my custom logic at these two event points? Thank you in advance.

Since you override the method when you use the same name, you need to implement all the actions that happen in the standard way, or call the parent (standard) method inside.
You can call the method from the parent class with inheritance, thanks to the function super()and then implement your own logic :
def dragEnterEvent(self, event):
super().dragEnterEvent(event)
print('enter')

Related

Make QTableWidgetItem non-editable, but still able to select parts of a string

I have a QTableWidget with data that I would like the user to be able to double-click on, and copy a specific part of the text. The only thing I want to disable is the user's ability to change that text, meaning that setting the flag ~Qt.ItemIsEditable is too restrictive. How can I achieve this in PyQt5?
Note: This solution works for all kinds of classes that inherit from QAbstractItemView like QTableWidget or QTreeWidget.
A possible solution is to use a modify the editor to be read-only through a delegate.
class StyledItemDelegate(QStyledItemDelegate):
def createEditor(self, *args):
editor = super().createEditor(*args)
if isinstance(editor, QLineEdit):
editor.setReadOnly(True)
return editor
delegate = StyledItemDelegate(view)
view.setItemDelegate(delegate)

PyQt: how to create a dialog as a child of another dialog

In PyQt, I have a dialog that spawns another dialog (when you click a button to do so in the first dialog). I want to maintain a strong parent-child relationship, for garbage collection purposes, and to make the .findChild and .findChildren functions usable.
The root of the question may be: how do you use .setParent() but still have the object in question be shown as a separate window, rather than shown within the parent widget?
The 'parent' dialog (actually a container widget within a tab within a dialog) is 'newEntryWidget'. It spawns 'clueDialog' when a signal (not shown here) calls newEntryWidget.quickTextClueAction as a slot. Visually, clueDialog should be a "top level window" with its own banner, its own window attributes (I want to keep it on top of everything else), etc.
class newEntryWidget(QWidget,Ui_newEntryWidget):
def __init__(self,parent,sec=0,formattedLocString='',fleet='',dev='',origLocString='',amendFlag=False,amendRow=None):
QDialog.__init__(self)
self.parent=parent # just an attribute; not the same as setParent
...
...
def quickTextClueAction(self):
self.newClueDialog=clueDialog(self,self.ui.timeField.text(),self.ui.teamField.text(),self.ui.radioLocField.text(),lastClueNumber+1)
self.newClueDialog.show()
class clueDialog(QDialog,Ui_clueDialog):
def __init__(self,parent,t,callsign,radioLoc,newClueNumber):
QDialog.__init__(self)
self.parent=parent # just an attribute; not the same as setParent
...
...
Right now, since I am using self.parent=parent, which is just an attribute and not true "parent/child relationship" in Qt terms, clueDialog does display as a top level window, which is what I want:
But, if I add 'self.setParent(parent)' in the clueDialog init function, I get this:
How can I preserve the top-level-window behavior, and have the real-honest-parent-child-relationship so that .findChild(clueDialog) will work from within the newEntryWidget object?
Ultimately, I want to enforce that the newEntryWidget object should not be closed if it still has and 'child' clueDialogs open.
Instead of calling .setParent, call QDialog.__init__(self, parent) which constructs the clue dialog with a parent from the beginning. Setting it this way allows Qt establishes the parent-child relationship at the beginning of clueDialog's lifetime.
I believe this will fix both your issues: 1) the clue window frame, caption, etc. will be painted, 2) you will be able to iterate for proper children of newEntry.

In Python3/tkinter is there a way to temporarily stop accepting clicks in a Treeview widget?

I have a GUI based in Python 3 and tkinter that has a big ttk.Treeview. I have defined methods for row selection (one click) and opening an advanced info panel (double-click). I need to ensure that, after being double-clicked, for the next one or two seconds, the Treeview state won't be changed by another click. Is it possible to deactivate Treeview mouse bindings, like what we do with buttons?
Doing a little more research, I was able to come up with a solution for this. I just created an empty method that is called when the tree widget is supposed to be inactive. So, we can use something like this to "unbind" all the mouse events and re-bind them a few seconds later, as needed:
def nothing(self, *event):
""" # Hacking moment: A function that does nothing, for those times you need it...
"""
pass
def bind_tree(self):
""" # Bind mouse and keyboard events to their respective functions or methods...
"""
self.tree.bind('<<TreeviewSelect>>', self.selectItem_popup)
self.tree.bind('<Double-1>', self.show_details)
self.tree.bind("<Button-2>", self.popupMenu)
self.tree.bind("<Button-3>", self.popupMenu)
def unbind_tree(self):
""" # Unbind all mouse and keyboard events, by binding them to an empty method...
"""
self.tree.bind('<<TreeviewSelect>>', self.nothing)
self.tree.bind('<Double-1>', self.nothing)
self.tree.bind("<Button-2>", self.nothing)
self.tree.bind("<Button-3>", self.nothing)
Then, in the rest of the code, We only need to call bind_tree() and unbind_tree() as needed.
This worked for me:
tree.bind("<ButtonRelease-1>", my_select_function)
# Do some stuff
tree.unbind("<ButtonRelease-1>")

PyQt4: disable deleting widget after setCentralWidget

I have two custom widgets (two classes based on QtGui.QWidget). In __init__ of QtGui.QMainWindow I create their instances:
self.MyWidget1 = MyWidget1()
self.MyWidget2 = MyWidget2()
There are also two buttons (QtGui.QPushButton) in __init__ part, and there are two slots when user clicks each of them:
def clickButton1(self):
self.setCentralWidget(self.MyWidget1)
def clickButton2(self):
self.setCentralWidget(self.MyWidget2)
But it works only on first click and then PyQt says that underlying widget (MyWidget1 or MyWidget2) was deleted. I think it was done by sip module. Is there a way to prevent deleting widgets after reseting of central widget? Thanks!
I think it's almost impossible.
From setCentralWidget docs:
Note: QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time.
So, you should create new MyWidget instance.
def clickButton2(self):
self.setCentralWidget(MyWidget2())
But the right way of doing such things is to use QStackedWidget

capture delete key in CListCtrl and do soem processing

I have a class which inherits from CListCtrl class, say class list.
I have another class dlg, which inherits from CDialog.
Class dlg contains an instance of class list.
I have got a delete button in class dlg, on which I delete the selected item in listCtrl and do lots of other processing.
I want the same functionality on delete key.
I added OnKeyDown() fn is my class list, where I can capture VK_DELETE key. But my problem is that, how do I do otehr processing that I need to do in dialog class.
All that processing is dlg class based not list class based.
I have many such dlg classes with different data and in every dlg class processing is different.
I tried capturing VK_DELETE in dialog class, but it doesn't capture it if focus is on list class.
I am totally stuck and have no idea, how to do this.
Please give me some idea how i can do this.
Thanks,
SG
What about delegating the call captured in the List class to the parent Dialog class. Thus you capture the VK_DELETE on the List class and say to the parent that you received a Delete command. Thus you can keep all your processing on the parent Dialog class if you wish.
((CMyParentDialog*) GetParent())->OnDeleteKeyPressed(this);
Or better, create a custom message and post it to the parent window.
#define W_DELETE_PRESSED_ON_LIST (WM_USER + 1)
GetParent()->PostMessage(WM_DELETE_PRESSED_ON_LIST);

Resources