How to set PropertyGrid values

577 views
Skip to first unread message

Brendan Simon (eTRIX)

unread,
Sep 12, 2017, 1:08:30 PM9/12/17
to wxPython-users
I'm using wxPython 4.0.0b1, on macOS.  I have instantiated a PropertyGrid and have created properties that display ok.  My first time using this widget !!

I want to press a button to do some actions, then update some of the property values (e.g. serial numbers, etc).

The following reads the values ok, but when nothing updates when I call`SetPropertyValues()`.  Adding call to `Refresh()` doesn't help.

    def on_button_program(self, event):
        pg = self.property_grid_1
        d = pg.GetPropertyValues(inc_attributes=False)
        d['SERIAL_NUMBER'] += 1
        pg.SetPropertyValues(d, autofill=False)
        #pg.Refresh()

If I add `autofill=True` to `SetPropertyValues()` then the propertygrid does update, but it appends the information instead of replacing the property data that is already there.

    def on_button_program(self, event):
        pg = self.property_grid_1
        d = pg.GetPropertyValues(inc_attributes=False)
        d['SERIAL_NUMBER'] += 1
        pg.SetPropertyValues(d, autofill=True)
        #pg.Refresh()


I've tried clearing the property grid before setting new values, but that doesn't do anything either.

    def on_button_program(self, event):
        pg = self.property_grid_1
        d = pg.GetPropertyValues(inc_attributes=False)
        d['SERIAL_NUMBER'] += 1
        pg.Clear()
        pg.SetPropertyValues(d, autofill=True)
        #pg.Refresh()

I've also tried this, instead of `SetPropertyValues()`, but that causes a crash (I might be using it incorrectly).

        pg.AutoFill(d)

I've also tried this, instead of `ReplaceProperty()`, but I get an error.

        pg.ReplaceProperty("SERIAL_NUMBER", wx.propgrid.IntProperty("SERIAL_NUMBER", wx.propgrid.PG_LABEL, sn))

        => GetPropertyByNameA(): no property with name 'SERIAL_NUMBER'

Any thoughts on what I'm doing wrong?

What is the best method to update 2 or 3 properties?  e.g increment a serial number, adjust a string property (based on new serial number), etc?

Thanks, Brendan.

Brendan Simon

unread,
Sep 13, 2017, 3:48:37 AM9/13/17
to wxPython-users
From what I can gather by looking at the wxPhoenix demo, the SetPropertyValues takes an object with attributes (which presumably are accessed by the `__dict__` method).

So I created a dummy class with no attributes (`ValueObj`), as the demo does, and tried adding my dict with:

    vo = ValueObj()
    vo.__dict__ = d
    pg.SetPropertyValues(vo)

Unfortunately that didn't help either.  I'm not sure why not, because it _appears_ to be the same as the demo, although it uses some funky code (`six.exec_()`) to assign values to `ValueObj` via a memo dialog.

I also tried explicitly setting the values.

    vo = ValueObj()
    vo.SERIAL_NUMBER = 123
    pg.SetPropertyValues(vo)

