PyQt5: Notify when attribute's value is changed - python

First of all, please, look at the code sample, which is given below. How can I access, for example,.setDisabled(...) on QPushButton when the value of attribute self.markup_points inside QGraphicsView is changed? How I can implement this using pyqt signals or... use a singleton?
class ImageView(QtWidgets.QGraphicsView):
def __init__(self, parent):
super(ImageView, self).__init__(parent)
self.markup_points = []
...
...
def set_image(self, pixmap):
foo()
def mousePressEvent(self, event):
foo()
self.markup_points.append(QtCore.QPointF(bar()))
super(ImageView, self).mousePressEvent(event)
...
def keyPressEvent(self, event):
key = event.key()
modifiers = int(event.modifiers())
if (modifiers and modifiers & MOD_MASK == modifiers and
key > 0 and key != QtCore.Qt.Key_Control and key != QtCore.Qt.Key_Meta):
if key == 88:
self.remove_point()
def remove_point(self):
if len(self.markup_points):
self.markup_points.pop()
...
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
...
self.imageView = ImageView()
self.btnLoad.clicked.connect(self._load_combination)
self.btnSkip.clicked.connect(self._skip_combination)
self.btnSave.clicked.connect(self._save_objects)
# qpushbutton that I want to access later
self.btnRemove.clicked.connect(self.imageView.remove_point)
...
def event_if_something_is_changed_in_image_view(self):
self.btnRemove.setDisabled(True)

Why do you think a singleton is the solution? A singleton is an anti-pattern so it should be avoided and only in certain cases is it necessary, besides it has nothing to do with notifying about the changes, so discard it.
The solution is to create a signal that is issued when there is a change, and connect it to a slot that receives notifications:
class ImageView(QtWidgets.QGraphicsView):
markupPointsChanged = QtCore.pyqtSignal(list) # <---
def __init__(self, parent):
super(ImageView, self).__init__(parent)
self.markup_points = []
# ...
def mousePressEvent(self, event):
foo()
self.append_point(QtCore.QPointF(bar()))
super(ImageView, self).mousePressEvent(event)
def keyPressEvent(self, event):
key = event.key()
modifiers = int(event.modifiers())
if (modifiers and modifiers & MOD_MASK == modifiers and
key > 0 and key not in (QtCore.Qt.Key_Control, QtCore.Qt.Key_Meta)):
if key == QtCore.Qt.Key_X:
self.remove_point()
def append_point(self, p):
self.markup_points.append(p)
self.markupPointsChanged.emit(self.markup_points) # <---
def remove_point(self):
if self.markup_points:
self.markup_points.pop()
self.markupPointsChanged.emit(self.markup_points) # <---
# ...
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
# ...
self.imageView = ImageView()
self.btnLoad.clicked.connect(self._load_combination)
self.btnSkip.clicked.connect(self._skip_combination)
self.btnSave.clicked.connect(self._save_objects)
self.btnRemove.clicked.connect(self.imageView.remove_point)
self.imageView.markupPointsChanged.connect(self.on_markupPointsChanged) # <---
#QtCore.pyqtSlot(list)
def on_markupPointsChanged(self, points):
print(points)
self.btnRemove.setDisabled(True)

Related

How to add a menu only in the QTreeWidgetItem after pressing the right click? [duplicate]

