Custom Dialog for wx.ListCtrl

I have several wx.ListCtrl, and the user can enter new data, clear existing
data, edit existing data, and save the data. Looking at the WriteItYourself
example for the wx.ListCtrl, I learned that values are not directly entered,
but are indirectly via wx.TextCtrl. So, I created a custom dialog box to do
this.

   The code is attached; by itself it works just fine. Before I integrate it
into the main application I'd appreciate your comments on how it's
implemented and laid out in the dialog box. Unlike all my previous requests,
this is a _working example_ :slight_smile: but there may be more efficient ways of doing
the same thing.

Rich

customDlg.py (1.63 KB)

···

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

   The code is attached; by itself it works just fine. Before I integrate it
into the main application I'd appreciate your comments on how it's
implemented and laid out in the dialog box. Unlike all my previous requests,
this is a _working example_ :slight_smile: but there may be more efficient ways of doing
the same thing.

You have:
p------------------------q

                       >
Number: |
[ ] |
Component: |
[ ] |
                       >
    [Apply] |

b------------------------d

I prefer:
p---------------------------------q

                                >
Number: [ ] |
Component: [ ] |
                                >
              [Cancel] [ OK ] |

b---------------------------------d

Ricardo

···

On Fri, 2005-12-09 at 13:04 -0800, Rich Shepard wrote:

Ricardo,

   I do, too. I'm flailing around trying to figure out how to get it that way.
Also, I prefer it as a method than a class, so I'm trying to learn how to
convert it from the latter to the former.

Rich

···

On Fri, 9 Dec 2005, Ricardo Pedroso wrote:

You have:
p------------------------q
> >
>Number: |
>[ ] |
>Component: |
>[ ] |
> >
> [Apply] |
b------------------------d

I prefer:
p---------------------------------q
> >
> Number: [ ] |
> Component: [ ] |
> >
> [Cancel] [ OK ] |
b---------------------------------d

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Rich Shepard wrote:

  I do, too. I'm flailing around trying to figure out how to get it that way.

Like the enclosed, maybe?

Also, I prefer it as a method than a class, so I'm trying to learn how to
convert it from the latter to the former.

I'm not sure I follow what you mean. Why wouldn't you want this particular dialog to be a class? It seems like a pretty classic OO design to me.

-Chris