Still not updating my property grid :(

Anyone have an answer or suggestions to this ??

Thanks, Brendan.

Chris Barker

unread,
Sep 13, 2017, 5:32:00 PM9/13/17
to wxpython-users
I haven't tried any of this, but the docs say:

"the source of the property values to set, which can be either a dictionary or an object with a __dict__ attribute."

All python class instances have a __dict__ attribute that holds the instance attributes. Presumably that is why the __dict__ API is used -- you should be able to pass in pretty much any old class instance that you are using to manage your data.

In [1]: class Test():
   ...:     pass
   ...:


In [2]: t = Test()

In [3]: t.__dict__

Out[3]: {}


In [4]: class Test():

   ...:     def __init__(self):
   ...:         self.this = 'something'
   ...:         self.that = 'something else'
   ...:        

In [5]: t = Test()

In [6]: t.__dict__
Out[6]: {'that': 'something else', 'this': 'something'}


So you don't want to set the __dict__ parameter.

If you are py3, and want a class to hold a bunch of parameters, you might want to use types.SimpleNamespace

In [14]: t = types.SimpleNamespace(this="something", that="something else")

In [15]: t.__dict__
Out[15]: {'that': 'something else', 'this': 'something'}

Though if you don't have your data in a class instance for any other reason, then simply pass in a dict:

data = {'this': 'something', 'that', 'something else'}
pg.SetPropertyValues(data)


However:


So I created a dummy class with no attributes (`ValueObj`), as the demo does, and tried adding my dict with:

    vo = ValueObj()
    vo.__dict__ = d
    pg.SetPropertyValues(vo)

that should work though, even though it's a bad idea!
 
I also tried explicitly setting the values.

    vo = ValueObj()
    vo.SERIAL_NUMBER = 123
    pg.SetPropertyValues(vo)

that also should work...

Still not updating my property grid :(

so something else is up -- sorry I'm not being helpful ...
 
-CHB

--

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

Chris....@noaa.gov

Tim Roberts

unread,
Sep 13, 2017, 6:42:26 PM9/13/17
to wxpytho...@googlegroups.com
Brendan Simon wrote:

From what I can gather by looking at the wxPhoenix demo, the SetPropertyValues takes an object with attributes (which presumably are accessed by the `__dict__` method).

I'm afraid I'm just going to muddy things rather than add clarity, but I fear some of the problems here are caused by the mapping between Python and C++.  The PropertyGrid clearly has a C++-centric design.  GetPropertyValues returns an array of "PGVariant" objects, which is a generic way of storing things of different types in a strongly-typed language.  So, when you say

    d['SERIAL_NUMBER'] += 1

my guess is that something in the C++/Python interface is incorrectly converting the operating to strings, so the addition becomes a string concatenation, and the concatenated result back to the property grid.

I started to write a reply to the original mail, suggesting that you needed to do something SIMILAR to:
        oldvalue = grid.GetIntPropertyValye('SERIAL_NUMBER')
        grid.SetIntPropertyValue('SERIAL_NUMBER', oldvalue+1)

However, I couldn't find the APIs that would do that, so I abandoned my reply.  I think your path to happiness lies somewhere along that line.  Although it's contrary to the Python philosophy, because the C++ code has these variant objects, I think you're going to need to do some type-specific stuff.

Or, perhaps you just assume the interface is all strings, and do something similar to
     newvalue = str( int(oldvalue) + 1 )
if you can figure out how to spell that.
-- 
Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.

Robin Dunn

unread,
Sep 13, 2017, 8:11:48 PM9/13/17
to wxPython-users


We have typemaps in place that implement auto conversions to/from Python objects to/from the types that the C++ variant classes know about by default, strings, numbers, bool, wxDateTime, wxSize, wxBitmap, arrays of strings, etc. and then stuffs them into the C++ variant object for use by the propgrid code.  This maintains the data types and means that when you get the value back out of the variant object it will be the same type you started with or whatever type the propgrid code put into the variant for return values.  For any other type it is stored in the the variant as a PyObject*, and although the propgrid code won't be able to do anything with them without help (such as creating a custom property type) you will still get the same type of value (the same instance actually) of what was originally put in the variant.



On Wednesday, September 13, 2017 at 11:42:26 AM UTC-7, Tim Roberts wrote:
Brendan Simon wrote:

From what I can gather by looking at the wxPhoenix demo, the SetPropertyValues takes an object with attributes (which presumably are accessed by the `__dict__` method).

SetPropertyValues is meant to just be a convenience function for Python code. It basically just takes the key/value pairs from the dict or obj.__dict__ and  boils it down to calls to SetProperyValue(key, value), and it also supports sub-objects or sub-dictionaries if a property has child properties. The implementation is in Python code so you can track it down in the propgrid wrapper module if you're interesting in what it is doing and how it is doing it.

If SetPropertyValues is not working as expected then I would try doing individual SetPropertyValue calls and perhaps that will help figure out what is wrong with the SetPropertyValues. For example, perhaps the type of the value doesn't match the type of the Property class? 


I'm afraid I'm just going to muddy things rather than add clarity, but I fear some of the problems here are caused by the mapping between Python and C++.  The PropertyGrid clearly has a C++-centric design.  GetPropertyValues returns an array of "PGVariant" objects, which is a generic way of storing things of different types in a strongly-typed language.  So, when you say

    d['SERIAL_NUMBER'] += 1

my guess is that something in the C++/Python interface is incorrectly converting the operating to strings, so the addition becomes a string concatenation, and the concatenated result back to the property grid.

I started to write a reply to the original mail, suggesting that you needed to do something SIMILAR to:
        oldvalue = grid.GetIntPropertyValye('SERIAL_NUMBER')
        grid.SetIntPropertyValue('SERIAL_NUMBER', oldvalue+1)


Close.  Since the values are coming back as variant objects which should be converted automatically to the correct type of Python object, there is no need to have type-specific methods for the getter. The setter does the same in the opposite direction, so GetPropertyValue(name) and SetProperyValue(name, value) are sufficient.


--
Robin Dunn
Software Craftsman
http://wxPython.org

  

Brendan Simon

unread,
Sep 14, 2017, 6:46:15 AM9/14/17
to wxPython-users
On Thursday, 14 September 2017 06:11:48 UTC+10, Robin Dunn wrote:

SetPropertyValues is meant to just be a convenience function for Python code. It basically just takes the key/value pairs from the dict or obj.__dict__ and  boils it down to calls to SetProperyValue(key, value), and it also supports sub-objects or sub-dictionaries if a property has child properties. The implementation is in Python code so you can track it down in the propgrid wrapper module if you're interesting in what it is doing and how it is doing it.

If SetPropertyValues is not working as expected then I would try doing individual SetPropertyValue calls and perhaps that will help figure out what is wrong with the SetPropertyValues. For example, perhaps the type of the value doesn't match the type of the Property class?

Since the values are coming back as variant objects which should be converted automatically to the correct type of Python object, there is no need to have type-specific methods for the getter. The setter does the same in the opposite direction, so GetPropertyValue(name) and SetProperyValue(name, value) are sufficient.

I must be doing something fundamentally wrong, as calling any method which retrieves a property -- e.g. `GetPropertyByName('SERIAL_NUMBER')`, `GetPropertyCategory(cat)` -- returns `None`.

Example: p is always assigned None.

#pg.Append(wx.propgrid.StringProperty(label="XYZ", name="XYZ", value="HELLO"))
pg.Append(wx.propgrid.StringProperty("XYZ", value="HELLO"))
p = pg.GetPropertyByName('XYZ')

If I use `GetPropertyValue()` I get an exception.

v = pg.GetPropertyValue('XYZ')
print("BJS: v = {!r}".format(v))

wx._core.wxAssertionError: C++ assertion "p" failed at /Users/robind/projects/buildbots/macosx-vm4/dist-osx-py36/Phoenix/ext/wxWidgets/src/propgrid/propgridiface.cpp(418) in GetPropertyByNameA(): no property with name 'XYZ'
OnInit returned false, exiting...


Brendan Simon

unread,
Sep 14, 2017, 7:00:46 AM9/14/17
to wxPython-users
SOLVED !!

I thought I was using `PropertyGrid`, but in fact I was using `PropertyGridManager`.  I am using `wxglade` to layout my frame, and for some reason I had in my head it generated code for `PropertyGrid`, not `PropertyGridManager`.

#self.property_grid_1 = wx.propgrid.PropertyGridManager(self, wx.ID_ANY)
self.property_grid_1 = wx.propgrid.PropertyGrid(self, wx.ID_ANY)

If I change the code (as above) then I get get property info and update properties ok :)

So, why doesn't it work with `PropertyGridManager`? -- when the demo does !!

Well it's because I need to add a page with `pg.AddPage()`.

Is this potentially a bug, or just incorrect used of `PropertyGridManager`?

If a page is required, should there at least be a default page added?

Or, should `Append` (and friends) return and error/exception if there is not page?

Thanks all for your help,
Brendan.

Reply all
Reply to author
Forward
0 new messages