Scrolling while freezing first column and first row

I have a schedule created using GridBagSizer. See attached code that
displays the schedule. The first row contains column labels and the
first column contains row labels (time periods).The schedule has
scrollbars as the schedule may be larger than the window.
Appointments may span multiple rows but not columns. How can I freeze
the first row and first column while scrolling? i.e. when scrolling
right, the first column should always remain displayed and when
scrolling down, the first row should always remain displayed.

import wx
import datetime

ROW_HEIGHT = 26
COL_WIDTH = 85

class ColumnWidget(wx.Panel):
    def __init__(self, parent, ID=-1, label='',
                 pos=wx.DefaultPosition, size=(COL_WIDTH,
ROW_HEIGHT)):
        wx.Panel.__init__(self, parent, ID, pos, size,
                          wx.BORDER_NONE, label)
        self.label = label
        self.SetBackgroundColour("white")
        self.SetMinSize(size)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, evt):
        sz = self.GetClientSize()
        dc = wx.PaintDC(self)
        w,h = dc.GetTextExtent(self.label)
        dc.SetFont(self.GetFont())
        dc.DrawText(self.label, 3, (sz.height-h)/2)

class ApptWindow(wx.Panel):
    def __init__(self, parent, ID=-1, labels=None,
                 pos=wx.DefaultPosition, size=(COL_WIDTH,
ROW_HEIGHT)):
        wx.Panel.__init__(self, parent, ID, pos, size,
                          wx.BORDER_NONE, labels[0])
        self.labels = labels
        self.SetBackgroundColour("white")
        self.SetMinSize(size)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, evt):
        sz = self.GetClientSize()
        dc = wx.PaintDC(self)
        dc.SetFont(self.GetFont())
        xPos=3; yPos=3
        dc.DrawText(self.labels[0], xPos, yPos)
        for str in self.labels:
            dc.DrawText(str, xPos, yPos)
            w,h = dc.GetTextExtent(str)
            yPos += h + 1

class PeriodWidget(wx.Panel):
    def __init__(self, parent, ID=-1, label='',
                 pos=wx.DefaultPosition, size=(50, ROW_HEIGHT)):
        wx.Panel.__init__(self, parent, ID, pos, size,
                          wx.BORDER_NONE, label)
        self.label = label
        self.SetBackgroundColour("white")
        self.SetMinSize(size)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, evt):
        sz = self.GetClientSize()
        dc = wx.PaintDC(self)
        w,h = dc.GetTextExtent(self.label)
        dc.SetFont(self.GetFont())
        dc.DrawText(self.label, (sz.width-w-1), 1)