I have the following code to create a QTreeWidget and a contextmenu with 2 actions.
import sys
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__()
self.tw = QtWidgets.QTreeWidget()
self.tw.setHeaderLabels(['Name', 'Cost ($)'])
cg = QtWidgets.QTreeWidgetItem(['carrots', '0.99'])
c1 = QtWidgets.QTreeWidgetItem(['carrot', '0.33'])
self.tw.addTopLevelItem(cg)
self.tw.addTopLevelItem(c1)
self.tw.installEventFilter(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tw)
def eventFilter(self, source: QtWidgets.QTreeWidget, event):
if (event.type() == QtCore.QEvent.ContextMenu and
source is self.tw):
menu = QtWidgets.QMenu()
AAction = QtWidgets.QAction("AAAAA")
AAction.triggered.connect(lambda :self.a(source.itemAt(event.pos())))
BAction = QtWidgets.QAction("BBBBBB")
BAction.triggered.connect(lambda :self.b(source, event))
menu.addAction(AAction)
menu.addAction(BAction)
menu.exec_(event.globalPos())
return True
return super(Dialog, self).eventFilter(source, event)
def a(self, item):
if item is None:
return
print("A: {}".format([item.text(i) for i in range(self.tw.columnCount())]))
def b(self, source, event):
item = source.itemAt(event.pos())
if item is None:
return
print("B: {}".format([item.text(i) for i in range(source.columnCount())]))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Dialog()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
When opening the contextmenu in the header and clicking on one of the actions it prints either carrot or carrots, depending on where in the contextmenu I click. But I give the position of right click event to the functions.
So why is this happening and what can I do to stop it?
Your code has 2 errors:
The main error is that the itemAt() method uses the coordinates with respect to the viewport() and not with respect to the view (the QTreeWidget) so you will get incorrect items (the header occupies a space making the positions with respect to the QTreeWidget and the viewport() have an offset).
You should not block the eventloop, for example blocking the eventFilter you may be blocking other events that would cause errors that are difficult to debug, in this case it is better to use a signal.
class Dialog(QtWidgets.QDialog):
rightClicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent=None):
super(Dialog, self).__init__()
self.tw = QtWidgets.QTreeWidget()
self.tw.setHeaderLabels(["Name", "Cost ($)"])
cg = QtWidgets.QTreeWidgetItem(["carrots", "0.99"])
c1 = QtWidgets.QTreeWidgetItem(["carrot", "0.33"])
self.tw.addTopLevelItem(cg)
self.tw.addTopLevelItem(c1)
self.tw.viewport().installEventFilter(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tw)
self.rightClicked.connect(self.handle_rightClicked)
def eventFilter(self, source: QtWidgets.QTreeWidget, event):
if event.type() == QtCore.QEvent.ContextMenu and source is self.tw.viewport():
self.rightClicked.emit(event.pos())
return True
return super(Dialog, self).eventFilter(source, event)
def handle_rightClicked(self, pos):
item = self.tw.itemAt(pos)
if item is None:
return
menu = QtWidgets.QMenu()
print_action = QtWidgets.QAction("Print")
print_action.triggered.connect(lambda checked, item=item: self.print_item(item))
menu.addAction(print_action)
menu.exec_(self.tw.viewport().mapToGlobal(pos))
def print_item(self, item):
if item is None:
return
texts = []
for i in range(item.columnCount()):
text = item.text(i)
texts.append(text)
print("B: {}".format(",".join(texts)))
Although it is unnecessary that you use an eventFilter to handle the contextmenu since a simpler solution is to set the contextMenuPolicy of the QTreeWidget to Qt::CustomContextMenu and use the customContextMenuRequested signal:
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__()
self.tw = QtWidgets.QTreeWidget()
self.tw.setHeaderLabels(["Name", "Cost ($)"])
cg = QtWidgets.QTreeWidgetItem(["carrots", "0.99"])
c1 = QtWidgets.QTreeWidgetItem(["carrot", "0.33"])
self.tw.addTopLevelItem(cg)
self.tw.addTopLevelItem(c1)
self.tw.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tw.customContextMenuRequested.connect(self.handle_rightClicked)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tw)
def handle_rightClicked(self, pos):
item = self.tw.itemAt(pos)
if item is None:
return
menu = QtWidgets.QMenu()
print_action = QtWidgets.QAction("Print")
print_action.triggered.connect(lambda checked, item=item: self.print_item(item))
menu.addAction(print_action)
menu.exec_(self.tw.viewport().mapToGlobal(pos))
def print_item(self, item):
if item is None:
return
texts = []
for i in range(item.columnCount()):
text = item.text(i)
texts.append(text)
print("B: {}".format(",".join(texts)))

Remove drop shadow in leaveEvent

