Differences between revisions 8 and 9
Revision 8 as of 2020-03-15 17:16:51
Size: 17876
Editor: Ecco
Comment:
Revision 9 as of 2020-03-21 14:46:34
Size: 18116
Editor: Ecco
Comment:
Deletions are marked like this. Additions are marked like this.
Line 294: Line 294:
        
Line 302: Line 302:
        
Line 318: Line 318:
            
Line 326: Line 326:
        self.Layout()

        #------------
        self.Layout()        

        #------------
        
Line 331: Line 331:
        
Line 339: Line 339:
        
Line 359: Line 359:
        
Line 364: Line 364:
 
Line 374: Line 374:
        
Line 384: Line 384:
        self.SetCursor(wx.Cursor(wx.CURSOR_HAND))
        
Line 394: Line 396:
        
Line 411: Line 413:
        self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
        
Line 461: Line 465:
        
Line 486: Line 490:
        
Line 495: Line 499:
        
Line 505: Line 509:
class MyApp(wx.App): class MyApp(wx.App):    
Line 515: Line 519:
    

List control and drag and drop - DnD (Phoenix)

Keywords : ListCtrl, Drag and drop.


Demonstrating :

Tested py3.x, wx4.x and Win10.

Are you ready to use some samples ? ;)

Test, modify, correct, complete, improve and share your discoveries ! (!)


Sample one

img_sample_one.png

Here's how you can use drag-and-drop to reorder a simple list, or to move items from one list to another.