class View(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, None, -1, "Schedule",size=(600, 600))
        self.sizer = wx.GridBagSizer(hgap=1, vgap=1)
        self.scrolling_window = wx.ScrolledWindow( self )
        self.scrolling_window.SetScrollRate(1,1)
        self.scrolling_window.EnableScrolling(True,True)

        self.Bind(wx.EVT_SIZE, self.OnSize)

    def ColumnLabels(self):
        for ix in range(30):
            cw = ColumnWidget(self.scrolling_window, label='Column ' +
str(ix))
            self.sizer.Add(cw, pos=(0, ix + 1))

    def PeriodLabels(self, startDateTime, endDateTime):
        self.startDateTime = startDateTime
        self.endDateTime = endDateTime

        periodDuration = datetime.timedelta(minutes=30)
        periodStartTime = self.startDateTime
        self.periods = [None]
        ix = 1

        while periodStartTime < self.endDateTime:
            self.periods += [periodStartTime]
            lbl = str(periodStartTime.strftime('%H:%M'))

            pw = PeriodWidget(self.scrolling_window, label=lbl)
            self.sizer.Add(pw, pos=(ix, 0))
            ix += 1
            periodStartTime = periodStartTime + periodDuration

    def calEvents(self):
        labels = ['Appt Info', 'L2: more info .............', 'L3:
more info........', 'L4: more info...']
        place = [
                (1, 1, 3), (2, 2, 2), (3, 5, 1), (1, 8, 5), (1, 21,
5), (3, 29, 6)
                , (9, 1, 3), (9, 2, 2), (13, 5, 1), (15, 8, 5), (12,
21, 5), (13, 29, 6)
                , (27, 8, 2), (17, 12, 6)
                ]
        for (row, col, spa) in place:
            aw = ApptWindow(self.scrolling_window, labels=labels)
            self.sizer.Add(aw, pos=(row, col), span=(spa,1),
flag=wx.EXPAND)

    def OnSize(self, event):
        self.scrolling_window.SetSize(self.GetClientSize())

    def prepare(self):
        self.scrolling_window.SetSizer(self.sizer)
        self.Centre()

def mainTest():
    app = wx.App(redirect=False)
    view = View(None)
    today = datetime.date.today()
    strToday = str(today.strftime('%Y-%m-%d'))
    dateTimeStart = datetime.datetime.strptime(strToday + ' ' +
'08:00', '%Y-%m-%d %H:%M')
    dateTimeEnd = datetime.datetime.strptime(strToday + ' ' +
'22:00' , '%Y-%m-%d %H:%M')
    view.PeriodLabels(dateTimeStart, dateTimeEnd)
    view.ColumnLabels()
    view.calEvents()

    view.prepare()
    view.Show()
    app.MainLoop()

if __name__ == "__main__":
    mainTest()

Sam23 wrote:

I have a schedule created using GridBagSizer. See attached code that
displays the schedule. The first row contains column labels and the
first column contains row labels (time periods).The schedule has
scrollbars as the schedule may be larger than the window.
Appointments may span multiple rows but not columns. How can I freeze
the first row and first column while scrolling? i.e. when scrolling
right, the first column should always remain displayed and when
scrolling down, the first row should always remain displayed.

Sizers don't support doing that. Scrolled windows have the ability to be made to work that way, sort of. Basically you would need to use some extra child windows of the scrolled window. One for the first column, one for the first row, and one for the scrollable area. You would then need to call SetTargetWindow to make the scrolled window use the child for the scrollable area as the one to actually do the scrolling. (wx.grid.Grid is implemented this way.)

···

--
Robin Dunn
Software Craftsman

Robin, thanks for looking at this. This is my first attempt at Python
and wxPython programming, and I have been cracking my head on how to
do do the scroll with column and row labels for more than just a few
days. Can you point me to where I can find sample code for
SetTargetWindow?

I have tried looking for info on SetTargetWindow for some time; even
prior to my earlier post for help - saw it described tantalizingly in
the wx documentation as a solution for problems like scrolling
spreadsheet-like scenarios. But I haven't been able to find any
examples of how to do this. I googled this again today, and found
references to the same wx documentation and surprise, surprise! - your
reply to my posting! I have also looked through the 'wxPython in
Action' book and downloaded the wxPython source code to try to find
the source for the wx.grid.Grid but did not seem see it - it looks
like vc code only and not python code.

With this 3-window approach, does it mean that I should draw all the
windows manually i.e. do not use any sizers, but instead use exact
pixel specifications to align the rows and columns? The column and
row labels need to have correct alignment with the appointment windows
- the GridBagSizer seemed to be a good way to get stuff aligned in 2-
dimensions. If I have the main scrolling window and 2 child windows -
what is the best way to ensure that the child windows's cells are
aligned with the main scrolling window? Should I use sizers on all 3
windows, and is there a way to ensure that they 'match' (both
horizontally and vertically) ? The width of corresponding cells need
to match exactly otherwise they will 'run' after any mismatch. Or do I
just keep track of every pixel location without using sizers?

I also considered using wx.Grid instead of writing my own grid-like
solution, but there seems to be a catch. My row labels (the first
column) are in 30 minute interval e.g. 2.30 PM, 3:00 PM, 3.30 PM, but
my appointment windows need to have a resolution of 5 minutes e.g.
appointment from 2:15 to 3:05, so there is a need to draw appointment
windows that span part of the row to reflect this precision. I am not
sure if this can be done easily with wx.grid.Grid.

My apologies for the many questions. This is really my first attempt
at GUI programming - previous GUI experience was in pre .Net VB and
HTML, which are quite different paradigms altogether.

Any pointers or suggestion will be much appreciated. Thanks again.

···

On Jun 18, 4:22 am, Robin Dunn <ro...@alldunn.com> wrote:

Sam23 wrote:
> I have a schedule created using GridBagSizer. See attached code that
> displays the schedule. The first row contains column labels and the
> first column contains row labels (time periods).The schedule has
> scrollbars as the schedule may be larger than the window.
> Appointments may span multiple rows but not columns. How can I freeze
> the first row and first column while scrolling? i.e. when scrolling
> right, the first column should always remain displayed and when
> scrolling down, the first row should always remain displayed.

Sizers don't support doing that. Scrolled windows have the ability to
be made to work that way, sort of. Basically you would need to use some
extra child windows of the scrolled window. One for the first column,
one for the first row, and one for the scrollable area. You would then
need to call SetTargetWindow to make the scrolled window use the child
for the scrollable area as the one to actually do the scrolling.
(wx.grid.Grid is implemented this way.)

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

Robin, thanks for looking at this. This is my first attempt at Python
and wxPython programming, and I have been cracking my head on how to
do do the scroll with column and row labels for more than just a few
days. Can you point me to where I can find sample code for
SetTargetWindow?

This seems to be the syntax:

scrolledWindow.SetTargetWindow(self.somePanel)

See also http://207.22.26.166/gtitle.py

I have tried looking for info on SetTargetWindow for some time; even
prior to my earlier post for help - saw it described tantalizingly in
the wx documentation as a solution for problems like scrolling
spreadsheet-like scenarios. But I haven't been able to find any
examples of how to do this. I googled this again today, and found
references to the same wx documentation and surprise, surprise! - your
reply to my posting! I have also looked through the 'wxPython in
Action' book and downloaded the wxPython source code to try to find
the source for the wx.grid.Grid but did not seem see it - it looks
like vc code only and not python code.

I'm pretty sure that the grid is just wrapped C++...so what you're
seeing is the "swigged" interface. It's rather hard to read.

With this 3-window approach, does it mean that I should draw all the
windows manually i.e. do not use any sizers, but instead use exact
pixel specifications to align the rows and columns? The column and
row labels need to have correct alignment with the appointment windows
- the GridBagSizer seemed to be a good way to get stuff aligned in 2-
dimensions. If I have the main scrolling window and 2 child windows -
what is the best way to ensure that the child windows's cells are
aligned with the main scrolling window? Should I use sizers on all 3
windows, and is there a way to ensure that they 'match' (both
horizontally and vertically) ? The width of corresponding cells need
to match exactly otherwise they will 'run' after any mismatch. Or do I
just keep track of every pixel location without using sizers?

I also considered using wx.Grid instead of writing my own grid-like
solution, but there seems to be a catch. My row labels (the first
column) are in 30 minute interval e.g. 2.30 PM, 3:00 PM, 3.30 PM, but
my appointment windows need to have a resolution of 5 minutes e.g.
appointment from 2:15 to 3:05, so there is a need to draw appointment
windows that span part of the row to reflect this precision. I am not
sure if this can be done easily with wx.grid.Grid.

The wxPython demo has a bunch of grid demos. In the Simple wx.Grid
demo, it shows how to span rows and columns. I don't think that is
very hard for it to do. Using the grid widget may be simpler for
alignment purposes, although it is a fairly complex widget.

···

On Jun 18, 10:39 pm, Sam23 <qm0...@gmail.com> wrote:

My apologies for the many questions. This is really my first attempt
at GUI programming - previous GUI experience was in pre .Net VB and
HTML, which are quite different paradigms altogether.

Any pointers or suggestion will be much appreciated. Thanks again.

------
Mike

Mike, thanks for the reply and example. I have looked at both in
detail. Unfortunately, I could not get to understand the example
enough to figure out how to apply it to what I am trying to achieve.
Firstly, I hit this error: 'raise NoLicenseKey' - the program requires
a license key from Google. I tried to get this, but Google said: "We
are no longer issuing new API keys for the SOAP Search API." I
struggled with the code a bit but there were a lot of classes that I
wasn't familiar with (wxRemotelyScrolledTree, wxTreeCompanion..... and
couldn't get sufficient traction on the code. Any chance of getting a
simpler example that addresses the synchronised scrolling and maybe
also how to ensure alignment of the plain rectangular panels in the 3
different windows. At this point, I would probably be happy to
attempt a struggle with implementing it using wx.grid, but I need to
be able to draw objects that not just span rows, but span partial
rows.. e.g. spanning from the last quarter of the starting row and
first half of ending row. As a last resort, I am also considering just
drawing everything at a low level without sizers, subwindows, etc, and
doing scrolling manually - i.e. redrawing the entire screen in
response to scroll events. Thanks for your patience.

···

On Jun 19, 9:20 pm, Mike Driscoll <kyoso...@gmail.com> wrote:

On Jun 18, 10:39 pm, Sam23 <qm0...@gmail.com> wrote:

> Robin, thanks for looking at this. This is my first attempt at Python
> and wxPython programming, and I have been cracking my head on how to
> do do the scroll with column and row labels for more than just a few
> days. Can you point me to where I can find sample code for
> SetTargetWindow?

This seems to be the syntax:

scrolledWindow.SetTargetWindow(self.somePanel)

See alsohttp://207.22.26.166/gtitle.py

> I have tried looking for info on SetTargetWindow for some time; even
> prior to my earlier post for help - saw it described tantalizingly in
> the wx documentation as a solution for problems like scrolling
> spreadsheet-like scenarios. But I haven't been able to find any
> examples of how to do this. I googled this again today, and found
> references to the same wx documentation and surprise, surprise! - your
> reply to my posting! I have also looked through the 'wxPython in
> Action' book and downloaded the wxPython source code to try to find
> the source for the wx.grid.Grid but did not seem see it - it looks
> like vc code only and not python code.

I'm pretty sure that the grid is just wrapped C++...so what you're
seeing is the "swigged" interface. It's rather hard to read.

> With this 3-window approach, does it mean that I should draw all the
> windows manually i.e. do not use any sizers, but instead use exact
> pixel specifications to align the rows and columns? The column and
> row labels need to have correct alignment with the appointment windows
> - the GridBagSizer seemed to be a good way to get stuff aligned in 2-
> dimensions. If I have the main scrolling window and 2 child windows -
> what is the best way to ensure that the child windows's cells are
> aligned with the main scrolling window? Should I use sizers on all 3
> windows, and is there a way to ensure that they 'match' (both
> horizontally and vertically) ? The width of corresponding cells need
> to match exactly otherwise they will 'run' after any mismatch. Or do I
> just keep track of every pixel location without using sizers?

> I also considered using wx.Grid instead of writing my own grid-like
> solution, but there seems to be a catch. My row labels (the first
> column) are in 30 minute interval e.g. 2.30 PM, 3:00 PM, 3.30 PM, but
> my appointment windows need to have a resolution of 5 minutes e.g.
> appointment from 2:15 to 3:05, so there is a need to draw appointment
> windows that span part of the row to reflect this precision. I am not
> sure if this can be done easily with wx.grid.Grid.

The wxPython demo has a bunch of grid demos. In the Simple wx.Grid
demo, it shows how to span rows and columns. I don't think that is
very hard for it to do. Using the grid widget may be simpler for
alignment purposes, although it is a fairly complex widget.

> My apologies for the many questions. This is really my first attempt
> at GUI programming - previous GUI experience was in pre .Net VB and
> HTML, which are quite different paradigms altogether.

> Any pointers or suggestion will be much appreciated. Thanks again.

------
Mike

Sam23 wrote:

Mike, thanks for the reply and example. I have looked at both in
detail. Unfortunately, I could not get to understand the example
enough to figure out how to apply it to what I am trying to achieve.
Firstly, I hit this error: 'raise NoLicenseKey' - the program requires
a license key from Google. I tried to get this, but Google said: "We
are no longer issuing new API keys for the SOAP Search API." I
struggled with the code a bit but there were a lot of classes that I
wasn't familiar with (wxRemotelyScrolledTree, wxTreeCompanion..... and
couldn't get sufficient traction on the code. Any chance of getting a
simpler example that addresses the synchronised scrolling and maybe
also how to ensure alignment of the plain rectangular panels in the 3
different windows. At this point, I would probably be happy to
attempt a struggle with implementing it using wx.grid, but I need to
be able to draw objects that not just span rows, but span partial
rows.. e.g. spanning from the last quarter of the starting row and
first half of ending row. As a last resort, I am also considering just
drawing everything at a low level without sizers, subwindows, etc, and
doing scrolling manually - i.e. redrawing the entire screen in
response to scroll events. Thanks for your patience.

Based on your description of what you are wanting to create, doing it yourself might be a lot better than trying to pound it into wx.grid.Grid. You may also want to think about using something like FloatCanvas as it will handle all of the low-level drawing (and buffering, scaling, zooming, etc.) for you and let you deal with the higher level shape objects representing the labels, grid lines, appointment blocks, etc.

···

--
Robin Dunn
Software Craftsman

I looked at FloatCanvas, and it seems to be a separate project from
wx.Python. I am very much a python & wx novice and a little worried
about getting into another major new area that might be out of my
depth. These comments on the floatcanvas page didn't help:

'The code is well documented (well, you can figure out what's going
on, at least)'
'The code is sort of special case, but can be used for general
purposes, just as long as you don't mind the FloatCanvas feeling
slightly out of place.'
'Bugs and Limitations: Lots: patches, fixes welcome'.