customDlg.py (2.26 KB)

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (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

Like the enclosed, maybe?

Chris,

   Yes; exactly. I got stuck by trying to wrench the approach of my example
code into a different shape. Using different sizers is both a perfect
solution and a good lesson for me.

   The point of all this is to populate a wx.ListCtrl; now I can focus on
that.

I'm not sure I follow what you mean. Why wouldn't you want this particular
dialog to be a class? It seems like a pretty classic OO design to me.

   Probably because I'm still thinking non-OO.

Thanks,

Rich

···

On Fri, 9 Dec 2005, Christopher Barker wrote:

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Yes.

   Now I'm missing the proper technique for capturing the entered values so
they can be returned to the calling method and added to the wx.ListCtrl. What
I have, after a number of experiments, is attached. It doesn't quite work.

Thanks,

Rich

customDlg.py (2.47 KB)

···

On Fri, 9 Dec 2005, Christopher Barker wrote:

Like the enclosed, maybe?

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

It's all about scoping and knowing where your data is coming from.

You've got this bad habit of passing events to things that don't need an
event. Rule of thumb: don't pass events unless there is a possibility
of them being Skip()ed

- Josiah

#!/usr/bin/env python

# customdialog1.py

import wx

class MyDialog(wx.Dialog):
    def __init__(self, parent, ID):
        wx.Dialog.__init__(self, parent, ID)

        mainSizer = wx.BoxSizer(wx.VERTICAL)

        gridSizer = wx.FlexGridSizer(2, 2, 5, 5)
        gridSizer.AddGrowableCol(1, 1)
        gridSizer.Add(wx.StaticText(self, wx.ID_ANY, 'Number: '))
        numT = wx.TextCtrl(self, wx.ID_ANY, size=wx.Size(25, 25), style=wx.RAISED_BORDER)

          self.numT = numT = wx.TextCtrl(self, wx.ID_ANY, size=wx.Size(25, 5), style=wx.RAISED_BORDER)

        self.Bind(wx.EVT_TEXT_ENTER, self.OnNewNumber, numT)
        gridSizer.Add(numT)
        gridSizer.Add(wx.StaticText(self, wx.ID_ANY, 'Component: '))
        compT = wx.TextCtrl(self, wx.ID_ANY, size=wx.Size(150, 25), style=wx.RAISED_BORDER)

          self.compT = compT = wx.TextCtrl(self, wx.ID_ANY, size=wx.Size(150, 25), style=wx.RAISED_BORDER)

        self.Bind(wx.EVT_TEXT_ENTER, self.OnNewComponent, compT)
        gridSizer.Add(compT)

        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
        applyBut = wx.Button(self, wx.ID_APPLY, 'Apply')

        buttonSizer.Add(applyBut, 0, wx.ALL, 5)

        mainSizer.Add(gridSizer, 0, wx.ALL, 10)
        mainSizer.Add(buttonSizer, 0 , wx.ALIGN_RIGHT)
        
        applyBut.Bind(wx.EVT_BUTTON, self.OnAcceptData)
        self.SetSizerAndFit(mainSizer)

    def OnNewNumber(self, event):
        num = None

          num = self.numT.GetValue()

        foo = wx.MessageDialog(self, 'You entered: %\n' % numT.GetValue(),

          foo = wx.MessageDialog(self, 'You entered: %\n' % num,

                               'Text Entered', style=wx.OK|wx.ICON_INFORMATION)
        foo.ShowModal()
        foo.Destroy()
        return num

    def OnNewComponent(self, event):
        comp = None

          comp = self.compT.GetValue()

        bar = wx.MessageDialog(self, 'You entered: %\n' % compT.GetValue(),

          bar = wx.MessageDialog(self, 'You entered: %\n' % comp,

                               'Text Entered', style=wx.OK|wx.ICON_INFORMATION)
        bar.ShowModal()
        bar.Destroy()
        return num

          return comp

    def OnAcceptData(self, event):
        self.OnNewNumber(event)
        self.onNewComponent(event)

          self.OnNewComponent(event)
          #You should do something with the data returned by
          #the above two methods.
          #Perhaps by passing them to self.GetParent()

···

Rich Shepard <rshepard@appl-ecosys.com> wrote:

        self.Close()

class MyFrame(wx.Frame):
    def __init__(self, parent, ID, title):
        wx.Frame.__init__(self, parent, ID, title, size=(550,500))
        panel = wx.Panel(self, -1)
        wx.Button(panel, 1, 'Show Custom Dialog', (100,100))

        wx.EVT_BUTTON(self, 1, self.OnShowCustomDialog)

    def OnShowCustomDialog(self, event):
        dia = MyDialog(self, -1)
        dia.ShowModal()
        dia.Destroy()

class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame(None, -1, 'customdialog1.py')
        frame.Show(True)
        frame.Centre()
        return True

app = MyApp(0)
app.MainLoop()

Josiah,

   Obviously, this seemed to me to be the proper way to retrieve the entered
values so they could be added to the wx.ListCtrl and stored for use in other
components of the application. Since I did not do it correctly it is a safe
assumption that I do not know the correct way.

   Pointing out the obvious -- that I did it wrong -- does not teach me how to
do it correctly so that I can apply it here and on the other ListCtrl widgets
in the application.

Rich

···

On Fri, 9 Dec 2005, Josiah Carlson wrote:

You've got this bad habit of passing events to things that don't need an
event. Rule of thumb: don't pass events unless there is a possibility of
them being Skip()ed

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Ah, my apologies. I just noticed that you did do that, intermingled with
the code I originally sent. I think my example came from the wiki; I'll check
on that.

Thanks for the help, Josiah,

Rich

···

On Sat, 10 Dec 2005, Rich Shepard wrote:

Pointing out the obvious -- that I did it wrong -- does not teach me how
to do it correctly so that I can apply it here and on the other ListCtrl
widgets in the application.

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

self.numT = numT = wx.TextCtrl(self, wx.ID_ANY, size=wx.Size(25, 5), style=wx.RAISED_BORDER)

   Why the multiple assignments rather than a single one?

         #You should do something with the data returned by
         #the above two methods.
         #Perhaps by passing them to self.GetParent()

   Thanks for the hint. I wasn't sure how to get them to the ListCtrl,

   I appreciate learning how to correctly apply wxPython from this mail list.

Rich

···

On Fri, 9 Dec 2005, Josiah Carlson wrote:

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

> self.numT = numT = wx.TextCtrl(self, wx.ID_ANY, size=wx.Size(25, 5), style=wx.RAISED_BORDER)

   Why the multiple assignments rather than a single one?

So that I wouldn't also have to change two later lines that read...

        self.Bind(wx.EVT_TEXT_ENTER, self.OnNewNumber, numT)
        gridSizer.Add(numT)

I could have added a line that read "self.numT = numT" instead. It's
mostly a style thing, along with a bit of laziness.

> #You should do something with the data returned by
> #the above two methods.
> #Perhaps by passing them to self.GetParent()

   Thanks for the hint. I wasn't sure how to get them to the ListCtrl,

Alternatively in the __init__ method, you can keep a reference to the
parent control via "self.parent = parent".

- Josiah

···

Rich Shepard <rshepard@appl-ecosys.com> wrote:

On Fri, 9 Dec 2005, Josiah Carlson wrote:

   Why the multiple assignments rather than a single one?

So that I wouldn't also have to change two later lines that read...

        self.Bind(wx.EVT_TEXT_ENTER, self.OnNewNumber, numT)
        gridSizer.Add(numT)

   I see. Thanks.

I could have added a line that read "self.numT = numT" instead. It's
mostly a style thing, along with a bit of laziness.

   Perfectly clear.

Josiah,

   Now I have an OO question that is related to applying this one custom
dialog to multiple situations. For example, on one notebook tab I have three
list controls, each for a different category of components. We can refer to
them as Cat. A, Cat. B., and Cat. C.

   For each one, I have buttons to add, edit, or clear values in each
ListCtrl. Right now I'm working on properly implementing the add function. It
makes sense to call the same custom dialog for each of the three categories,
but the values need to be returned to the proper ListCtrl.

   The sample code from the wiki has code to insert the entered values in a
single LC:

   def OnAdd(self,event):
     if not self.tc1.GetValue() or not self.tc2.GetValue():
       return
     num_items = self.lc.GetItemCount()
     self.lc.InsertStringItem(num_items, self.tc1.GetValue())
     self.lc.SetStringItem(num_items, 1, self.tc2.GetValue())
     self.tc1.Clear()
     self.tc2.Clear()

   But, the OnAdd needs to change 'lc' and 'tc1'/'tc2' to the appropriate Cat.
A, Cat. B, or Cat. C, depending upon which LC called the custom dialog
method. I'm not sure how to ensure that the values are returned to the
calling function so they'll be inserted in the proper ListCtrl.

Thanks again,

Rich

···

On Sat, 10 Dec 2005, Josiah Carlson wrote:

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

I was attempting to remind you of a previous change I also made to the
TextEntryDlg() method in message id <20051207154731.8D00.JCARLSON@uci.edu> .
I removed the event argument from a method where it was unnecessary.

I probably should have been clearer.

- Josiah

···

Rich Shepard <rshepard@appl-ecosys.com> wrote:

On Fri, 9 Dec 2005, Josiah Carlson wrote:

> You've got this bad habit of passing events to things that don't need an
> event. Rule of thumb: don't pass events unless there is a possibility of
> them being Skip()ed

Josiah,

   Obviously, this seemed to me to be the proper way to retrieve the entered
values so they could be added to the wx.ListCtrl and stored for use in other
components of the application. Since I did not do it correctly it is a safe
assumption that I do not know the correct way.

Oh, d'oh! All I need do is pass the appropriate parameters to the function
and grab what it returns.

Rich

···

On Sat, 10 Dec 2005, Rich Shepard wrote:

Now I have an OO question that is related to applying this one custom
dialog to multiple situations. For example, on one notebook tab I have
three list controls, each for a different category of components. We can
refer to them as Cat. A, Cat. B., and Cat. C.

--
Richard B. Shepard, Ph.D. | Author of "Quantifying Environmental
Applied Ecosystem Services, Inc. (TM) | Impact Assessments Using Fuzzy Logic"
<http://www.appl-ecosys.com> Voice: 503-667-4517 Fax: 503-667-8863

Rich,

I've lost track a biot of what your needs are, but:

        #Perhaps by passing them to self.GetParent()
     

Alternatively in the __init__ method, you can keep a reference to the
parent control via "self.parent = parent".

In general, I wouldn't do either, What that does is create a probably unneccesary coupling between the dilog and the parent that calls it.

If this is a modal dialog, then you could do what is most common for modal dilaogs:

Collect the data, store it in some way in the instance, then when the dialog os closed, get the data. Something like this psuedo code:

Result = MyDialog.ShowModel()
If Result = OK:
       Data = MyDialog.GetTheData()
else:
    do whatever you'd do if the dialog is cancled

This is a pretty common idiom, you should be able to find examples

On the other hand, if this is not a modal dilaog (or if you wantt to be able to have an "apply" button that applies changes without closing) then you do need a way to talk to the app and give it the updated data. In thios case, I'd pass in an instance of your model to the dialog, and have the dialog call a method in the model that updates the relevant data. I think this is cleaner than calling amethod on teh parent. The parent may well be a GUI object that doesn't really need the data at all.

-Chris

Rich Shepard wrote:

For example, on one notebook tab I have three list controls, each for a different category of components. We can refer to
them as Cat. A, Cat. B., and Cat. C.

  For each one, I have buttons to add, edit, or clear values in each
ListCtrl.

Create a class that holds a ListCtrl and the three buttons, all together. Then you create three instances of this class, one each for A, B and C.

> It

makes sense to call the same custom dialog for each of the three categories,
but the values need to be returned to the proper ListCtrl.

Now call your dialog from within that class, and yhou can use Josiah's suggestion of passing an instace of your list class into the dialog, or do what I suggested, and calll a GetData method on the Dialog oafter it closes. Since each listclass instance is separte, there is no confusion about which one you want changed.

This is a great example of what I've been suggesting to you for a while: when you have group of controls that work together, make a calss for them, particularly if it's a repeated pattern!

Here you have three things that are the some kind of object, but contain distinct data: instances of a class!

-Chris