Supporting Customization of Painting in Owner-Drawn Controls

Gosh, the subject sounds pretentious. :slight_smile: Let’s say I have this big ol’ owner drawn control with various slightly complex portions on it, and I want to be able to make it generic in its display so various subclasses can take over drawing certain parts of it… what’s the best strategy for that?

So, on the main control, I have an OnPaint handler where I fetch a DC:

    if self.IsDoubleBuffered():
        dc = wx.PaintDC(self)
    else:
        dc = wx.BufferedPaintDC(self)

And then I go about drawing things. However, if I’m drawing a certain region and its been indicated that the subclass wants to take over this region-- how best do I pass that on? Should I just call a method, pass it my DC, and perhaps a wxRect indicating what region it should draw upon? That seems sorta un-wx-ish, somehow.

If this doesn’t sound entirely too vague-- any advice?

Thanks in advance.

–Stephen

Stephen Hansen wrote:

Gosh, the subject sounds pretentious. :slight_smile: Let's say I have this big ol' owner drawn control with various slightly complex portions on it, and I want to be able to make it generic in its display so various subclasses can take over drawing certain parts of it... what's the best strategy for that?

So, on the main control, I have an OnPaint handler where I fetch a DC:

        if self.IsDoubleBuffered():
            dc = wx.PaintDC(self)
        else:
            dc = wx.BufferedPaintDC(self)

And then I go about drawing things. However, if I'm drawing a certain region and its been indicated that the subclass wants to take over this region-- how best do I pass that on? Should I just call a method, pass it my DC, and perhaps a wxRect indicating what region it should draw upon? That seems sorta un-wx-ish, somehow.

Just break up your drawing into logical chunks, make each chunk be a separate method and then have your EVT_PAINT handler call each method. When you want to override parts of it in a subclass then just override the method. Take a look at the wx.lib.buttons module as an example. The base class there has split out things like drawing the label, drawing the bezel, etc. and they are overridden in the derived classes in various ways.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Hi Stephen,

Gosh, the subject sounds pretentious. :slight_smile: Let's say I have this big ol' owner drawn control with various slightly complex portions on it, and I want to be able to make it generic in its display so various subclasses can take over drawing certain parts of it... what's the best strategy for that?

So, on the main control, I have an OnPaint handler where I fetch a DC:

        if self.IsDoubleBuffered():
            dc = wx.PaintDC(self)
        else:
            dc = wx.BufferedPaintDC(self)

First, you can do:

dc = wx.AutoBufferedPaintDC(self)

instead of that code. :slight_smile: The "Auto" means it determines when it should or should not provide a double-buffered context.

And then I go about drawing things. However, if I'm drawing a certain region and its been indicated that the subclass wants to take over this region-- how best do I pass that on? Should I just call a method, pass it my DC, and perhaps a wxRect indicating what region it should draw upon? That seems sorta un-wx-ish, somehow.

Usually, subclassing is used to override and expand functionality in a base class, but here it sounds like you're using it to break the functionality of a big class up into smaller logical groups. While I don't know what you're trying to accomplish, the first thought that popped into my head was having each owner-drawn region as an object, e.g. MyControlRect(x, y, w, h) with a paint method, then have your main class manage those objects and on paint, iterate through them and draw them in their proper location. If I knew more about what exactly you are trying to do, I could probably give a more detailed explanation, but hopefully you get the idea. :slight_smile:

Maybe that won't be of any help, but when I hear phrases like "big ol'" and "slightly complex portions", usually it means it's time to try and break things down a bit. :wink:

Regards,

Kevin

···

On Jan 15, 2009, at 6:44 PM, Stephen Hansen wrote:

If this doesn't sound entirely too vague-- any advice?

Thanks in advance.

--Stephen
_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

First, you can do:

dc = wx.AutoBufferedPaintDC(self)

instead of that code. :slight_smile: The “Auto” means it determines when it should or should not provide a double-buffered context.

The reason I don’t do that is because for certain drawing actions I prefer to use GraphicsContext, and “gc = wx.GraphicsContext.Create(dc)” with an wx.AutoBufferedPaintDC() raises an error.