I had the 'wxPython in action' book to help with learning wxPython.
Without a similar guide, I may have to flounder quite a bit (probably
get lost) in FloatCanvas before getting back on track.

I am still hoping to be able to use the basic tools in core wx.Python
to do the job. At least I have some familiarity with these. Also I am
thinking that using the more mature wx.Python library should result in
a more stable product than a relatively new project.

I have actually been attempting to modify my BagGridSizer code to do
the 'scroll with titles'; and over the weekend, have had some limited
success. Now I am able (with some hopefully minor bugs) to scroll
horizontally while keeping the first column 'frozen'.

I created my own scrollbars (wx.Scrollbar) instead of using
wx.ScrolledWindow. When the horizontal scroll event occurs, I
reshuffle all the affected windows in the BagGridSizer as follows:
Detach all window except those in the first column (the period
labels), and then put the detached windows back with the necessary
offsets, e.g. shift those that have scrolled out of visibility to a
far right end of the sizer (out of sight), and those to be shown are
shifted to the correct positions e.g those in column 3 are shifted to
column 2, those in column 4 are shifted to column 3, etc. It seems to
be working and I get to stick to using basic controls that I have
supporting documentation for.

It did take me a while to do this though.. I made a lot of mistakes at
first - mostly with the manual scrollbars.