Toggle line numbers
   1 # sample_one.py
   2 
   3 """
   4 
   5 Drag and drop (DnD) demo with listctrl :
   6 - Dragging of multiple selected items.
   7 - Dropping on an empty list.
   8 - Dropping of items on a list with a different number of columns.
   9 - Dropping on a different applications.
  10 
  11 """
  12 
  13 import pickle
  14 import sys
  15 from   random import choice
  16 import wx
  17 
  18 # class MyFrame
  19 # class MyDragList
  20 # class MyListDrop
  21 # class MyApp
  22 
  23 #---------------------------------------------------------------------------
  24 
  25 items = ['Foo', 'Bar', 'Baz', 'Zif', 'Zaf', 'Zof']
  26 
  27 #---------------------------------------------------------------------------
  28 
  29 class MyFrame(wx.Frame):
  30     def __init__(self, parent, id):
  31         wx.Frame.__init__(self, parent, id,
  32                           "Sample one",
  33                           size=(450, 295))
  34 
  35         #------------
  36 
  37         self.SetIcon(wx.Icon('wxwin.ico'))
  38         self.SetMinSize((450, 295))
  39 
  40         #------------
  41 
  42         dl1 = MyDragList(self, style=wx.LC_LIST)
  43         dl1.SetBackgroundColour("#e6ffd0")
  44 
  45         dl2 = MyDragList(self, style=wx.LC_REPORT)
  46         dl2.InsertColumn(0, "Column - 0", wx.LIST_FORMAT_LEFT)
  47         dl2.InsertColumn(1, "Column - 1", wx.LIST_FORMAT_LEFT)
  48         dl2.InsertColumn(2, "Column - 2", wx.LIST_FORMAT_LEFT)
  49         dl2.SetBackgroundColour("#f0f0f0")
  50 
  51         maxs = -sys.maxsize - 1
  52 
  53         for item in items:
  54             dl1.InsertItem(maxs, item)
  55             idx = dl2.InsertItem(maxs, item)
  56             dl2.SetItem(idx, 1, choice(items))
  57             dl2.SetItem(idx, 2, choice(items))
  58 
  59         #------------
  60 
  61         sizer = wx.BoxSizer(wx.HORIZONTAL)
  62 
  63         sizer.Add(dl1, proportion=1, flag=wx.EXPAND)
  64         sizer.Add(dl2, proportion=1, flag=wx.EXPAND)
  65 
  66         self.SetSizer(sizer)
  67         self.Layout()
  68 
  69 #---------------------------------------------------------------------------
  70 
  71 class MyDragList(wx.ListCtrl):
  72     def __init__(self, *arg, **kw):
  73         wx.ListCtrl.__init__(self, *arg, **kw)
  74 
  75         #------------
  76 
  77         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.StartDrag)
  78 
  79         #------------
  80 
  81         dt = MyListDrop(self)
  82         self.SetDropTarget(dt)
  83 
  84     #-----------------------------------------------------------------------
  85 
  86     def GetItemInfo(self, idx):
  87         """
  88         Collect all relevant data of a listitem, and put it in a list.
  89         """
  90 
  91         l = []
  92         l.append(idx) # We need the original index, so it is easier to eventualy delete it.
  93         l.append(self.GetItemData(idx)) # Itemdata.
  94         l.append(self.GetItemText(idx)) # Text first column.
  95         for i in range(1, self.GetColumnCount()): # Possible extra columns.
  96             l.append(self.GetItem(idx, i).GetText())
  97         return l
  98 
  99 
 100     def StartDrag(self, event):
 101         """
 102         Put together a data object for drag-and-drop _from_ this list.
 103         """
 104 
 105         l = []
 106         idx = -1
 107         while True: # Find all the selected items and put them in a list.
 108             idx = self.GetNextItem(idx, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED)
 109             if idx == -1:
 110                 break
 111             l.append(self.GetItemInfo(idx))
 112 
 113         # Pickle the items list.
 114         itemdata = pickle.dumps(l, 1)
 115         # Create our own data format and use it
 116         # in a Custom data object.
 117         ldata = wx.CustomDataObject("ListCtrlItems")
 118         ldata.SetData(itemdata)
 119         # Now make a data object for the  item list.
 120         data = wx.DataObjectComposite()
 121         data.Add(ldata)
 122 
 123         # Create drop source and begin drag-and-drop.
 124         dropSource = wx.DropSource(self)
 125         dropSource.SetData(data)
 126         res = dropSource.DoDragDrop(flags=wx.Drag_DefaultMove)
 127 
 128         # If move, we want to remove the item from this list.
 129         if res == wx.DragMove:
 130             # It's possible we are dragging/dropping from this list to this list.
 131             # In which case, the index we are removing may have changed...
 132 
 133             # Find correct position.
 134             l.reverse() # Delete all the items, starting with the last item.
 135             for i in l:
 136                 pos = self.FindItem(i[0], i[2])
 137                 self.DeleteItem(pos)
 138 
 139 
 140     def Insert(self, x, y, seq):
 141         """
 142         Insert text at given x, y coordinates --- used with drag-and-drop.
 143         """
 144 
 145         # Find insertion point.
 146         index, flags = self.HitTest((x, y))
 147 
 148         if index == wx.NOT_FOUND: # Not clicked on an item.
 149             if flags & (wx.LIST_HITTEST_NOWHERE|wx.LIST_HITTEST_ABOVE|wx.LIST_HITTEST_BELOW): # Empty list or below last item.
 150                 index = self.GetItemCount() # Append to end of list.
 151             elif self.GetItemCount() > 0:
 152                 if y <= self.GetItemRect(0).y: # Clicked just above first item.
 153                     index = 0 # Append to top of list.
 154                 else:
 155                     index = self.GetItemCount() + 1 # Append to end of list.
 156         else: # Clicked on an item.
 157             # Get bounding rectangle for the item the user is dropping over.
 158             rect = self.GetItemRect(index)
 159 
 160             # If the user is dropping into the lower half of the rect,
 161             # we want to insert _after_ this item.
 162             # Correct for the fact that there may be a heading involved.
 163             if y > rect.y - self.GetItemRect(0).y + rect.height/2:
 164                 index += 1
 165 
 166         for i in seq: # Insert the item data.
 167             idx = self.InsertItem(index, i[2])
 168             self.SetItemData(idx, i[1])
 169             for j in range(1, self.GetColumnCount()):
 170                 try: # Target list can have more columns than source.
 171                     self.SetItem(idx, j, i[2+j])
 172                 except:
 173                     pass # Ignore the extra columns.
 174             index += 1
 175 
 176 #---------------------------------------------------------------------------
 177 
 178 class MyListDrop(wx.DropTarget):
 179     """
 180     Drop target for simple lists.
 181     """
 182     def __init__(self, source):
 183         """
 184         Arguments:
 185         source: source listctrl.
 186         """
 187         wx.DropTarget.__init__(self)
 188 
 189         #------------
 190 
 191         self.dv = source
 192 
 193         #------------
 194 
 195         # Specify the type of data we will accept.
 196         self.data = wx.CustomDataObject("ListCtrlItems")
 197         self.SetDataObject(self.data)
 198 
 199     #-----------------------------------------------------------------------
 200 
 201     # Called when OnDrop returns True.
 202     # We need to get the data and do something with it.
 203     def OnData(self, x, y, d):
 204         """
 205         ...
 206         """
 207 
 208         # Copy the data from the drag source to our data object.
 209         if self.GetData():
 210             # Convert it back to a list and give it to the viewer.
 211             ldata = self.data.GetData()
 212             l = pickle.loads(ldata)
 213             self.dv.Insert(x, y, l)
 214 
 215         # What is returned signals the source what to do
 216         # with the original data (move, copy, etc.)  In this
 217         # case we just return the suggested value given to us.
 218         return d
 219 
 220 #---------------------------------------------------------------------------
 221 
 222 class MyApp(wx.App):
 223     def OnInit(self):
 224 
 225         #------------
 226 
 227         frame = MyFrame(parent=None, id=-1)
 228         self.SetTopWindow(frame)
 229         frame.Show(True)
 230 
 231         return True
 232 
 233 #---------------------------------------------------------------------------
 234 
 235 def main():
 236     app = MyApp(False)
 237     app.MainLoop()
 238 
 239 #---------------------------------------------------------------------------
 240 
 241 if __name__ == "__main__" :
 242     main()