I want to remove the drop shadow effect in leaveEvent. Here's my code:
class GroupBox(QGroupBox):
def __init__(self, parent=None, shadow=True):
super(GroupBox, self).__init__(parent)
self.isShadow = shadow
if self.isShadow == True:
self.shadow = QGraphicsDropShadowEffect()
elif self.isShadow == False:
self.shadow = None
def enterEvent(self, event):
if self.shadow != None:
self.shadow.setBlurRadius(10)
self.shadow.setOffset(0, 0)
self.setGraphicsEffect(self.shadow)
def leaveEvent(self, event):
self.shadow = None
Any suggestions on how to fix this?
If you want to remove the QGraphicsEffect then you must pass None to the setGraphicsEffect method but the side effect is that the QGraphicsEffect will be removed so you have to re-create the QGraphicsEffect:
class GroupBox(QGroupBox):
def __init__(self, parent=None, shadow=True):
super(GroupBox, self).__init__(parent)
self._is_shadow = shadow
#property
def is_shadow(self):
return self._is_shadow
def enterEvent(self, event):
if self.is_shadow:
shadow_effect = QGraphicsDropShadowEffect(
blurRadius=10, offset=QPointF(0, 0)
)
self.setGraphicsEffect(shadow_effect)
def leaveEvent(self, event):
if self.is_shadow:
self.setGraphicsEffect(None)

Pass Data into a QThread in Python GUI

I'm trying to pass some parameters (No. of Loop Iterations, counter values, etc) into a run method in a Qt GUI Thread.
Everything work pretty well, including the Thread, SIGNAL and SLOT, expect when I try to pass in a value to the run method. When I pass in an integer value e.g. 100, there is an error at the run method. When print what the value is at the run method, the out put is a Object Memory Address e.g. <main.Main object at 0x02BC27B0>.
How do I fix this?.
Thank you for your help in advance.
Here is a sample of the Python Code:
#class testThread(QtCore.QThread):
def __init__(self,val, parent=None):
QtCore.QThread.__init__(self)
self.pulseTime = 0.002
self.counter = 0
self.icount = 0
self.timeout = 4
self.default = 99
self.val = val
print self.val
def run(self):
self.timer = QtCore.QTimer()
self.timer.setInterval(1000)
self.timer.setSingleShot(False)
self.timer.start()
self.timer.timeout.connect(self.update_mode)
self.exec_()
def update_mode(self):
self.counter += self.val
self.emit(QtCore.SIGNAL("int"),self.counter)
def stop(self):
self.terminate()
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.ui = Ui_Main()
self.ui.setupUi(self)
self._active = False
self.val = 100
self.setupSignals()
def spinBox_Value(self):
self.val = self.ui.spinBox.value()
def startPressed(self):
self.testThreadCall.start()
if not self._active:
self._active = True
self.ui.pButton_StartStop.setText("Stop")
else:
self._active = False
self.testThreadCall.stop()
print self._active
def setupSignals(self):
print "Val", self.val
self.testThreadCall = testThread(self, self.val)
self.testThreadCall.setTerminationEnabled(True)
QtCore.QObject.connect(self.testThreadCall, QtCore.SIGNAL("int"),self.labelSet)
self.ui.dopDown.addItems(['Default','Second Set','Third Set'])
self.ui.dopDown.currentIndexChanged.connect(self.dropDownEvent)
self.ui.pButton_StartStop.clicked.connect(self.startPressed)
self.ui.spinBox.valueChanged.connect(self.spinBox_Value)
def closeEvent(self, event):
self.testThreadCall.quit()
###if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
The class testThread is defined with:
class testThread(QtCore.QThread):
def __init__(self, val, parent=None):
...
Later, you create an instance of testThread like this:
self.testThreadCall = testThread(self, self.val)
So testThread.val is set to self (= the main window), and testThread.parent is set to self.val. Replacing (self, self.val) by (self.val, self) should fix your problem.

Add a click on QLineEdit

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)

PyQt: How to get QListWidget's listItem .data() on dropEvent