Scrollbars didn't respond to events, then scrollbars would be of funny
size and position (left & top instead of right and bottom), then
scrollbars would not appear in visible part of window (forced out by
the appointment window), then scrollbars would always be one-step
behind in appearing with correct size and position during resizing
etc... I had to re-read the chapter on sizers (twice) to get these
fixed...

One fix was that I had to call the Layout() method after resizing the
appointment window - not doing this this was the cause of the out-of-
step scrollbars (it was getting wrong info on appt window size).
Another was to created another GridBagSizer containing the first
GridBagSizer - this outer GridBagSizer is for positioning the
scrollbars.

Anyhow, it seems to be working to some extent now. Hopefully the
vertical scrolling which is more complex (because appointments can
span rows and part of rows) can be done similarly.

What do you think of this approach? Am I doing something very klutzy?
Again, thanks for the patience and support.

···

On Jun 20, 8:41 am, Robin Dunn <ro...@alldunn.com> wrote:

Sam23 wrote:
> Mike, thanks for the reply and example. I have looked at both in
> detail. Unfortunately, I could not get to understand the example
> enough to figure out how to apply it to what I am trying to achieve.
> Firstly, I hit this error: 'raise NoLicenseKey' - the program requires
> a license key from Google. I tried to get this, but Google said: "We
> are no longer issuing new API keys for the SOAP Search API." I
> struggled with the code a bit but there were a lot of classes that I
> wasn't familiar with (wxRemotelyScrolledTree, wxTreeCompanion..... and
> couldn't get sufficient traction on the code. Any chance of getting a
> simpler example that addresses the synchronised scrolling and maybe
> also how to ensure alignment of the plain rectangular panels in the 3
> different windows. At this point, I would probably be happy to
> attempt a struggle with implementing it using wx.grid, but I need to
> be able to draw objects that not just span rows, but span partial
> rows.. e.g. spanning from the last quarter of the starting row and
> first half of ending row. As a last resort, I am also considering just
> drawing everything at a low level without sizers, subwindows, etc, and
> doing scrolling manually - i.e. redrawing the entire screen in
> response to scroll events. Thanks for your patience.

Based on your description of what you are wanting to create, doing it
yourself might be a lot better than trying to pound it into
wx.grid.Grid. You may also want to think about using something like
FloatCanvas as it will handle all of the low-level drawing (and
buffering, scaling, zooming, etc.) for you and let you deal with the
higher level shape objects representing the labels, grid lines,
appointment blocks, etc.

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

Sam23 wrote:

I looked at FloatCanvas, and it seems to be a separate project from
wx.Python.

There is a version of it distributed with wxPython. See wx.lib.floatcanvas and the samples in the demo.

[...]

What do you think of this approach? Am I doing something very klutzy?

Not the ideal approach, but if it works for you then go for it.

···

--
Robin Dunn
Software Craftsman

Using the 'window-shuffling' approach, and with much struggles, I have
managed to get the vertical scroll working as well, and also the
ability for the appointments to span partial rows. I agree that this
solution for scrolling seems less than ideal. There are a few
concerns. One is the code is quite complicated - I have to calculate a
lot of stuff when scrolling and when resizing the window. I also have
to have special coding for situations when an appointment is partially
scrolled off-the top of the screen (need to show lower part of
appointment). I am wondering if this approach is not efficient from a
performance point of view - the scrolling doesn't seem to be as fast
or as smooth as a scroll using wx.ScrolledWindow.

Unfortunately, this less then ideal approach is the only way I know to
meet the requirements at the moment.

