And have those partial functions keep reevaluating, possibly in
response to a Timer event. The point of this exercise is I've got a
ctypes Structure mapped onto a hardware device, and would like to
periodically refresh the readouts of the various fields. Also, it just
seems useful, and possibly useful enough that someone else would have
already thought of a good answer.
My first thought was to subclass ListItem and smarten up its GetText(),
but it doesn't seem to actually get called. I suppose I could keep a
list off to the side of which items are function results rather than
static data, but that doesn't feel very Pythonic. Any better ideas?
Thanks,
Rob
···
--
Rob Gaddi, Highland Technology
18 Otis St.
San Francisco, CA 94103
415-551-1700
I don't see anything non-pythonic about extracting the "dynamic variables" out of the
argument list, and placing them in another "timed update list"
When your new DynamicListCtrl is _init_'ed, parse out the partials and put them in
the "timed execution" list, with their ListCtrl index. The timer can execute all the
functions and change the data on each "tick".
Of course, issues like: What if the function execution times exceed the tick time?
Might make you change your technique, perhaps creating a Queue of partials, with
a separate thread processing the Queue. Then you might want to create a Set that
shows what items are already in the Queue so you don't Queue up the same data
element more than once, or add a field in your original parse list that indicates
queued or not queued. Perhaps you want different refresh rates for different data
items, so your "feed list" has a refresh rate argument for each item.
Have Fun!
···
On 11/25/2013 8:00 PM, Rob Gaddi wrote:
I'd like to build a ListCtrl that shows constantly changing data.
Ideally, I could feed it from a structure like:
from functools import partial
_sampledata = [
('Time', partial(time.strftime, '%X')),
('Date', partial(time.strftime, '%x')),
('DF', partial(subprocess.check_output, ['df', '-h'])),
('Rob', 'Gaddi'),
('< 1', 0)
]
And have those partial functions keep reevaluating, possibly in
response to a Timer event. The point of this exercise is I've got a
ctypes Structure mapped onto a hardware device, and would like to
periodically refresh the readouts of the various fields. Also, it just
seems useful, and possibly useful enough that someone else would have
already thought of a good answer.
My first thought was to subclass ListItem and smarten up its GetText(),
but it doesn't seem to actually get called. I suppose I could keep a
list off to the side of which items are function results rather than
static data, but that doesn't feel very Pythonic. Any better ideas?
You can get a lot of the way there with the Data View Controls - take a look at the demo, (search for DVC), if you have a thread that updates the data based on a timer.
Gadget/Steve
···
On 26/11/13 01:00, Rob Gaddi wrote:
I'd like to build a ListCtrl that shows constantly changing data.
Ideally, I could feed it from a structure like:
from functools import partial
_sampledata = [
('Time', partial(time.strftime, '%X')),
('Date', partial(time.strftime, '%x')),
('DF', partial(subprocess.check_output, ['df', '-h'])),
('Rob', 'Gaddi'),
('< 1', 0)
]
And have those partial functions keep reevaluating, possibly in
response to a Timer event. The point of this exercise is I've got a
ctypes Structure mapped onto a hardware device, and would like to
periodically refresh the readouts of the various fields. Also, it just
seems useful, and possibly useful enough that someone else would have
already thought of a good answer.
My first thought was to subclass ListItem and smarten up its GetText(),
but it doesn't seem to actually get called. I suppose I could keep a
list off to the side of which items are function results rather than
static data, but that doesn't feel very Pythonic. Any better ideas?
Thanks Steve and Rufus for your help. I had been planning to build
this project around Classic, but I'm starting to wonder if going to
Phoenix (thus getting access to the DVCs) wouldn't be a good idea.
Rufus, I've been assuming that the top-level window would set the timer
correctly for the events contained, and the responsibility is there.
I think trying to bring threading issues into the control would
seriously conflate display and policy.
The answer I came up with is to set LC_VIRTUAL, which is effectively
the same as my original thought to subclass ListItem. Relevant code is
at the bottom.
Now I'm having a couple of sizing issues putting this in a
FoldPanelBar (though I had the same in a CollapsablePane). First of
all, is there a way to get columns to auto-adjust their width based on
content? Secondly, when I fold down the panel containing my
SmartListCtrl, it only folds down 4 rows worth and puts up a
scrollbar. Is there any way to get the foldable pane to size correctly
for my entire list?
···
On Tue, 26 Nov 2013 06:09:26 +0000 Steve Barnes <gadgetsteve@live.co.uk> wrote:
On 26/11/13 01:00, Rob Gaddi wrote:
> I'd like to build a ListCtrl that shows constantly changing data.
> Ideally, I could feed it from a structure like:
> [snip]
>
You can get a lot of the way there with the Data View Controls - take a
look at the demo, (search for DVC), if you have a thread that updates
the data based on a timer.
Gadget/Steve
======
import time
import wx
import wx.lib.mixins.listctrl
from functools import partial
_defaultstyle = wx.LC_REPORT # wx.LC_NO_HEADER
class SmartListCtrl(wx.ListCtrl,
wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin): """
A modified ListCtrl that can accept non-strings as arguments.
Most importantly, it can accept functions of no arguments, which are
re-executed every time the corresponding cell is repainted. This
simplifies the use of the ListCtrl for displaying dynamic data,
such as hardware registers. To call a function with static arguments,
see the partial function in the functools library.
Since we're accepting executables as arguments, which could take
some time, we make this a virtual control and reexecute the function
every time we GetItemText.
"""
def __init__(self, parent, *args, **kwargs):
# Virtual is non-optional
kwargs['style'] = kwargs.get('style', _defaultstyle) |
wx.LC_VIRTUAL
wx.ListCtrl.__init__(self, parent, *args, **kwargs)
wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)
self._data =
def Populate(self, columns, data):
for colidx, col in enumerate(columns):
self.InsertColumn(colidx, col)
# Turn anything we can from the data list into unicode now.
self._data = [
tuple(x if callable(x) else unicode(x) for x in row)
for row in data
]
self.SetItemCount(len(data))
self.Parent.Layout()
def OnGetItemText(self, item, col):
"""This provides the data for the virtual ListCtrl."""
tgt = self._data[item][col]
if callable(tgt):
return unicode(tgt())
else:
return tgt
--
Rob Gaddi, Highland Technology
18 Otis St.
San Francisco, CA 94103
415-551-1700
DVCs are present in the 2.9 series which is plenty stable enough to use.
Steve
···
On 26/11/13 23:57, Rob Gaddi wrote:
On Tue, 26 Nov 2013 06:09:26 +0000 > Steve Barnes <gadgetsteve@live.co.uk> wrote:
On 26/11/13 01:00, Rob Gaddi wrote:
I'd like to build a ListCtrl that shows constantly changing data.
Ideally, I could feed it from a structure like:
[snip]
You can get a lot of the way there with the Data View Controls - take a
look at the demo, (search for DVC), if you have a thread that updates
the data based on a timer.
Gadget/Steve
Thanks Steve and Rufus for your help. I had been planning to build
this project around Classic, but I'm starting to wonder if going to
Phoenix (thus getting access to the DVCs) wouldn't be a good idea.