Size: 17876
Comment:
|
Size: 18116
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.
Contents
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
Here's how you can use drag-and-drop to reorder a simple list, or to move items from one list to another.
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
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.
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
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://www.pgadmin.org/download/
https://github.com/1966bc/pyggybank
https://sourceforge.net/projects/pyggybank/
- - - - -
https://wiki.wxpython.org/TitleIndex
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...