How to set PropertyGrid values

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

callSetPropertyValues(). 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.

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 :frowning:

Anyone have an answer or suggestions to this ??

Thanks, Brendan.

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 :frowning:

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.Barker@noaa.gov

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.

···

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).

-- Tim Roberts, Providenza & Boekelheide, Inc.

timr@probo.com

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.

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

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…

···

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.

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 :slight_smile:
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.