Fortunately, it seems meets all the requirements; and to the casual
observer looking at the schedule, the scrolling with intact row and
column labels will look correct - albeit maybe a bit sluggish. At
least, I have a way forward.

Eventually, I hope to improve the code using a better approach. I am
still not certain about the correct approach for this problem. The
alternatives seems to be:
(a) SetTargetWindows with 3 sub-windows
(b) Pixel-by-pixel placement & complete redraw with scrolling or
resizing.
(c) FloatCanvas

I describe below my understanding (but mostly lack of understanding)
of the 3 approaches. Any enlightenment will be much appreciated.

------------(a) SetTargetWindows with 3 sub-windows
I have experimented with this further. I put all the column labels
into 1 panel, all the row labels into another panel, and all the
appointment labels into a 3rd panel. All these 3 panels are children
of a wx.ScrolledWindow. Positions and sizes (including VirtualSize)
are manually calculated (or just estimated and arbitrarily assigned)
at the pixel-level. I then used SetTargetWindows to point the
scrolling events to the appointment panel. After struggles (mostly
involving virtual and client sizes to get the scrollbars to show), I
got the SetTargetWindows scrolling to work. When I scrolled
vertically the appointments scrolled vertically smoothly and when I
scrolled horizontally, the appointments scrolled horizontally
smoothly. The row and column labels did not move and stayed
visible.

But this doesn't meet the requirements. When I scroll vertically, I
need the row labels to scroll vertically as well. Similarly, when I
scroll horizontally, I need the column labels to scroll horizontally
as well i.e. I need 2 windows to scroll, at any time, and in exact
alignment. But I can't figure out how to do this with SetTargetWindows
which seems to specify only 1 window as the recipient of the scroll
event.

One possibility is to intercept the scroll event, and then send
command to scroll the other window involved. But it is not clear to me
how the other 2 windows can be made scrollable without making them
scrollable windows with scrollbars showing (there shouldn't be
scrollbars on these 2 label subwindows; only on wx.ScrolledWindow).

The other problem is that, even if I can get 2 windows to scroll
together, there doesn't seem to be any easy way to ensure that the row
and column labels are aligned correctly with the appointment labels.
Because they are in 3 separate windows, I think it may be difficult to
ensure correct alignments - positioning is referenced to each window.
Do I need to find a way to reference positioning to a common reference
e.g. the containing frame?

------------(b) Pixel-by-pixel placement & complete redraw with
scrolling or resizing.

With this approach, there is only 1 panel, and all objects (row
labels, column labels and appointments) are manually placed. Because
they are all in the same frame, the calculations all have the same
reference. Recalculation and placing of objects is redone and the
entire screen repainted during scrolling events. The disadvantage of
this is that it will be very tedious to code, and the scrolling will
probably not be very efficient?

------------(c) FloatCanvas
If I am not mistaken, this approach will be more similar to (b: pixel-
by-pixel) than (a: SetTargetWindow) except that this uses that a
logical coordinate system rather than a pixel-by-pixel calculation.
Is this correct?

···

On Jun 25, 7:37 am, Robin Dunn <ro...@alldunn.com> wrote:

Sam23 wrote:
> I looked at FloatCanvas, and it seems to be a separate project from
> wx.Python.

There is a version of it distributed with wxPython. See
wx.lib.floatcanvas and the samples in the demo.

[...]

> What do you think of this approach? Am I doing something very klutzy?

Not the ideal approach, but if it works for you then go for it.

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

Christopher Barker wrote the following on 8/15/2009 12:11 AM:

For those of us that haven’t followed the whole thread, please post (or
re-post) a description of what you are trying to achieve, and maybe we
can offer something.

Chris,
Thanks. It will be great if I can get some help on doing this in a less onerous manner. I have re-posted this reply in the original discussion regarding the requirements: “Scrolling while freezing first column and first row”.

Not sure if I am doing something wrong with the search, but it looks like search in Google Group seems to be limited to recent months, so a search for the original discussion topic (prior to this re-post) doesn’t find it. Hopefully this re-post will work and bring it back to view.

I also re-attach a copy of the requirements implemented using GridBagSizer, lots of wx.Panels and lots and lots of coding to implement what appears to be fairly simple requirements. If you run the code, you will see quickly what I am trying to do:
(a) Have a spreadsheet-like view of rows and columns. Scrollbars should appear when contents exceed available window space. Row titles and column titles should never scroll out of view during scrolling but should scroll itself accordingly e.g. like the row and column titles in wx.Grid

(b) Contents may span rows or parts of rows. Rows are time periods of half an each: e.g. 2pm to 2:30pm, 2:30pm to 3pm etc, but appointments (the contents) may be from 2:15 pm to 3:45pm.

I have implemented these requirements as per the attached code. I only managed to get it working after a lot of difficulties, but this is also in part due to the fact that this was my first wx.Python attempt - I tripped over a lof of timing issues related to sizers, Destroy() etc.

This code now ‘seems’ to be working and with ‘reasonable’ performance but is probably not a good way to do it, and I suspect will require a lot more coding or hit some limitations as I try to finish up other requirements.