Sample two

img_sample_two.png

This striped drag list allows for dragging and dropping within itself while maintaining its appearance.

While not used in the example stub, it'll also stripe itself on an insert event (like from a pop-up file dialog) or on a delete event.

Toggle line numbers
   1 # sample_two.py
   2 
   3 """
   4 
   5 Drag and Drop with a striped drag list.
   6 
   7 """
   8 
   9 import wx
  10 
  11 # class MyFrame
  12 # class MyDragListStriped
  13 # class MyApp
  14 
  15 #---------------------------------------------------------------------------
  16 
  17 firstNameList = ["Ben", "Bruce", "Clark", "Dick", "Tom", "Jerry", "John"]
  18 lastNameList  = ["Grimm","Wayne", "Kent", "Grayson", "Pete", "Black", "Martin"]
  19 superNameList = ["30", "20", "40", "56", "26", "32", "89"]
  20 
  21 #---------------------------------------------------------------------------
  22         
  23 class MyFrame(wx.Frame):
  24     def __init__(self, parent, id):
  25         wx.Frame.__init__(self, parent, id,
  26                           "Sample two (drag list striped)",
  27                           size=(400, 210))
  28 
  29         #------------
  30         
  31         self.SetIcon(wx.Icon('wxwin.ico'))
  32         self.SetMinSize((400, 210))
  33 
  34         #------------
  35 
  36         dls = MyDragListStriped(self, style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
  37         dls.InsertColumn(0, "First name", wx.LIST_FORMAT_LEFT, 125)
  38         dls.InsertColumn(1, "Last name", wx.LIST_FORMAT_LEFT, 125)
  39         dls.InsertColumn(2, "Age", wx.LIST_FORMAT_LEFT, 130)
  40         dls.SetBackgroundColour("#f0f0f0")  #f0f0f0
  41 
  42         for index in range(len(firstNameList)):
  43             dls.InsertItem(index, firstNameList[index])
  44             dls.SetItem(index, 1, lastNameList[index])
  45             dls.SetItem(index, 2, superNameList[index])
  46             
  47         #------------
  48 
  49         sizer = wx.BoxSizer(wx.HORIZONTAL)
  50 
  51         sizer.Add(dls, proportion=1, flag=wx.EXPAND)
  52 
  53         self.SetSizer(sizer)
  54         self.Layout()        
  55 
  56         #------------
  57         
  58         dls._onStripe()
  59         
  60 #---------------------------------------------------------------------------
  61 
  62 class MyDragListStriped(wx.ListCtrl):
  63     def __init__(self, *arg, **kw):
  64         wx.ListCtrl.__init__(self, *arg, **kw)
  65 
  66         #------------
  67         
  68         self.Bind(wx.EVT_LIST_BEGIN_DRAG, self._onDrag)
  69         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._onSelect)
  70         self.Bind(wx.EVT_LEFT_UP,self._onMouseUp)
  71         self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
  72         self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeaveWindow)
  73         self.Bind(wx.EVT_ENTER_WINDOW, self._onEnterWindow)
  74         self.Bind(wx.EVT_LIST_INSERT_ITEM, self._onInsert)
  75         self.Bind(wx.EVT_LIST_DELETE_ITEM, self._onDelete)
  76 
  77         #------------
  78         # Variables.
  79         #------------
  80         self.IsInControl = True
  81         self.startIndex = -1
  82         self.dropIndex = -1
  83         self.IsDrag = False
  84         self.dragIndex = -1
  85 
  86     #-----------------------------------------------------------------------
  87         
  88     def _onLeaveWindow(self, event):
  89         """
  90         ...
  91         """
  92  
  93         self.IsInControl = False
  94         self.IsDrag = False
  95         event.Skip()
  96 
  97 
  98     def _onEnterWindow(self, event):
  99         """
 100         ...
 101         """
 102         
 103         self.IsInControl = True
 104         event.Skip()
 105 
 106 
 107     def _onDrag(self, event):
 108         """
 109         ...
 110         """
 111 
 112         self.SetCursor(wx.Cursor(wx.CURSOR_HAND))
 113         
 114         self.IsDrag = True
 115         self.dragIndex = event.Index
 116         event.Skip()
 117         pass
 118 
 119 
 120     def _onSelect(self, event):
 121         """
 122         ...
 123         """
 124         
 125         self.startIndex = event.Index
 126         event.Skip()
 127 
 128 
 129     def _onMouseUp(self, event):
 130         """
 131         Purpose : to generate a dropIndex.
 132         Process : check self.IsInControl, check self.IsDrag, HitTest, compare HitTest value
 133         The mouse can end up in 5 different places :
 134         - Outside the Control,
 135         - On itself,
 136         - Above its starting point and on another item,
 137         - Below its starting point and on another item,
 138         - Below its starting point and not on another item.
 139         """
 140 
 141         self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
 142         
 143         if self.IsInControl == False:       # 1. Outside the control : Do Nothing.
 144             self.IsDrag = False
 145         else:                               # In control but not a drag event : Do Nothing.
 146             if self.IsDrag == False:
 147                 pass
 148             else:                           # In control and is a drag event : Determine Location.
 149                 self.hitIndex = self.HitTest(event.GetPosition())
 150                 self.dropIndex = self.hitIndex[0]
 151                 # Drop index indicates where the drop location is; what index number.
 152                 #---------
 153                 # Determine dropIndex and its validity.
 154                 #--------
 155                 if self.dropIndex == self.startIndex or self.dropIndex == -1:    # 2. On itself or below control : Do Nothing.
 156                     pass
 157                 else:
 158                     #----------
 159                     # Now that dropIndex has been established do 3 things :
 160                     # 1. gather item data
 161                     # 2. delete item in list
 162                     # 3. insert item & it's data into the list at the new index
 163                     #----------
 164                     dropList = []         # Drop List is the list of field values from the list control.
 165                     thisItem = self.GetItem(self.startIndex)
 166                     for x in range(self.GetColumnCount()):
 167                         dropList.append(self.GetItem(self.startIndex, x).GetText())
 168                     thisItem.SetId(self.dropIndex)
 169                     self.DeleteItem(self.startIndex)
 170                     self.InsertItem(thisItem)
 171                     for x in range(self.GetColumnCount()):
 172                         self.SetItem(self.dropIndex, x, dropList[x])
 173             #------------
 174             # I don't know exactly why, but the mouse event MUST
 175             # call the stripe procedure if the control is to be successfully
 176             # striped. Every time it was only in the _onInsert, it failed on
 177             # dragging index 3 to the index 1 spot.
 178             #-------------
 179             # Furthermore, in the load button on the wxFrame that this lives in,
 180             # I had to call the _onStripe directly because it would occasionally fail
 181             # to stripe without it. You'll notice that this is present in the example stub.
 182             # Someone with more knowledge than I probably knows why...and how to fix it properly.
 183             #-------------
 184         self._onStripe()
 185         self.IsDrag = False
 186         event.Skip()
 187 
 188 
 189     def _onMouseDown(self, event):
 190         """
 191         ...
 192         """
 193         
 194         self.IsInControl = True
 195         event.Skip()
 196 
 197 
 198     def _onInsert(self, event):
 199         """
 200         Sequencing on a drop event is:
 201         wx.EVT_LIST_ITEM_SELECTED
 202         wx.EVT_LIST_BEGIN_DRAG
 203         wx.EVT_LEFT_UP
 204         wx.EVT_LIST_ITEM_SELECTED (at the new index)
 205         wx.EVT_LIST_INSERT_ITEM
 206         """
 207 
 208         # this call to onStripe catches any addition to the list; drag or not.
 209         self._onStripe()
 210         self.dragIndex = -1
 211         event.Skip()
 212 
 213 
 214     def _onDelete(self, event):
 215         """
 216         ...
 217         """
 218         
 219         self._onStripe()
 220         event.Skip()
 221 
 222 
 223     def _onStripe(self):
 224         """
 225         ...
 226         """
 227         
 228         if self.GetItemCount() > 0:
 229             for x in range(self.GetItemCount()):
 230                 if x % 2 == 0:
 231                     self.SetItemBackgroundColour(x, wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
 232                 else:
 233                     self.SetItemBackgroundColour(x, wx.WHITE)
 234 
 235 #---------------------------------------------------------------------------
 236 
 237 class MyApp(wx.App):    
 238     def OnInit(self):
 239 
 240         #------------
 241 
 242         frame = MyFrame(parent=None, id=-1)
 243         self.SetTopWindow(frame)
 244         frame.Show(True)
 245 
 246         return True
 247     
 248 #---------------------------------------------------------------------------
 249 
 250 def main():
 251     app = MyApp(False)
 252     app.MainLoop()
 253 
 254 #---------------------------------------------------------------------------
 255 
 256 if __name__ == "__main__" :
 257     main()


Download source

source.zip


Additional Information

Link :

https://wiki.wxpython.org/List%20control%20and%20SQLite%20database%20%28Phoenix%29

https://wiki.wxpython.org/List%20control%20virtual%20%28Phoenix%29

https://wiki.wxpython.org/List%20control%20virtual%20and%20SQLite%20database%20%28Phoenix%29

http://jak-o-shadows.users.sourceforge.net/python/wxpy/dblistctrl.html

http://wxpython-users.1045709.n5.nabble.com/Example-of-Database-Interaction-td2361801.html

http://www.kitebird.com/articles/pydbapi.html

https://dabodev.com/

https://www.pgadmin.org/download/

https://github.com/1966bc/pyggybank

https://sourceforge.net/projects/pyggybank/

- - - - -

https://wiki.wxpython.org/TitleIndex

https://docs.wxpython.org/


Thanks to

??? (sample_one.py coding), ??? (sample_two.py coding) and the wxPython community...


About this page

Date(d/m/y) Person (bot) Comments :

14/03/20 - Ecco (Created page and updated examples for wxPython Phoenix).


Comments

- blah, blah, blah...

How to create a list control with drag and drop (Phoenix) (last edited 2020-12-13 14:08:37 by Ecco)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.