And then I go about drawing things. However, if I’m drawing a certain region and its been indicated that the subclass wants to take over this region-- how best do I pass that on? Should I just call a method, pass it my DC, and perhaps a wxRect indicating what region it should draw upon? That seems sorta un-wx-ish, somehow.

Usually, subclassing is used to override and expand functionality in a base class, but here it sounds like you’re using it to break the functionality of a big class up into smaller logical groups. While I don’t know what you’re trying to accomplish, the first thought that popped into my head was having each owner-drawn region as an object, e.g. MyControlRect(x, y, w, h) with a paint method, then have your main class manage those objects and on paint, iterate through them and draw them in their proper location. If I knew more about what exactly you are trying to do, I could probably give a more detailed explanation, but hopefully you get the idea. :slight_smile:

Maybe that won’t be of any help, but when I hear phrases like “big ol’” and “slightly complex portions”, usually it means it’s time to try and break things down a bit. :wink:

Basically, I’m using the subclassing to sort of reconfigure the base functionality of the control. The control itself is really a series of “pieces” that get assembled according to a particular subclasses needs. So I am breaking it apart but “the control” is a controller component that puts 'em back together :slight_smile:

The general goal, in short? A read-only ListCtrl/Grid hybrid supporting variable multiple-lines-per-record (where Grid called them rows, I call them records as there are multiple rows (I call them lines) in each record), variable row height (per-line), variable cell width (where a single ‘column’ within a line can have a very different width then even the same column of another line), and where a wide range of things can be easily drawn into a particular cell: strings, background colors, checkboxes, and images so far.

A picture’s worth a thousand words – :slight_smile: The attached is the current state of the experiment. You can see about 2.1 records on the jpeg; each record has two “lines”. The first line contains status, an icon, slug, flags and keywords… The second line contains a thumbnail, and three check boxes. Each of those items is its own cell. The depth of each line is variable-- in this case the first line’s depth is 20px, the second is 100px (since the thumbnail is defined as 100x100). That’s rare: most uses would not have a thumbnail. But its an experiment so I’m throwing it in :slight_smile: Each of those check boxes is its own column/field/cell all by itself-- to show the variable cell-width. The “header” only matches the first line.

Currently, I’m following Robin’s advice and sorta following the Grid model – I have lots of “renderers” and each cell has a distinct data type that matches a registered renderer. It seems to be working good. (So thanks Robin!)

I was going to try to do each region as an object as you suggest-- but that’d be 10-20 thousand objects, which made me cringe :slight_smile: But this is all experimental still.

Thanks for your help.

–Stephen

P.S. If this gets past Experimental and into Useful, if anyone else would find it vaguely useful I can get my boss to let me release it as I’ve been doing it off-hours as a sort of fun project. Don’t know if anyone else ever needs this kind of thing, I’ve been trying to twist wxGrid and wxListCtrl into this particular need for years now and have finally given up :slight_smile:

Hi Stephen,

First, you can do:

dc = wx.AutoBufferedPaintDC(self)

instead of that code. :slight_smile: The "Auto" means it determines when it should or should not provide a double-buffered context.

The reason I don't do that is because for certain drawing actions I prefer to use GraphicsContext, and "gc = wx.GraphicsContext.Create(dc)" with an wx.AutoBufferedPaintDC() raises an error.

Ah, I see. Yet another incompatibility we probably should address... :frowning:

And then I go about drawing things. However, if I'm drawing a certain region and its been indicated that the subclass wants to take over this region-- how best do I pass that on? Should I just call a method, pass it my DC, and perhaps a wxRect indicating what region it should draw upon? That seems sorta un-wx-ish, somehow.

Usually, subclassing is used to override and expand functionality in a base class, but here it sounds like you're using it to break the functionality of a big class up into smaller logical groups. While I don't know what you're trying to accomplish, the first thought that popped into my head was having each owner-drawn region as an object, e.g. MyControlRect(x, y, w, h) with a paint method, then have your main class manage those objects and on paint, iterate through them and draw them in their proper location. If I knew more about what exactly you are trying to do, I could probably give a more detailed explanation, but hopefully you get the idea. :slight_smile:

Maybe that won't be of any help, but when I hear phrases like "big ol'" and "slightly complex portions", usually it means it's time to try and break things down a bit. :wink:

Basically, I'm using the subclassing to sort of reconfigure the base functionality of the control. The control itself is really a series of "pieces" that get assembled according to a particular subclasses needs. So I am breaking it apart but "the control" is a controller component that puts 'em back together :slight_smile:

The general goal, in short? A read-only ListCtrl/Grid hybrid supporting variable multiple-lines-per-record (where Grid called them rows, I call them records as there are multiple rows (I call them lines) in each record), variable row height (per-line), variable cell width (where a single 'column' within a line can have a very different width then even the same column of another line), and where a wide range of things can be easily drawn into a particular cell: strings, background colors, checkboxes, and images so far.

A picture's worth a thousand words -- :slight_smile: The attached is the current state of the experiment. You can see about 2.1 records on the jpeg; each record has two "lines". The first line contains status, an icon, slug, flags and keywords.. The second line contains a thumbnail, and three check boxes. Each of those items is its own cell. The depth of each line is variable-- in this case the first line's depth is 20px, the second is 100px (since the thumbnail is defined as 100x100). That's rare: most uses would not have a thumbnail. But its an experiment so I'm throwing it in :slight_smile: Each of those check boxes is its own column/field/cell all by itself-- to show the variable cell-width. The "header" only matches the first line.

Currently, I'm following Robin's advice and sorta following the Grid model -- I have lots of "renderers" and each cell has a distinct data type that matches a registered renderer. It seems to be working good. (So thanks Robin!)

I was going to try to do each region as an object as you suggest-- but that'd be 10-20 thousand objects, which made me cringe :slight_smile: But this is all experimental still.

No, Robin's advice is right - basically, by "region" I meant "cell", it's just that I was very vague because I didn't know quite what you were doing. :wink: Robin's been around long enough that he probably guessed from your description what you were trying to do. :wink:

Thanks for your help.

--Stephen

P.S. If this gets past Experimental and into Useful, if anyone else would find it vaguely useful I can get my boss to let me release it as I've been doing it off-hours as a sort of fun project. Don't know if anyone else ever needs this kind of thing, I've been trying to twist wxGrid and wxListCtrl into this particular need for years now and have finally given up :slight_smile:

We could certainly use a control like this - I'd have to say that it's one of the most often requested components that haven't yet been implemented. My big question would be how this would compare with wx.DataViewCtrl in 2.9 which I know has some of this stuff, but regardless it'd be nice to have something that works in 2.8 right now. :wink:

Thanks,

Kevin

···

On Jan 21, 2009, at 9:27 AM, Stephen Hansen wrote:

<Catalog.jpg>_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

P.S. If this gets past Experimental and into Useful, if anyone else would find it vaguely useful I can get my boss to let me release it as I’ve been doing it off-hours as a sort of fun project. Don’t know if anyone else ever needs this kind of thing, I’ve been trying to twist wxGrid and wxListCtrl into this particular need for years now and have finally given up :slight_smile:

We could certainly use a control like this - I’d have to say that it’s one of the most often requested components that haven’t yet been implemented. My big question would be how this would compare with wx.DataViewCtrl in 2.9 which I know has some of this stuff, but regardless it’d be nice to have something that works in 2.8 right now. :wink:

I got permission from my boss – so as soon as it gets a bit more stable, I’ll post something Somewhere to release it.

As for the DataViewCtrl, it seems like a great future addition but wouldn’t really completely replace my need for this and looking at it I don’t think I’d be able to implement the catalog control in terms of what DataViewCtrl offers. Its very row oriented, but I don’t see how I could do multiple-lines-per-record and especially different numbers of columns on individual lines with it. I also don’t see wxDataViewColumn exposing a means to have variable row height: so it looks like its bringing Grid-like-customizability to ListCtrl’s (and TreeCtrls in the future perhaps), but that isn’t quite my purpose. At least not exclusively. :slight_smile:

–Stephen