This is how the code was implemented using GridBagSizer and wx.Panels:
(a) The Appointments are aligned in the correct position with respect to rows and columns using grid positions in GridBagSizers e.g. an appointment for column 3 will have the same the xposition as the xposition for the cell for column 3.
(b) Scrolling is achieved by shuffling windows that have scrolled out of view to the far side, out of view (e.g. for row titles, they are shuffled to far bottom and for column titles they are shuffled to far right (out of view).
(c) Ability to span partial rows is achieved by creating a column of cells that represent 5-minute intervals. Each visible ‘period label’ row of half an hour spans 6 of the 5-minute interval cells; and appointments are placed by referencing the y-position of the 5-minute interval cells and not the 30-minute period labels. i.e. appointments can be displayed with 5-minute resolution.

The sample code also demonstrates one of the functions that the schedule should implement: change appointment. Click on an appointment, and it will be highlighted, and at the top, an option to shift the appointment is shown.

A look at this discussion thread will show the various options being discussed.

Cheers
Sam

cAps.zip (12.8 KB)

Not sure if I am doing something wrong with the search, but it looks like search in Google Group seems to be limited to recent months, so a search for the original discussion topic (prior to this re-post) doesn’t find it.

For previous posts, you can search Nabble:
http://www.nabble.com/wxPython-users-f35699.html

Che

I originally posted this in www.nabble.com, but shortly after,
realised that the forum had moved over to Google Groups, so I re-
posted the message in Google Group's wxPython. I then deleted the
posting in www.nabble.com. Subsequent replies and postings were all
done in Google Groups.

Strangely, even now, after 2 posts today on this thread, a search
using the subject "Scrolling while freezing first column and first
row" doesn't find this thread.

So it appears to be not related to the fact that it wasn't active for
a few months. If I search using Google, I can find this thread, but if
I search within Google Groups I can't find it! There is something
strange going on with this message.. Maybe it has to do with the fact
that it was posted during the a period when the group had already
transitioned to Google Group was already up, but Nabble was still
around.

···

On Aug 15, 2:11 pm, C M <cmpyt...@gmail.com> wrote:

> Not sure if I am doing something wrong with the search, but it looks like
> search in Google Group seems to be limited to recent months, so a search for
> the original discussion topic (prior to this re-post) doesn't find it.

For previous posts, you can search Nabble:http://www.nabble.com/wxPython-users-f35699.html

Che

I originally posted this in www.nabble.com, but shortly after,

realised that the forum had moved over to Google Groups, so I re-

posted the message in Google Group’s wxPython. I then deleted the

posting in www.nabble.com. Subsequent replies and postings were all

done in Google Groups.

FWIW, based on reading the Python list via Google Groups vs.

reading this list via email, I much prefer to be subscribed to the

list and post via sending it an email. Aside the from Groups’ heinous

spam being filtered out so well in Gmail (which you also use) one then

has a searchable archive in the copious Gmail account. I can star

and label messages and search for things I sent, attach a .py file

easily, etc. It’s been a valuable resource for me as I learn.

Che

I subscribe only to the digest and those related to my postings. I was
concerned about being overwhelmed with email. Maybe I should get all
of it. But the search is strange - it means others may have difficulty
searching within the group, possibly not just affecting my message but
others as well.

···

On Aug 15, 3:41 pm, C M <cmpyt...@gmail.com> wrote:

FWIW, based on reading the Python list via Google Groups vs.
reading this list via email, I much prefer to be subscribed to the
list and post via sending it an email. Aside the from Groups' heinous
spam being filtered out so well in Gmail (which you also use) one then
has a searchable archive in the copious Gmail account. I can star
and label messages and search for things I sent, attach a .py file
easily, etc. It's been a valuable resource for me as I learn.

I forgot to mention:
Run the cAps.py file. This is the ‘controller’ that runs other
necessary files.

···

On Aug 15, 12:34 pm, Sam23 <qm0...@gmail.com> wrote:

If you run the code, you will see quickly what I am trying to do:

Sam23 wrote:

Thanks. It will be great if I can get some help on doing this in a less onerous manner.

we'll see!

I also re-attach a copy of the requirements implemented using GridBagSizer, lots of wx.Panels and lots and lots of coding to implement what appears to be fairly simple requirements.

Actually, I don't think these requirements are simple at all.

(a) Have a spreadsheet-like view of rows and columns. Scrollbars should appear when contents exceed available window space. Row titles and column titles should never scroll out of view during scrolling but should scroll itself accordingly e.g. like the row and column titles in wx.Grid

(b) Contents may span rows or parts of rows.

hmmm - I guess what you really want is a wxGrid they allows spanning of rows. I haven't really used wx.Grid, but I take it doesn't support that.

Rows are time periods of half an each: e.g. 2pm to 2:30pm, 2:30pm to 3pm etc, but appointments (the contents) may be from 2:15 pm to 3:45pm.

I have implemented these requirements as per the attached code. I only managed to get it working after a lot of difficulties, but this is also in part due to the fact that this was my first wx.Python attempt

Well, you have jumped into the deep end!

This code now 'seems' to be working and with 'reasonable' performance

Is there something that the Chandler project developed that you could use? I imagine they had a Calendar control that might be able to be used like this.

but is probably not a good way to do it, and I suspect will require a lot more coding or hit some limitations as I try to finish up other requirements.

This is how the code was implemented using GridBagSizer and wx.Panels:

Well, there may be tweaks you can do to this, but the other option is really to draw the whole thing yourself, with wxDC or wxGraphicsContext. If you want to be able to zoom it , then FloatCanvas could help. Otherwise, I'd probably just do it from scratch, which would give you full control, but it would be a fair bit of of work. If this approach is working for you, I'd probably just stick with it. If you do go that route, I've been experimenting with using wxHtml to layout an render text to an off-screen bitmap, which can then be drawn with a DC -- that might be a way to do your text boxes.

    (b) Scrolling is achieved by shuffling windows that have scrolled out of view to the far side, out of view (e.g. for row titles, they are shuffled to far bottom and for column titles they are shuffled to far right (out of view).

It seems you should be able to use a wxScrolledWindow to do this for you, I know I've seen examples of putting lots of widgets on a ScrolledPanel around somewhere. That would require that the row an column labels be on different Panels, so I'm not sure how you'd keep them scrolled correctly, but I'll bet its possible.

    (c) Ability to span partial rows is achieved by creating a column of cells that represent 5-minute intervals. Each visible 'period label' row of half an hour spans 6 of the 5-minute interval cells; and appointments are placed by referencing the y-position of the 5-minute interval cells and not the 30-minute period labels. i.e. appointments can be displayed with 5-minute resolution.

nice trick!

The sample code also demonstrates one of the functions that the schedule should implement: change appointment. Click on an appointment, and it will be highlighted, and at the top, an option to shift the appointment is shown.

Hmm -- FloatCanvas could help with this too. It supports binding to objects.

You also could pop up a "editor" window right on top of the selected app. instead.

As I think about it, you are doing a fair bit of work keeping track of layout and all yourself, so it may not be that much more work to draw stuff yourself, too.

I don't know it this helped any,

-Chris

···

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

Chris,

Thanks for the detailed look at this issue. My replies below.

an experienced wx.Python programmer could have done this in in just a
few hours and much fewer lines of code. I still have a suspicion that
my solution is too much code.
Mike Driscoll pointed out that the wx demo shows that wx.Grid can span
rows and columns. The problem is that I need to span partial rows
(better than 30 minute resolution), which is not supported by wx.Grid.
I also suspect that I will eventually probably need to have some
control over the appearance of the column and row titles that wx.Grid
may not provide.
Yes, I suspected as much. Not the best screen for a newbie (new to
Python, wxPython & fairly new to OO) to attempt. I was hoping I
could do it with the basic tools in wx like sizers, Scrollbars, etc,
but it turned out to be much trickier than I expected.
I am not familiar with the Chandler project. I Googled it, but didn’t
find something that I could get a handle on. I suspect it is probably
too big to fit easily into my application and that I will get lost in
the code since it appears to cover many areas.
Drawing the whole using wx.DC is quite an attractive option for me,
since I already know how to do this. I use Paint for the wx.Panel
classes. The full featured zoom in FloatCanvas would be nice, but is not
essential
I have 2 critical questions regarding these 2 alternatives:
(a) sticking with the present approach (using Sizers & lots of
wx.Panels)
(b) redrawing table as a wx.Panel (start from scratch)
(a) sticking with the present approach (using Sizers & lots of
wx.Panels)
Currently, I think I have about 400+ wx.Panels. At the most, this will
increase to 500 wx.Panels. Am I reaching any sorts of limits in
wx.Python with these numbers? i.e will the application start to slow
down or become unstable with these types of numbers of wx.Panels? Or
when other TLW are opened concurrently? (albeit much simpler ones).
Most of the panels are fairly simple - rectangular displays with some
background colour and text, and possibly some simple graphics (coloured
boxes). The 5-minute interval windows (almost 200) are even simpler,
just background colour with no contents and no events. Event handling
is also fairly simple; no events are coded for the wx.Panels, the
click is handled at the Application level (traced back to the
originating wx.Panel).
Currently, the performance seems to be acceptable; nothing takes longer
than a second, even on an old laptop that is about 6 years old - a
Celeron 1000Hz with 512 MB memory with shared video memory. (b) Redrawing table as a wx.Panel (start from scratch)
Is this significantly faster and more stable than the current approach?
I was under the impression that (a) had some advantage e.g. with (b) a
Paint Event may frequently repaint the full table, whereas with (b) it
is is sometimes only necessary to repaint one of the wx.Panels. I did
some testing and it looks like I can repaint just part of the window
with (a). As an example, when the user clicks on the time slot, it is highlighted
with a yellow border. With the current many wx.Panels approach, I only
need to redraw the selected wx.Panel. With the single large wx.Panel
approach would I need to repaint the entire window? It looks like I can
specify which part of the screen to repaint. (using BufferedDC? and
specifying where to paint?)
My first posting in this thread had a simple single wx.ScrolledWindow
containing the schedule. This scrolled fine, both horizontally and
vertically - the catch was of course, with a single panel, the row and
columns titles scrolled out of sight…
I think SetTarget is supposed to allow an inner window to be the target
of scrolling whilst the scrollbars show in the ‘outer window’. But I am
not sure how to use this, especially with 2 ‘outer’ windows. I suspect
the solution is to place all 3 panels using absolute positioning,
trapping scroll events, and then redrawing the column and row labels
i…e SetTarget takes care of scrolling of the contents, but the titles
still have to be redrawn manually to simulate ‘scrolling’. If not
done correctly then the positioning of the appointment may drift with
respect to the labels, e.g. a 3 pm appointment may slide to match up
against a 3.30pm row label. I suspect the answer should be to use
absolute positioning for all 3 panels. Anyway, I am just speculating.
Initially, I thought it was good enough to have a display resolution of
30 minutes. When I realised that it wasn’t, I was stumped for quite a
while. I had already done quite a lot of code based on scrolling with
resolution of 30 minutes. Luckily I figured out this way around it.
I have an editor pop up for adding, editing or deleting appointments.
For shift, I thought it was more intuitive for the user to simply point
and click to do the shift.
Actually, it will probably be less work than what I am doing currently.
If you look at the code, you will see that there is a lot of it. The
catch is of course, the ‘harder way’ is already done (after much blood)
and ‘appears’ to be stable and the ‘easier way’ isn’t.
Thanks, it definitely helps. At least I have some perspective of the
effort so far, and an evaluation the possible paths forward. Any
further insights will be much appreciated; especially on the pros and
cons of the 2 approaches.

···

Chris.Barker@noaa.gov

Date: 2009/8/18
Subject: [wxPython-users] Re: Scrolling while freezing first column and
first row
To:
Actually, I don't think these requirements are simple at all.

wxPython-users@googlegroups.com


hmmm - I guess what you really want is a wxGrid they allows spanning of
rows. I haven't really used wx.Grid, but I take it doesn't support that.


Rows are time periods of
half an each: e.g. 2pm to 2:30pm, 2:30pm to 3pm etc, but appointments
(the contents) may be from 2:15 pm to 3:45pm.
I have implemented these requirements as per the attached code. I only
managed to get it working after a lot of difficulties, but this is also
in part due to the fact that this was my first wx.Python attempt
Well, you have jumped into the deep end!

Is there something that the Chandler project developed that you could
use? I imagine they had a Calendar control that might be able to be used
like this.

Well, there may be tweaks you can do to this, but the other option is
really to draw the whole thing yourself, with wxDC or wxGraphicsContext.
If you want to be able to zoom it , then FloatCanvas could help.
Otherwise, I'd probably just do it from scratch, which would give you
full control, but it would be a fair bit of of work. If this approach is
working for you, I'd probably just stick with it.
It seems you should be able to use a wxScrolledWindow to do this for
you, I know I've seen examples of putting lots of widgets on a
ScrolledPanel around somewhere. That would require that the row an
column labels be on different Panels, so I'm not sure how you'd keep
them scrolled correctly, but I'll bet its possible.
(c) Ability to span partial rows is achieved by creating a column of
cells that represent 5-minute intervals.
nice trick!

The sample code also demonstrates one of the functions that the schedule
should implement: change appointment. Click on an appointment, and it
will be highlighted, and at the top, an option to shift the appointment
is shown.

Hmm -- FloatCanvas could help with this too. It supports binding to objects.
You also could pop up a "editor" window right on top of the selected
app. instead.
As I think about it, you are doing a fair bit of work keeping track of
layout and all yourself, so it may not be that much more work to draw
stuff yourself, too.
I don't know it this helped any,

I would try this approach, using a ScrolledWindow and a buffered DC.
+: no risk of slow-down due to many wx components
+: complete control of look and feel
-: have to write code to mimic the decorations supplied by wx, eg
   borders, buttons, ...
-: needs a data structure to describe location on screen that can
   be used by a) drawing code b) mouse handling code

