mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2025-07-21 04:31:09 +02:00
258 lines
9.3 KiB
Python
258 lines
9.3 KiB
Python
#!/usr/bin/env python
|
|
# Tags: phoenix-port, py3-port
|
|
|
|
import wx
|
|
import wx.dataview as dv
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
# This model class provides the data to the view when it is asked for.
|
|
# Since it is a list-only model (no hierarchical data) then it is able
|
|
# to be referenced by row rather than by item object, so in this way
|
|
# it is easier to comprehend and use than other model types. In this
|
|
# example we also provide a Compare function to assist with sorting of
|
|
# items in our model. Notice that the data items in the data model
|
|
# object don't ever change position due to a sort or column
|
|
# reordering. The view manages all of that and maps view rows and
|
|
# columns to the model's rows and columns as needed.
|
|
#
|
|
# For this example our data is stored in a simple list of lists. In
|
|
# real life you can use whatever you want or need to hold your data.
|
|
|
|
class TestModel(dv.DataViewIndexListModel):
|
|
def __init__(self, data, log):
|
|
dv.DataViewIndexListModel.__init__(self, len(data))
|
|
self.data = data
|
|
self.log = log
|
|
|
|
# All of our columns are strings. If the model or the renderers
|
|
# in the view are other types then that should be reflected here.
|
|
def GetColumnType(self, col):
|
|
return "string"
|
|
|
|
# This method is called to provide the data object for a
|
|
# particular row,col
|
|
def GetValueByRow(self, row, col):
|
|
return self.data[row][col]
|
|
|
|
# This method is called when the user edits a data item in the view.
|
|
def SetValueByRow(self, value, row, col):
|
|
self.log.write("SetValue: (%d,%d) %s\n" % (row, col, value))
|
|
self.data[row][col] = value
|
|
return True
|
|
|
|
# Report how many columns this model provides data for.
|
|
def GetColumnCount(self):
|
|
return len(self.data[0])
|
|
|
|
# Report the number of rows in the model
|
|
def GetCount(self):
|
|
#self.log.write('GetCount')
|
|
return len(self.data)
|
|
|
|
# Called to check if non-standard attributes should be used in the
|
|
# cell at (row, col)
|
|
def GetAttrByRow(self, row, col, attr):
|
|
##self.log.write('GetAttrByRow: (%d, %d)' % (row, col))
|
|
if col == 3:
|
|
attr.SetColour('blue')
|
|
attr.SetBold(True)
|
|
return True
|
|
return False
|
|
|
|
|
|
# This is called to assist with sorting the data in the view. The
|
|
# first two args are instances of the DataViewItem class, so we
|
|
# need to convert them to row numbers with the GetRow method.
|
|
# Then it's just a matter of fetching the right values from our
|
|
# data set and comparing them. The return value is -1, 0, or 1,
|
|
# just like Python's cmp() function.
|
|
def Compare(self, item1, item2, col, ascending):
|
|
if not ascending: # swap sort order?
|
|
item2, item1 = item1, item2
|
|
row1 = self.GetRow(item1)
|
|
row2 = self.GetRow(item2)
|
|
a = self.data[row1][col]
|
|
b = self.data[row2][col]
|
|
if col == 0:
|
|
a = int(a)
|
|
b = int(b)
|
|
if a < b: return -1
|
|
if a > b: return 1
|
|
return 0
|
|
|
|
def DeleteRows(self, rows):
|
|
# make a copy since we'll be sorting(mutating) the list
|
|
# use reverse order so the indexes don't change as we remove items
|
|
rows = sorted(rows, reverse=True)
|
|
|
|
for row in rows:
|
|
# remove it from our data structure
|
|
del self.data[row]
|
|
# notify the view(s) using this model that it has been removed
|
|
self.RowDeleted(row)
|
|
|
|
|
|
def AddRow(self, value):
|
|
# update data structure
|
|
self.data.append(value)
|
|
# notify views
|
|
self.RowAppended()
|
|
|
|
|
|
|
|
class TestPanel(wx.Panel):
|
|
def __init__(self, parent, log, model=None, data=None):
|
|
self.log = log
|
|
wx.Panel.__init__(self, parent, -1)
|
|
|
|
# Create a dataview control
|
|
self.dvc = dv.DataViewCtrl(self,
|
|
style=wx.BORDER_THEME
|
|
| dv.DV_ROW_LINES # nice alternating bg colors
|
|
#| dv.DV_HORIZ_RULES
|
|
| dv.DV_VERT_RULES
|
|
| dv.DV_MULTIPLE
|
|
)
|
|
|
|
# Create an instance of our simple model...
|
|
if model is None:
|
|
self.model = TestModel(data, log)
|
|
else:
|
|
self.model = model
|
|
|
|
# ...and associate it with the dataview control. Models can
|
|
# be shared between multiple DataViewCtrls, so this does not
|
|
# assign ownership like many things in wx do. There is some
|
|
# internal reference counting happening so you don't really
|
|
# need to hold a reference to it either, but we do for this
|
|
# example so we can fiddle with the model from the widget
|
|
# inspector or whatever.
|
|
self.dvc.AssociateModel(self.model)
|
|
|
|
# Now we create some columns. The second parameter is the
|
|
# column number within the model that the DataViewColumn will
|
|
# fetch the data from. This means that you can have views
|
|
# using the same model that show different columns of data, or
|
|
# that they can be in a different order than in the model.
|
|
self.dvc.AppendTextColumn("Artist", 1, width=170, mode=dv.DATAVIEW_CELL_EDITABLE)
|
|
self.dvc.AppendTextColumn("Title", 2, width=260, mode=dv.DATAVIEW_CELL_EDITABLE)
|
|
self.dvc.AppendTextColumn("Genre", 3, width=80, mode=dv.DATAVIEW_CELL_EDITABLE)
|
|
|
|
# There are Prepend methods too, and also convenience methods
|
|
# for other data types but we are only using strings in this
|
|
# example. You can also create a DataViewColumn object
|
|
# yourself and then just use AppendColumn or PrependColumn.
|
|
c0 = self.dvc.PrependTextColumn("Id", 0, width=40)
|
|
|
|
# The DataViewColumn object is returned from the Append and
|
|
# Prepend methods, and we can modify some of it's properties
|
|
# like this.
|
|
c0.Alignment = wx.ALIGN_RIGHT
|
|
c0.Renderer.Alignment = wx.ALIGN_RIGHT
|
|
c0.MinWidth = 40
|
|
|
|
# Through the magic of Python we can also access the columns
|
|
# as a list via the Columns property. Here we'll mark them
|
|
# all as sortable and reorderable.
|
|
for c in self.dvc.Columns:
|
|
c.Sortable = True
|
|
c.Reorderable = True
|
|
|
|
# Let's change our minds and not let the first col be moved.
|
|
c0.Reorderable = False
|
|
|
|
# set the Sizer property (same as SetSizer)
|
|
self.Sizer = wx.BoxSizer(wx.VERTICAL)
|
|
self.Sizer.Add(self.dvc, 1, wx.EXPAND)
|
|
|
|
# Add some buttons to help out with the tests
|
|
b1 = wx.Button(self, label="New View", name="newView")
|
|
self.Bind(wx.EVT_BUTTON, self.OnNewView, b1)
|
|
b2 = wx.Button(self, label="Add Row")
|
|
self.Bind(wx.EVT_BUTTON, self.OnAddRow, b2)
|
|
b3 = wx.Button(self, label="Delete Row(s)")
|
|
self.Bind(wx.EVT_BUTTON, self.OnDeleteRows, b3)
|
|
|
|
btnbox = wx.BoxSizer(wx.HORIZONTAL)
|
|
btnbox.Add(b1, 0, wx.LEFT|wx.RIGHT, 5)
|
|
btnbox.Add(b2, 0, wx.LEFT|wx.RIGHT, 5)
|
|
btnbox.Add(b3, 0, wx.LEFT|wx.RIGHT, 5)
|
|
self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5)
|
|
|
|
# Bind some events so we can see what the DVC sends us
|
|
self.Bind(dv.EVT_DATAVIEW_ITEM_EDITING_DONE, self.OnEditingDone, self.dvc)
|
|
self.Bind(dv.EVT_DATAVIEW_ITEM_VALUE_CHANGED, self.OnValueChanged, self.dvc)
|
|
|
|
|
|
def OnNewView(self, evt):
|
|
f = wx.Frame(None, title="New view, shared model", size=(600,400))
|
|
TestPanel(f, self.log, self.model)
|
|
b = f.FindWindowByName("newView")
|
|
b.Disable()
|
|
f.Show()
|
|
|
|
|
|
def OnDeleteRows(self, evt):
|
|
# Remove the selected row(s) from the model. The model will take care
|
|
# of notifying the view (and any other observers) that the change has
|
|
# happened.
|
|
items = self.dvc.GetSelections()
|
|
rows = [self.model.GetRow(item) for item in items]
|
|
self.model.DeleteRows(rows)
|
|
|
|
|
|
def OnAddRow(self, evt):
|
|
# Add some bogus data to a new row in the model's data
|
|
id = len(self.model.data) + 1
|
|
value = [str(id),
|
|
'new artist %d' % id,
|
|
'new title %d' % id,
|
|
'genre %d' % id]
|
|
self.model.AddRow(value)
|
|
|
|
|
|
def OnEditingDone(self, evt):
|
|
self.log.write("OnEditingDone\n")
|
|
|
|
def OnValueChanged(self, evt):
|
|
self.log.write("OnValueChanged\n")
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
def runTest(frame, nb, log):
|
|
# Get the data from the ListCtrl sample to play with, converting it
|
|
# from a dictionary to a list of lists, including the dictionary key
|
|
# as the first element of each sublist.
|
|
import ListCtrl
|
|
musicdata = sorted(ListCtrl.musicdata.items())
|
|
musicdata = [[str(k)] + list(v) for k,v in musicdata]
|
|
|
|
win = TestPanel(nb, log, data=musicdata)
|
|
return win
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
overview = """<html><body>
|
|
<h2><center>DataViewCtrl with DataViewIndexListModel</center></h2>
|
|
|
|
This sample shows how to derive a class from PyDataViewIndexListModel and use
|
|
it to interface with a list of data items. (This model does not have any
|
|
hierarchical relationships in the data.)
|
|
|
|
<p> See the comments in the source for lots of details.
|
|
|
|
</body></html>
|
|
"""
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import sys,os
|
|
import run
|
|
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
|
|
|