There are two QListWidgets. Every list item was assigned MyClass()'s instance using setData(QtCore.Qt.UserRole, myClassInstA).
Clicking a lower QListWidget's listItem prints out a clicked listItem's data retrieved using:
.data(QtCore.Qt.UserRole).toPyObject()
Any itemA dragged and dropped from top QListWidget onto a lower shows None for data.
Interesting that clicking the same item prints there is a data. I wonder if it is possible to retrieve a data stored in listItem within droppedOnB() function (so droppedOnB() is able to print out the data stored in a item).
from PyQt4 import QtGui, QtCore
import sys, os
class MyClass(object):
def __init__(self):
super(MyClass, self).__init__()
class ThumbListWidget(QtGui.QListWidget):
_drag_info = []
def __init__(self, type, name, parent=None):
super(ThumbListWidget, self).__init__(parent)
self.setObjectName(name)
self.setIconSize(QtCore.QSize(124, 124))
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setAcceptDrops(True)
self._dropping = False
def startDrag(self, actions):
self._drag_info[:] = [str(self.objectName())]
for item in self.selectedItems():
self._drag_info.append(self.row(item))
super(ThumbListWidget, self).startDrag(actions)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
super(ThumbListWidget, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
super(ThumbListWidget, self).dragMoveEvent(event)
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.emit(QtCore.SIGNAL("dropped"), links)
else:
# event.setDropAction(QtCore.Qt.MoveAction)
self._dropping = True
super(ThumbListWidget, self).dropEvent(event)
self._dropping = False
def rowsInserted(self, parent, start, end):
if self._dropping:
self._drag_info.append((start, end))
self.emit(QtCore.SIGNAL("dropped"), self._drag_info)
super(ThumbListWidget, self).rowsInserted(parent, start, end)
class Dialog_01(QtGui.QMainWindow):
def __init__(self):
super(QtGui.QMainWindow,self).__init__()
self.listItems={}
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.listWidgetA = ThumbListWidget(self, 'MainTree')
self.listWidgetB = ThumbListWidget(self, 'SecondaryTree')
self.listWidgetB.setDragDropMode(QtGui.QAbstractItemView.DropOnly)
for i in range(7):
listItemA=QtGui.QListWidgetItem()
listItemA.setText('A'+'%04d'%i)
self.listWidgetA.addItem(listItemA)
myClassInstA=MyClass()
listItemA.setData(QtCore.Qt.UserRole, myClassInstA)
listItemB=QtGui.QListWidgetItem()
listItemB.setText('B'+'%04d'%i)
self.listWidgetB.addItem(listItemB)
myClassInstB=MyClass()
listItemB.setData(QtCore.Qt.UserRole, myClassInstB)
myBoxLayout.addWidget(self.listWidgetA)
myBoxLayout.addWidget(self.listWidgetB)
self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)
self.listWidgetB.clicked.connect(self.itemClicked)
def droppedOnB(self, dropped_list):
print '\n\t droppedOnB()', dropped_list, dropped_list[-1]
destIndexes=dropped_list[-1]
for index in range(destIndexes[0],destIndexes[-1]+1):
dropedItem=self.listWidgetB.item(index)
modelIndex=self.listWidgetB.indexFromItem(dropedItem)
dataObject = modelIndex.data(QtCore.Qt.UserRole).toPyObject()
print '\n\t\t droppedOnB()', type(modelIndex), type(dataObject)
def itemClicked(self, modelIndex):
dataObject = modelIndex.data(QtCore.Qt.UserRole).toPyObject()
print 'itemClicked(): ' ,type(modelIndex), type(dataObject)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
dialog_1.resize(720,480)
sys.exit(app.exec_())
The ultimate goal was to replace MyClassA's instance data-object (attached to itemA) with MyClassB after itemA is dropped onto listWidgetB.
When itemA is dropped onto the destination listWidgetB the itemA arrives with no data stored. It returns None if you try to retrieve it with itemA.data(QtCore.Qt.UserRole).toPyObject() (if done inside of droppedOnB() - a method triggered first onDrop event).
Trying to assign, reassign a data to just dropped listItem... or even removing it causes all kind of surprises later. I am using .setHidden(True).
Those are the steps:
Get source listItem and its data.
Get destination (dropped) item and hide it.
Declare MyClassB() instance
Create new list item. Assign created in previous step MyClassB() instance object to listItem using .setData(QtCore.Qt.UserRole, myClassInstB)
Add new listItem to ListWidget.
Here is a functional code. Once again, itemA is assign an instance of ClassA on creation.
After itemA is dropped onto listWidgetB a dropped item is hidden and 'substituted' with another item to which classB instance is assigned.
from PyQt4 import QtGui, QtCore
import sys, os, copy
class MyClassA(object):
def __init__(self):
super(MyClassA, self).__init__()
class MyClassB(object):
def __init__(self):
super(MyClassB, self).__init__()
self.DataObjectCopy=None
class ThumbListWidget(QtGui.QListWidget):
_drag_info = []
def __init__(self, type, name, parent=None):
super(ThumbListWidget, self).__init__(parent)
self.setObjectName(name)
self.setIconSize(QtCore.QSize(124, 124))
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setAcceptDrops(True)
self._dropping = False
def startDrag(self, actions):
self._drag_info[:] = [str(self.objectName())]
for item in self.selectedItems():
self._drag_info.append(self.row(item))
super(ThumbListWidget, self).startDrag(actions)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
super(ThumbListWidget, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
super(ThumbListWidget, self).dragMoveEvent(event)
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.emit(QtCore.SIGNAL("dropped"), links)
else:
# event.setDropAction(QtCore.Qt.MoveAction)
self._dropping = True
super(ThumbListWidget, self).dropEvent(event)
self._dropping = False
def rowsInserted(self, parent, start, end):
if self._dropping:
self._drag_info.append((start, end))
self.emit(QtCore.SIGNAL("dropped"), self._drag_info)
super(ThumbListWidget, self).rowsInserted(parent, start, end)
class Dialog_01(QtGui.QMainWindow):
def __init__(self):
super(QtGui.QMainWindow,self).__init__()
self.listItems={}
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.listWidgetA = ThumbListWidget(self, 'MainTree')
self.listWidgetB = ThumbListWidget(self, 'SecondaryTree')
self.listWidgetB.setDragDropMode(QtGui.QAbstractItemView.DropOnly)
for i in range(3):
listItemA=QtGui.QListWidgetItem()
listItemA.setText('A'+'%04d'%i)
self.listWidgetA.addItem(listItemA)
myClassInstA=MyClassA()
listItemA.setData(QtCore.Qt.UserRole, myClassInstA)
listItemB=QtGui.QListWidgetItem()
listItemB.setText('B'+'%04d'%i)
self.listWidgetB.addItem(listItemB)
myClassInstB=MyClassB()
listItemB.setData(QtCore.Qt.UserRole, myClassInstB)
myBoxLayout.addWidget(self.listWidgetA)
myBoxLayout.addWidget(self.listWidgetB)
self.connect(self.listWidgetB, QtCore.SIGNAL("dropped"), self.droppedOnB)
self.listWidgetB.clicked.connect(self.itemClicked)
def droppedOnB(self, dropped_list):
if not dropped_list or len(dropped_list)<3: return
srcIndexes=dropped_list[1:-1]
destIndexes=dropped_list[-1]
# build a list of corresponding src-to-dest indexes sets
itemsIndxes=[]
i=0
for num in range(destIndexes[0], destIndexes[-1]+1):
itemsIndxes.append((srcIndexes[i], num))
i+=1
print '\n\t droppedOnB(): dropped_list =',dropped_list,'; srcIndexes =',srcIndexes,'; destIndexes =',destIndexes, '; itemsIndxes =',itemsIndxes
for indexSet in itemsIndxes:
srcNum = indexSet[0]
destIndxNum = indexSet[-1]
# Get source listItem's data object
srcItem=self.listWidgetA.item(srcNum)
if not srcItem: continue
srcItemData = srcItem.data(QtCore.Qt.UserRole)
if not srcItemData: continue
srcItemDataObject=srcItemData.toPyObject()
if not srcItemDataObject: continue
# make a deepcopy of src data object
itemDataObject_copy=copy.deepcopy(srcItemDataObject)
# get dropped item
droppedItem=self.listWidgetB.item(destIndxNum)
# hide dropped item since removing it results to mess
droppedItem.setHidden(True)
# declare new ClassB instance
myClassInstB=MyClassB()
myClassInstB.DataObjectCopy=itemDataObject_copy
# create a new listItem
newListItem=QtGui.QListWidgetItem()
newListItem.setData(QtCore.Qt.UserRole, myClassInstB)
newListItem.setText(srcItem.text())
self.listWidgetB.blockSignals(True)
self.listWidgetB.addItem(newListItem)
self.listWidgetB.blockSignals(False)
def itemClicked(self, modelIndex):
dataObject = modelIndex.data(QtCore.Qt.UserRole).toPyObject()
print 'itemClicked(): instance type:', type(dataObject)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
dialog_1.resize(720,480)
sys.exit(app.exec_())

Categories