I would have separate windows for the 1st row and column and update
them in response to the Draw events caused by scrolling.

Phil

···

On Aug 18, 6:46 am, Sam23 <qm0...@gmail.com> wrote:

...
Drawing the whole using wx.DC is quite an attractive option for me,
since I already know how to do this. I use Paint for the wx.Panel classes.

Sam23 wrote:

Drawing the whole using wx.DC is quite an attractive opaybe you tion for me, since I already know how to do this. I use Paint for the wx.Panel classes.

If you want to be able to zoom it , then FloatCanvas could help.
  

The full featured zoom in FloatCanvas would be nice,

Well, you get it for free, so why not?

increase to 500 wx.Panels. Am I reaching any sorts of limits in wx.Python with these numbers?

I'm not sure, but that does seem like a lot.

boxes). The 5-minute interval windows (almost 200) are even simpler, just background colour with no contents and no events.

maybe you don't need windows for that -- StaticBitmaps are not real Windows on all platforms. Or is that what you are using anyway?

Currently, the performance seems to be acceptable; nothing takes longer than a second,

a second is a pretty long time, actually.

some testing and it looks like I can repaint just part of the window with (a).

yes, you only need to paint what has changed.

As an example, when the user clicks on the time slot, it is highlighted with a yellow border. With the current many wx.Panels approach, I only need to redraw the selected wx.Panel. With the single large wx.Panel approach would I need to repaint the entire window?

nope, only what's been damaged, though you need to figure out what that is.

It looks like I can specify which part of the screen to repaint. (using BufferedDC? and specifying where to paint?)

Buffering is a good idea too -- you can buffer the Calendar, then draw the highlighted event over the buffer. You only need to re-blit the buffer to restore it. FloatCanvas does something like this with the "foreground".

Actually, it will probably be less work than what I am doing currently.

maybe, but it adds up.

The more I think about it, the more I think FloatCanvas may help. I'm going to see if I can whip up a bit of a demo...

-Chris

···

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

Phil,
Thanks for the coherent and concise summary of the pros and cons of going with ScrolledWindow and a bufferedDC. My replies below.

Phil Mayes wrote the following on 8/18/2009 11:14 PM:

I would try this approach, using a ScrolledWindow and a buffered DC.
+: no risk of slow-down due to many wx components
  

Yes, nice.

+: complete control of look and feel
  

Yes, very nice.

-: have to write code to mimic the decorations supplied by wx, eg
   borders, buttons, ...
  

Not too bad. Only the Calendar window is self-drawn. Other parts of the frame can have panels using wx Buttons, Menu, status bar, etc. The calendar is a table with text and some simple graphic objects (coloured boxes), so it probaly won't look too out of place.

-: needs a data structure to describe location on screen that can
   be used by a) drawing code b) mouse handling code
  

Yes, this will be the bulk of the work.

I would have separate windows for the 1st row and column and update
them in response to the Draw events caused by scrolling.
  

I think there may be some advantages with not using ScrolledWindow i.e. draw everything using buffered DC only. If I use ScrolledWindow, I will need to coordinate the scrolling events of the ScrolledWindow with that of the row and column titles. This means that I am handling scrolling using 2 different ways (one via ScrolledWindow and another via manual redrawing). I am not sure I know how to do this; have to try. Thing is, since I am going to have to 'scroll' the row and column labels, I might as well handle the 'scroll' of the contents - i.e. only 1 set of logic to code for; and no worries about scrolling becoming un-cordinated.