Problem with controls mass production

I like to create a list of dictionaries, where each dictionary contains the properties for one of my controls.

    Buttons = [
        {"Name": "Load",   "Tip": "Click here to load (or re-load) the selected configuration."},

        {"Name": "Save",   "Tip": "Click here to save your current settings into the selected configuration."},

        {"Name": "Go",     "Tip": "Click here to proceed using the current settings."},
        {"Name": "Cancel", "Tip": "Click here to exit without doing anything."},

        ]

I then loop through the list and create the controls and bind them all to a single method (OnClick or OnChange, or whatever).
for btn in Buttons:
b = wx.Button(pnl, -1, label=btn[“Name”], name=btn[“Name”])

        b.SetToolTip(wx.ToolTip(btn["Tip"]))
        boxTopRight.Add(b, 0, wx.ALIGN_CENTER|wx.ALL)
        boxTopRight.Add((10,10), 0, wx.EXPAND)
        self.Bind(wx.EVT_BUTTON, self.OnClick, b)

My OnClick looks like this:

def OnClick(self, event):
    btn = event.GetEventObject().Name
    if (btn == "Cancel"):
        etc...

but this could probably be a dictionary instead of a bunch of ifs…

···

On Thu, Jul 10, 2008 at 10:03 AM, raffaello barbarossa.platz@gmail.com wrote:

Sometimes in a frame I need build a whole set of controls (usually buttons or wx.textCtrl). Since they differ only for a few properties, the pythonic way would be to out all those properties in a list and pass it to a “for” loop. But this does not work with the variables used to identify these controls at frame scope. For example:

btClose = btHide = btShow = None

and then in init:

params = [[‘Exit’, pos, self.btClose], etc.]
for i in range(len(params)):
bt = self.BuildButton(params[i])

      self.btClose.Bind(etc)

brings about the exception “AttributeError: None object has no attribute ‘Bind’”

It seems that if you pass an argument the function receives its value, instead of its address.

The only workaround solution I could devise was a set of
if i == 0: self.btClose = bt
elif i == 1: self.btHide = bt, etc.

that, at least to me, seems more pachidermic than pythonic.

Anybody has a better solution?


www.fsrtechnologies.com

Mike Driscoll wrote:

These are interesting ways to do this, but how do you add specific buttons to specific sizers when you create them this way? I've tried this before based on some similar code in the demo, but then I can't get button_a into sizer_a and button_b into sizer_b because I don't have a handle to either button.

We need a simple, complete example for this to play with, but the short answer is obvious: you need a reference to the buttons. Store them in some container -- a list or a dictionary, or even a custom container class that has more features, if you need. I'd tend to go with a dict, as that way it's easier to know what button you are dealing with.

In general, though, the buttons are there for something, and what that something is will guide how to store them.

-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

Christopher,

<div class="moz-text-flowed" style="font-family: -moz-fixed">Mike Driscoll wrote:

These are interesting ways to do this, but how do you add specific buttons to specific sizers when you create them this way? I've tried this before based on some similar code in the demo, but then I can't get button_a into sizer_a and button_b into sizer_b because I don't have a handle to either button.

We need a simple, complete example for this to play with, but the short answer is obvious: you need a reference to the buttons. Store them in some container -- a list or a dictionary, or even a custom container class that has more features, if you need. I'd tend to go with a dict, as that way it's easier to know what button you are dealing with.

In general, though, the buttons are there for something, and what that something is will guide how to store them.

-Chris

I don't have that code anymore as that was about a year ago and I ended up doing it completely differently. Anyway, if I decide to write a program with a lot of buttons, I'll post the sample for you guys.

Thanks,

Mike

I would put money on your GUI code being bloated, probably due to
duplication similar to what I showed earlier. But I would point out
one major thing; you are new to wxPython programming, and probably
programming in Python, and even likely programming generally. As long
as you learn good habits, your code can get better.

Regarding whether or not to use delayedresult...your game produces
output in a log file. You are currently checking the content of the
file to see if it has new lines, and if so, processing them. Rather
than using an entire thread to check to see if there is new log
output, you can just use a wx.Timer(). wx.Timer() is designed to fire
an event after a specified delay...like every 1 second. If you get an
event every second or so, and upon receiving that event, spend some
time processing the log file from the last place you processed it,
which outputs information to the different windows. I'm sure it would
work just fine.

- Josiah

···

On Fri, Jul 11, 2008 at 10:43 AM, Steve Freedenburg <stevefreedenburg@charter.net> wrote:

Alright Mr. Carlson,

Here is my next question then. Woud the wx.lib.delayedresult accomplish
what I'm looking for?
The "long running task" is the only thing this program really needs to
accomplish. And once it's
started, it is 99% of what needs to be happening. The other frames to
recieve messages are fluff.
By flluff I mean, a nice way to show a result with pretty colors and fonts.

The comments ARE ugly, and some were even added to the e-mail to try and
explain things better.
When I looked at the code in the e-mail it confused me, and I wrote it, so I
commented more.

I'm really floored... from 35 lines to 6... that's amazing.... I don't
quite understand your 6 lines of
code, compared to my 35. but I'm sure I can muddle my way through it.

So basically what you saying is, I have a GUI consisting of over 1000 lines
of code, to execute a task
that at best (your code) is 6 lines, and worst (my code) is 35. I wonder if
my GUI is overly bloated code
as well... The globals only need to be assigned if they are being
changed.... Ok, well that is probably
a few dozen likes that can be removed. I'm depressed now ;P...

----- Original Message ----- From: "Josiah Carlson"
<josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 13:22
Subject: Re: [wxpython-users] Am I on the right path?

Yes, this is what a LongRunningTask is.

You asked for critique, so here's a bit of critique :wink: .

Unless you are writing to a global variable, you don't need to declare
it as global. You can read globals any time.

You don't need to keep calling Publisher(), it's a singleton, it
always returns itself. Just use Publisher.sendMessage(). If you end
up running it in a secondary thread (what LongRunningTask will give
you the option to do), you can use wx.CallAfter(Publisher.sendMessage,
...) to get it executing in the main thread again. You can also use a
wx.Timer() to schedule calls to check lines.

Your message names, ('container2', 'container2message') seem pretty
redundant. ('container2', 'message') would be sufficient. Then
again, I would personally use 'container.2.message' (you can use
dotted strings instead of tuples of strings to get the same semantics
without all of the parens, quotes, and commas), as it splits types,
then which object of that type, then what functionality of that
particular object. Brevity + clarity = win.

Describing the functionality of every line with a comment at the end
of the line that is doing something is really ugly, plus long lines
can be difficult to read. Don't get me wrong, I've done it myself,
but if I am to comment, I comment the line just before. Speaking of
comments, you've got the same comment and code 6 times. Don't copy
and paste!

for i in xrange(1,7):
  if FirstReadLine.find(globals()['C%iSearchString'%i]):
      # If the line has been matched, send it off to the proper container
      Publisher.sendMessage('container.%i.message'%i, [FirstReadLine])
  else:
      print "no match on %i"%i

Those 6 lines will replace 35 lines of your original code (including
blank lines).

If you really want to save yourself some time, don't use named search
strings, etc, use a list of strings...

searchstrings = [(1, 'this is search string 1'), ...]

Then you can add and remove new chat windows in your GUI and in this
list, and get dynamic chat windows.

- Josiah

On Fri, Jul 11, 2008 at 9:53 AM, Steve Freedenburg >> <stevefreedenburg@charter.net> wrote:

When I click "start" on my program, a task starts to run. This task
doesn't
stop until the program exits. Maybe a stop button will be better, but at
the momment the task running casues the program to become "Not
Responding"

Is this what wxPython calls a long running task?

And if it is, is the LongRunningTask on wxPyWiki where i need to be
looking?

Lastly I'm going to post my code for the task. Could you critique it for
me. I'm not sure it works because the windows "not responding" is more
important. The code below could
be the most Pythonic and dynamic code ever written for it's purpose, but
the
GUI can't handle it, it doesn't matter. But here it is anyhow.

<CODE>
def LogFileSearch(self):
     global LogDirValue # Globals
     global LineCountFirst
     global C1SearchString # The Search strings contain whatever
the user wants to search for.
     global C2SearchString
     global C3SearchString
     global C4SearchString
     global C5SearchString
     global C6SearchString

     LineCountFirst = len(open(LogDirValue, 'r').readlines()) # Get the
line count of log.
     print LineCountFirst # for debugging, print length of the log (total
lines)
     FirstReadLine = linecache.getline(LogDirValue, LineCountFirst) #
Defines the last line of the log as FirstReadLine variable.
     if FirstReadLine.find(C1SearchString) >= 0: # If match in search
string = true do stuff, or skip to next elif.
         Text1 = FirstReadLine
         Publisher().sendMessage(('container1', 'container1message'),
[Text1]) # pub sub to sent the line to it's respective frame with
textctrl
widget.
     else:
         print 'no match on 1'

     if FirstReadLine.find(C2SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text2 = FirstReadLine
         Publisher().sendMessage(('container2', 'container2message'),
[Text2])
     else:
         print 'no match on 2'

     if FirstReadLine.find(C3SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text3 = FirstReadLine
         Publisher().sendMessage(('container3', 'container3message'),
[Text3])
     else:
         print 'no match on 3'

     if FirstReadLine.find(C4SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text4 = FirstReadLine
         Publisher().sendMessage(('container4', 'container4message'),
[Text4])
     else:
        print 'no match on 4'

     if FirstReadLine.find(C5SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text5 = FirstReadLine
         Publisher().sendMessage(('container5', 'container5message'),
[Text5])
     else:
         print 'no match on 5'

     if FirstReadLine.find(C6SearchString) >= 0: # If match in search
string = true do stuff, or skip to else.
         Text6 = FirstReadLine
         Publisher().sendMessage(('container6', 'container6message'),
[Text6])
     else:
         print 'no match on 6'

     linecache.clearcache() #clear the linecache

     self.LogFileSearchLineCounter() #now go to the line counter to
check
for new lines

def LogFileSearchLineCounter(self):
     global LineCountFirst
     global LogDirValue
     LineCountLast = len(open(LogDirValue).readlines()) # get the count
of
log file again. # read lines again
     if LineCountFirst < LineCountLast: # compare LineCountFirst with
LineCountLast if < go back to LogFileSearch # compare the 2 numbers
         self.LogFileSearch() # if last is larger got back to
LogFileSearch
     elif LineCountLast == LineCountFirst: # if the
the
numbers are ==
         print 'no new lines yet...' # wait one second
         time.sleep(1) # then redo LogFileSearchLineCounter
         self.LogFileSearchLineCounter()
_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

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

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

I'm feeling a little scatter brained here... Yesterday I was feeling victorious... today I'm bummed out, because
now I feel lost again. If you're feeling generous I'm game for a spoiler. How would I use a wx.Timer this way?

Where would I put the wx.Timer? Either way it's creating a loop correct?

Click "start" in the GUI and the log gets processed and repeats every second... how does using wx.Timer
differ? It still creates a loop, that would never end. Wouldn't that still cause the GUI to stop responding?

Or does clicking "start" while using a wx.Timer somewhere in the event handler (I have no idea where actually,)
trigger the log processing to occur, than revert back to the GUI waiting for an event to take place (invoke the
wx.Timer somehow like I said) and the event happens again? And doing something like that will cause the
program to be happy and still able to stop it via another event handle?

The GUI functions 100%... bloated or not, which I may spend some time with in the future to refine / condense it. The log processing is questionable.... Just the python code I wrote without a GUI by using the print command works...

The GUI is complex in some aspects. The largest part is the user config dialog. The logpath is definable.
Each container for "sorted log entries" is a miniframe with a textctrl. The user can define it's label, foreground color, background, color, and font. There are 6 of these, for the major type of log entries in the game.

So the logpath, and all the users preferences for the containers are saved and pickled in a file.

If the file doesn't exist when the user first starts the program I predefined all the varables in the beginning of the
program. So there won't be any errrors thrown out. If the log does exist (the user created it) than when the program loads, it checks, and automatically loads the users preferences. The config dialog also gets the
current settings, whether they are the ones I preset, or the users so the user doesn't have to re-enter them all
in.

So let's say the user invokes the config dialog, sets everything up for the first time. He saves it... uses the program (if i get the timer / thread / pubsub / 35 lines of code to 6 sorted out) and finds out his foreground
color on Container 3 looks bad with the background color. He can go back into the config dialog and only has
to change one thing...

Another thing I did was create a "load" menu item, so the user can reload the data on the fly without having to
restart the program. So in the middle of using it, the user can make adjustments, and then simply reload them.

The containers are accessed by menu items that are checkitems. The user clicks the menuitem, the frame shows... the user clicks it again, the frame hides.

So the objective was to make the container for text, be able to hold any information the user wants it to hold.
The label could be "Chocolate instances:" and the search string could be "chocolate" and any log entry that had "chocolate" anywhere in the line would show up in that container.

Throw in an "about" and "help" dialog to try and be user friendly

···

----- Original Message ----- From: "Josiah Carlson" <josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 15:29
Subject: Re: [wxpython-users] Am I on the right path?

I would put money on your GUI code being bloated, probably due to
duplication similar to what I showed earlier. But I would point out
one major thing; you are new to wxPython programming, and probably
programming in Python, and even likely programming generally. As long
as you learn good habits, your code can get better.

Regarding whether or not to use delayedresult...your game produces
output in a log file. You are currently checking the content of the
file to see if it has new lines, and if so, processing them. Rather
than using an entire thread to check to see if there is new log
output, you can just use a wx.Timer(). wx.Timer() is designed to fire
an event after a specified delay...like every 1 second. If you get an
event every second or so, and upon receiving that event, spend some
time processing the log file from the last place you processed it,
which outputs information to the different windows. I'm sure it would
work just fine.

- Josiah

On Fri, Jul 11, 2008 at 10:43 AM, Steve Freedenburg > <stevefreedenburg@charter.net> wrote:

Alright Mr. Carlson,

Here is my next question then. Woud the wx.lib.delayedresult accomplish
what I'm looking for?
The "long running task" is the only thing this program really needs to
accomplish. And once it's
started, it is 99% of what needs to be happening. The other frames to
recieve messages are fluff.
By flluff I mean, a nice way to show a result with pretty colors and fonts.

The comments ARE ugly, and some were even added to the e-mail to try and
explain things better.
When I looked at the code in the e-mail it confused me, and I wrote it, so I
commented more.

I'm really floored... from 35 lines to 6... that's amazing.... I don't
quite understand your 6 lines of
code, compared to my 35. but I'm sure I can muddle my way through it.

So basically what you saying is, I have a GUI consisting of over 1000 lines
of code, to execute a task
that at best (your code) is 6 lines, and worst (my code) is 35. I wonder if
my GUI is overly bloated code
as well... The globals only need to be assigned if they are being
changed.... Ok, well that is probably
a few dozen likes that can be removed. I'm depressed now ;P...

----- Original Message ----- From: "Josiah Carlson"
<josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 13:22
Subject: Re: [wxpython-users] Am I on the right path?

Yes, this is what a LongRunningTask is.

You asked for critique, so here's a bit of critique :wink: .

Unless you are writing to a global variable, you don't need to declare
it as global. You can read globals any time.

You don't need to keep calling Publisher(), it's a singleton, it
always returns itself. Just use Publisher.sendMessage(). If you end
up running it in a secondary thread (what LongRunningTask will give
you the option to do), you can use wx.CallAfter(Publisher.sendMessage,
...) to get it executing in the main thread again. You can also use a
wx.Timer() to schedule calls to check lines.

Your message names, ('container2', 'container2message') seem pretty
redundant. ('container2', 'message') would be sufficient. Then
again, I would personally use 'container.2.message' (you can use
dotted strings instead of tuples of strings to get the same semantics
without all of the parens, quotes, and commas), as it splits types,
then which object of that type, then what functionality of that
particular object. Brevity + clarity = win.

Describing the functionality of every line with a comment at the end
of the line that is doing something is really ugly, plus long lines
can be difficult to read. Don't get me wrong, I've done it myself,
but if I am to comment, I comment the line just before. Speaking of
comments, you've got the same comment and code 6 times. Don't copy
and paste!

for i in xrange(1,7):
  if FirstReadLine.find(globals()['C%iSearchString'%i]):
      # If the line has been matched, send it off to the proper container
      Publisher.sendMessage('container.%i.message'%i, [FirstReadLine])
  else:
      print "no match on %i"%i

Those 6 lines will replace 35 lines of your original code (including
blank lines).

If you really want to save yourself some time, don't use named search
strings, etc, use a list of strings...

searchstrings = [(1, 'this is search string 1'), ...]

Then you can add and remove new chat windows in your GUI and in this
list, and get dynamic chat windows.

- Josiah

On Fri, Jul 11, 2008 at 9:53 AM, Steve Freedenburg >>> <stevefreedenburg@charter.net> wrote:

When I click "start" on my program, a task starts to run. This task
doesn't
stop until the program exits. Maybe a stop button will be better, but at
the momment the task running casues the program to become "Not
Responding"

Is this what wxPython calls a long running task?

And if it is, is the LongRunningTask on wxPyWiki where i need to be
looking?

Lastly I'm going to post my code for the task. Could you critique it for
me. I'm not sure it works because the windows "not responding" is more
important. The code below could
be the most Pythonic and dynamic code ever written for it's purpose, but
the
GUI can't handle it, it doesn't matter. But here it is anyhow.

<CODE>
def LogFileSearch(self):
     global LogDirValue # Globals
     global LineCountFirst
     global C1SearchString # The Search strings contain whatever
the user wants to search for.
     global C2SearchString
     global C3SearchString
     global C4SearchString
     global C5SearchString
     global C6SearchString

     LineCountFirst = len(open(LogDirValue, 'r').readlines()) # Get the
line count of log.
     print LineCountFirst # for debugging, print length of the log (total
lines)
     FirstReadLine = linecache.getline(LogDirValue, LineCountFirst) #
Defines the last line of the log as FirstReadLine variable.
     if FirstReadLine.find(C1SearchString) >= 0: # If match in search
string = true do stuff, or skip to next elif.
         Text1 = FirstReadLine
         Publisher().sendMessage(('container1', 'container1message'),
[Text1]) # pub sub to sent the line to it's respective frame with
textctrl
widget.
     else:
         print 'no match on 1'

     if FirstReadLine.find(C2SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text2 = FirstReadLine
         Publisher().sendMessage(('container2', 'container2message'),
[Text2])
     else:
         print 'no match on 2'

     if FirstReadLine.find(C3SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text3 = FirstReadLine
         Publisher().sendMessage(('container3', 'container3message'),
[Text3])
     else:
         print 'no match on 3'

     if FirstReadLine.find(C4SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text4 = FirstReadLine
         Publisher().sendMessage(('container4', 'container4message'),
[Text4])
     else:
        print 'no match on 4'

     if FirstReadLine.find(C5SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
         Text5 = FirstReadLine
         Publisher().sendMessage(('container5', 'container5message'),
[Text5])
     else:
         print 'no match on 5'

     if FirstReadLine.find(C6SearchString) >= 0: # If match in search
string = true do stuff, or skip to else.
         Text6 = FirstReadLine
         Publisher().sendMessage(('container6', 'container6message'),
[Text6])
     else:
         print 'no match on 6'

     linecache.clearcache() #clear the linecache

     self.LogFileSearchLineCounter() #now go to the line counter to
check
for new lines

def LogFileSearchLineCounter(self):
     global LineCountFirst
     global LogDirValue
     LineCountLast = len(open(LogDirValue).readlines()) # get the count
of
log file again. # read lines again
     if LineCountFirst < LineCountLast: # compare LineCountFirst with
LineCountLast if < go back to LogFileSearch # compare the 2 numbers
         self.LogFileSearch() # if last is larger got back to
LogFileSearch
     elif LineCountLast == LineCountFirst: # if the
the
numbers are ==
         print 'no new lines yet...' # wait one second
         time.sleep(1) # then redo LogFileSearchLineCounter
         self.LogFileSearchLineCounter()
_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

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

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

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

Steve Freedenburg wrote:

now I feel lost again. If you're feeling generous I'm game for a spoiler. How would I use a wx.Timer this way?

Where would I put the wx.Timer? Either way it's creating a loop correct?

a wx.Timer is integrated with the ww main loop, so it doesn't block the GUI at all -- this kind of thing is what it is for.

Take a look in the Wiki, the Demo and this list for wx.Timer samples.

The GUI is complex in some aspects. The largest part is the user config dialog. The logpath is definable.
Each container for "sorted log entries" is a miniframe with a textctrl. The user can define it's label, foreground color, background, color, and font. There are 6 of these, for the major type of log entries in the game.

yup, all that is going to take a lot of code!

Good luck,

-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

That it works is something that you should be proud of. However, you
asked about code quality, and some of us get paid to write software
for a living, so...

wxTimer would be creating a loop, but only to handle the most recent
lines. After it handles the most recent lines, the loop dies, and
wxPython continues handling everything it was.

Your current GUI seems to keep re-calling the "search" function, and
if it doesn't have information, it uses time.sleep(1) to wait to see
if there is more information. The difference with using a wx.Timer()
is rather than doing a time.sleep(), you would do nothing. Literally.
You return from the function.

For example...

import os
import wx
from wx.lib.pubsub import Publisher

class myFrame(wx.Frame):
    def __init__(self, ...):
        ...

        self.timer = wx.Timer(self, id=wx.NewId())
        self.Bind(wx.EVT_TIMER, self.SearchMore, self.timer)
        self.timer.Start(1000, oneShot=0)
        self.lastpos = os.stat(LOGFILENAME).st_size

    def SearchMore(self, evt):
        fil = open(LOGFILENAME, 'rb')
        fil.seek(self.lastpos)
        for line in fil:
            if len(line) == len(line.rstrip('\r\n')):
                #we got a partial line, we'll wait until the next pass...
                return
            self.lastpos += len(line)

            for i in xrange(1,7):
                if FirstReadLine.find(globals()['C%iSearchString'%i]):
                    # If the line has been matched, send it off to the
proper container
                    Publisher.sendMessage('container.%i.message'%i,
[FirstReadLine])
            else:
                print "no match on %i"%i

The above will only start scanning from the end of the current log
file, but that should be ok. If you were really ambitious, you could
have it then scan backwards through the file and fill in logs over
time.

As for spit and polish stuff...I'll not comment.

- Josiah

···

On Fri, Jul 11, 2008 at 1:18 PM, Steve Freedenburg <stevefreedenburg@charter.net> wrote:

I'm feeling a little scatter brained here... Yesterday I was feeling
victorious... today I'm bummed out, because
now I feel lost again. If you're feeling generous I'm game for a spoiler.
How would I use a wx.Timer this way?

Where would I put the wx.Timer? Either way it's creating a loop correct?

Click "start" in the GUI and the log gets processed and repeats every
second... how does using wx.Timer
differ? It still creates a loop, that would never end. Wouldn't that still
cause the GUI to stop responding?

Or does clicking "start" while using a wx.Timer somewhere in the event
handler (I have no idea where actually,)
trigger the log processing to occur, than revert back to the GUI waiting for
an event to take place (invoke the
wx.Timer somehow like I said) and the event happens again? And doing
something like that will cause the
program to be happy and still able to stop it via another event handle?

The GUI functions 100%... bloated or not, which I may spend some time with
in the future to refine / condense it. The log processing is
questionable.... Just the python code I wrote without a GUI by using the
print command works...

The GUI is complex in some aspects. The largest part is the user config
dialog. The logpath is definable.
Each container for "sorted log entries" is a miniframe with a textctrl. The
user can define it's label, foreground color, background, color, and font.
There are 6 of these, for the major type of log entries in the game.

So the logpath, and all the users preferences for the containers are saved
and pickled in a file.

If the file doesn't exist when the user first starts the program I
predefined all the varables in the beginning of the
program. So there won't be any errrors thrown out. If the log does exist
(the user created it) than when the program loads, it checks, and
automatically loads the users preferences. The config dialog also gets the
current settings, whether they are the ones I preset, or the users so the
user doesn't have to re-enter them all
in.

So let's say the user invokes the config dialog, sets everything up for the
first time. He saves it... uses the program (if i get the timer / thread /
pubsub / 35 lines of code to 6 sorted out) and finds out his foreground
color on Container 3 looks bad with the background color. He can go back
into the config dialog and only has
to change one thing...

Another thing I did was create a "load" menu item, so the user can reload
the data on the fly without having to
restart the program. So in the middle of using it, the user can make
adjustments, and then simply reload them.

The containers are accessed by menu items that are checkitems. The user
clicks the menuitem, the frame shows... the user clicks it again, the frame
hides.

So the objective was to make the container for text, be able to hold any
information the user wants it to hold.
The label could be "Chocolate instances:" and the search string could be
"chocolate" and any log entry that had "chocolate" anywhere in the line
would show up in that container.

Throw in an "about" and "help" dialog to try and be user friendly

----- Original Message ----- From: "Josiah Carlson"
<josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 15:29
Subject: Re: [wxpython-users] Am I on the right path?

I would put money on your GUI code being bloated, probably due to
duplication similar to what I showed earlier. But I would point out
one major thing; you are new to wxPython programming, and probably
programming in Python, and even likely programming generally. As long
as you learn good habits, your code can get better.

Regarding whether or not to use delayedresult...your game produces
output in a log file. You are currently checking the content of the
file to see if it has new lines, and if so, processing them. Rather
than using an entire thread to check to see if there is new log
output, you can just use a wx.Timer(). wx.Timer() is designed to fire
an event after a specified delay...like every 1 second. If you get an
event every second or so, and upon receiving that event, spend some
time processing the log file from the last place you processed it,
which outputs information to the different windows. I'm sure it would
work just fine.

- Josiah

On Fri, Jul 11, 2008 at 10:43 AM, Steve Freedenburg >> <stevefreedenburg@charter.net> wrote:

Alright Mr. Carlson,

Here is my next question then. Woud the wx.lib.delayedresult accomplish
what I'm looking for?
The "long running task" is the only thing this program really needs to
accomplish. And once it's
started, it is 99% of what needs to be happening. The other frames to
recieve messages are fluff.
By flluff I mean, a nice way to show a result with pretty colors and
fonts.

The comments ARE ugly, and some were even added to the e-mail to try and
explain things better.
When I looked at the code in the e-mail it confused me, and I wrote it,
so I
commented more.

I'm really floored... from 35 lines to 6... that's amazing.... I don't
quite understand your 6 lines of
code, compared to my 35. but I'm sure I can muddle my way through it.

So basically what you saying is, I have a GUI consisting of over 1000
lines
of code, to execute a task
that at best (your code) is 6 lines, and worst (my code) is 35. I wonder
if
my GUI is overly bloated code
as well... The globals only need to be assigned if they are being
changed.... Ok, well that is probably
a few dozen likes that can be removed. I'm depressed now ;P...

----- Original Message ----- From: "Josiah Carlson"
<josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 13:22
Subject: Re: [wxpython-users] Am I on the right path?

Yes, this is what a LongRunningTask is.

You asked for critique, so here's a bit of critique :wink: .

Unless you are writing to a global variable, you don't need to declare
it as global. You can read globals any time.

You don't need to keep calling Publisher(), it's a singleton, it
always returns itself. Just use Publisher.sendMessage(). If you end
up running it in a secondary thread (what LongRunningTask will give
you the option to do), you can use wx.CallAfter(Publisher.sendMessage,
...) to get it executing in the main thread again. You can also use a
wx.Timer() to schedule calls to check lines.

Your message names, ('container2', 'container2message') seem pretty
redundant. ('container2', 'message') would be sufficient. Then
again, I would personally use 'container.2.message' (you can use
dotted strings instead of tuples of strings to get the same semantics
without all of the parens, quotes, and commas), as it splits types,
then which object of that type, then what functionality of that
particular object. Brevity + clarity = win.

Describing the functionality of every line with a comment at the end
of the line that is doing something is really ugly, plus long lines
can be difficult to read. Don't get me wrong, I've done it myself,
but if I am to comment, I comment the line just before. Speaking of
comments, you've got the same comment and code 6 times. Don't copy
and paste!

for i in xrange(1,7):
if FirstReadLine.find(globals()['C%iSearchString'%i]):
     # If the line has been matched, send it off to the proper container
     Publisher.sendMessage('container.%i.message'%i, [FirstReadLine])
else:
     print "no match on %i"%i

Those 6 lines will replace 35 lines of your original code (including
blank lines).

If you really want to save yourself some time, don't use named search
strings, etc, use a list of strings...

searchstrings = [(1, 'this is search string 1'), ...]

Then you can add and remove new chat windows in your GUI and in this
list, and get dynamic chat windows.

- Josiah

On Fri, Jul 11, 2008 at 9:53 AM, Steve Freedenburg >>>> <stevefreedenburg@charter.net> wrote:

When I click "start" on my program, a task starts to run. This task
doesn't
stop until the program exits. Maybe a stop button will be better, but
at
the momment the task running casues the program to become "Not
Responding"

Is this what wxPython calls a long running task?

And if it is, is the LongRunningTask on wxPyWiki where i need to be
looking?

Lastly I'm going to post my code for the task. Could you critique it
for
me. I'm not sure it works because the windows "not responding" is more
important. The code below could
be the most Pythonic and dynamic code ever written for it's purpose,
but
the
GUI can't handle it, it doesn't matter. But here it is anyhow.

<CODE>
def LogFileSearch(self):
    global LogDirValue # Globals
    global LineCountFirst
    global C1SearchString # The Search strings contain whatever
the user wants to search for.
    global C2SearchString
    global C3SearchString
    global C4SearchString
    global C5SearchString
    global C6SearchString

    LineCountFirst = len(open(LogDirValue, 'r').readlines()) # Get the
line count of log.
    print LineCountFirst # for debugging, print length of the log
(total
lines)
    FirstReadLine = linecache.getline(LogDirValue, LineCountFirst) #
Defines the last line of the log as FirstReadLine variable.
    if FirstReadLine.find(C1SearchString) >= 0: # If match in search
string = true do stuff, or skip to next elif.
        Text1 = FirstReadLine
        Publisher().sendMessage(('container1', 'container1message'),
[Text1]) # pub sub to sent the line to it's respective frame with
textctrl
widget.
    else:
        print 'no match on 1'

    if FirstReadLine.find(C2SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text2 = FirstReadLine
        Publisher().sendMessage(('container2', 'container2message'),
[Text2])
    else:
        print 'no match on 2'

    if FirstReadLine.find(C3SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text3 = FirstReadLine
        Publisher().sendMessage(('container3', 'container3message'),
[Text3])
    else:
        print 'no match on 3'

    if FirstReadLine.find(C4SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text4 = FirstReadLine
        Publisher().sendMessage(('container4', 'container4message'),
[Text4])
    else:
       print 'no match on 4'

    if FirstReadLine.find(C5SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text5 = FirstReadLine
        Publisher().sendMessage(('container5', 'container5message'),
[Text5])
    else:
        print 'no match on 5'

    if FirstReadLine.find(C6SearchString) >= 0: # If match in search
string = true do stuff, or skip to else.
        Text6 = FirstReadLine
        Publisher().sendMessage(('container6', 'container6message'),
[Text6])
    else:
        print 'no match on 6'

    linecache.clearcache() #clear the linecache

    self.LogFileSearchLineCounter() #now go to the line counter to
check
for new lines

def LogFileSearchLineCounter(self):
    global LineCountFirst
    global LogDirValue
    LineCountLast = len(open(LogDirValue).readlines()) # get the count
of
log file again. # read lines again
    if LineCountFirst < LineCountLast: # compare LineCountFirst with
LineCountLast if < go back to LogFileSearch # compare the 2 numbers
        self.LogFileSearch() # if last is larger got back to
LogFileSearch
    elif LineCountLast == LineCountFirst: # if the
the
numbers are ==
        print 'no new lines yet...' # wait one second
        time.sleep(1) # then redo LogFileSearchLineCounter
        self.LogFileSearchLineCounter()
_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

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

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

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

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

Steve,

I'm feeling a little scatter brained here... Yesterday I was feeling victorious... today I'm bummed out, because
now I feel lost again. If you're feeling generous I'm game for a spoiler. How would I use a wx.Timer this way?

GUI programming can be a pain. I've had my fair share of ups and downs as I get some truly great parts working smoothly and then I hit a "major" snag that take me a couple of days to figure out. Then once I know the answer, I wonder why I had a problem in the first place. I think that's just the nature of programming (and learning something new) in general.

I usually create my Timers in my __init__, but if a timer should only run when you have a specific dialog up, start it then instead. Here's some code to get you going:

<code>

# note that 1000 = about 1 second
self.pollingPeriod = 1000
# initialize a timer object
self.timer = wx.Timer(self)
# start the timer
self.timer.Start(self.pollingPeriod)
# bind the timer event.
self.Bind(wx.EVT_TIMER, self.updateLog, self.timer)

</code>

So, in the "self.updateLog" event handler, you would read the log and update your UI as needed every second. One thing to keep in mind is that when you go to shut down your frame or application or whatever, be sure to call "self.timer.Stop()" to stop your timer instance. Otherwise you might get some goofy errors.

Where would I put the wx.Timer? Either way it's creating a loop correct?

As Chris pointed out, it does indeed create a loop. But it's within the wx framework, so it doesn't block the UI,

I hope that helps some.

···

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org
Python Extension Building Network: http://www.pythonlibrary.org

raffaello wrote:

I know.
But if I have three buttons (in fact, in some of my applications they are much more) and I want to refer to each of them I need three separate references, and to call a control through a variable is much faster than FindWindowByName or FindWindowById. And if to declare a variable referring to a button I have to declare a full-fledged button, I can say goodbye to any idea of iteration.
As I said before, I miss the possibility, which exists in C++, to simply declare a variable by type, as an empty object, and to fill it afterwards with the desired properties using that variable as a pointer.
Is there anything alike in Python?

You can definitely do what you want, but respectfully you need to bone up on your python.

Try this:

import wx

app = wx.App()

frm = wx.Frame(None, -1)

buttons =

def onHit(evt):
   print "Button pressed:", evt.GetEventObject()
   for i, b in enumerate(buttons):
     print "%s: %s" % (i, b)

for i in range(3):
   but = wx.Button(frm, -1, "Button %s" % i, pos=(20, 20*i))
   but.Bind(wx.EVT_BUTTON, onHit)
   buttons.append(but)

frm.Show()
app.MainLoop()

raffaello wrote:

Find here below the simplest code I could think of, no sizers, no event-handlers, just three buttons. Alas, it does not work.

import wx

class MyFrame(wx.Frame):
       btFirst = None
    bt2nd = None
    bt3rd = None
    dButton ={ btFirst: None, bt2nd: None, bt3rd: None}

    def __init__(self):
        wx.Frame.__init__(self, None, -1, "My Frame", size=(300, 300))
        panel = wx.Panel(self, -1)
        keys = self.dButton.keys()
        pt = wx.Point(20, 10)
        labels = 'First', 'Second', 'Third'
        for i in range(len(keys)):
            bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))
            self.dButton[keys[i]] = bt
            print bt.Position.y
                   print self.dButton[self.bt2nd].LabelText
        print 'len(keys): ', len(keys)
       
if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show(True)
    app.MainLoop()

Try it, and you will see that the dictionary (or even a list for that) receives not the references-to-be for the buttons, but the reference's value, in my case None. In fact, the length of dButton is 1, which makes an iteration impossible.
In C++ it is possible to specify if your variable is a reference (pointer) or a value. In Python it doesn't seem to be so.

No, the storage models are quite different. In Python, names are just references to objects. You can't have a name that holds a reference to another name. That's not possible. You can certainly have two names that refer to the same object, but that's quite different. For example:

    one = [1,2,3]
    two = one
At this point, both "one" and "two" are bound to the same list, a list with three elements.
    one = [4,5,6,7]
All this does is make the name "one" bind to a new list with four elements. "two" is still bound to [1,2,3]. Thus, setting "two = one" does not inextricably bind those two names together. Instead, it just makes "two" point to whatever object "one" was pointing to at that point in time.

Now, some operations affect the object itself. For example:
    one = [1,2,3]
    two = one
Again, one list, two references. Let's say I modify the list:
    one.append( 4 )
That doesn't create a new list. It modifies the existing list in place. So, now we have a 4-element list, but still with 2 names bound to it: "one" and "two". So both of these will print 4:
    print len( one )
    print len( two )

This same issue causes another problem in your code. You did this:
        keys = self.dButton.keys()
then you modified self.dButton, then you tried to access "keys". The keys() method creates a new list from the dictionary's keys at that point in time. Here, that list is bound to "keys". When you change self.dButton, that list is not changed, so your saved "keys" list is now out of date.

If you want to hold references to your three buttons, just skip the btFirst/dButton stuff, and do this:

class MyFrame( wx.Frame ):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "My Frame", size=(300, 300))
        panel = wx.Panel(self, -1)
        pt = wx.Point(20, 10)
        labels = 'First', 'Second', 'Third'
        self.dButton = # note list, not dictionary
        for i in range(len(keys)):
            bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))
            self.dButton.append( bt )
            print bt.Position.y
                   print self.dButton[1].LabelText
        print 'len(buttons): ', len(self.dButton)

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Oh I agree with you, I'm not even amatuer... By trade I'm an electronics technician. And there is a great
deal of satisfaction in broadening my knowledge.

But the methodology and phrases are something that elude me (for now.)
I look on at an example, and try and use that structure for what I'm trying to accomplish, if it works
great! Then I want to accomplish something else and look at another example from another source
often times I've found that those two examples will not mesh well together because of one thing or
another. But they are so in the same family, but because of how programmer-A wrote his program
his way (whether it's ideal or not is irrelevant) the program from programmer-B won't work because
he wrote his a diferent way.

It seems to me that Python is SUPER flexible. Maybe even Mr. Fantastic flexible... OR even
flexible on a GALACTIC scale. And for the newbie, that can be hard to deal with. You can accomplish
the same thing many, many diferent ways.

For example. You can create a widget all in one line, because that widget will let you set all it's flags in one go.
or you can create a widget, and line after line define other aspects of it.

You guys are a great resource, and are a benefit to newbies like me. Thanks! I hope I really do LEARN
this language. It's fun! As far as doing it professionally, maybe only in addition to my current trade.

Programming microcontrollers in assembly (Micro Chip's 16F628) and PicBasic (Parallax's Basic Stamp II)
are something I enjoy. Assembly, and PicBasic aren't the same animal as Python. When we're talking 1-2kb
or program memory you HAVE to be mission oriented. I had to make a BasicStamp send serial data to a
LED Matrix for a game I needed to test. Basically make a bench test box for the display. It used several shift registers, and diferent enable lines to get the output to "light up" the display. That was fun project! But I couldn't do it in Python. The assembly I've done was to re-create a project I saw on the web where a guy
had installed a sensor on either end of a staircase in his house, and when the beam was broken it lit up the stairs
step by step with the direction of travel.

So what I do is just for kicks, and to see if I can do it, and maybe learn something in the process. I admire you
all for your abilities. With years of practice, maybe I can answer some bodies questions here.

Steve

···

----- Original Message ----- From: "Josiah Carlson" <josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 17:06
Subject: Re: [wxpython-users] Am I on the right path?

That it works is something that you should be proud of. However, you
asked about code quality, and some of us get paid to write software
for a living, so...

wxTimer would be creating a loop, but only to handle the most recent
lines. After it handles the most recent lines, the loop dies, and
wxPython continues handling everything it was.

Your current GUI seems to keep re-calling the "search" function, and
if it doesn't have information, it uses time.sleep(1) to wait to see
if there is more information. The difference with using a wx.Timer()
is rather than doing a time.sleep(), you would do nothing. Literally.
You return from the function.

For example...

import os
import wx
from wx.lib.pubsub import Publisher

class myFrame(wx.Frame):
   def __init__(self, ...):
       ...

       self.timer = wx.Timer(self, id=wx.NewId())
       self.Bind(wx.EVT_TIMER, self.SearchMore, self.timer)
       self.timer.Start(1000, oneShot=0)
       self.lastpos = os.stat(LOGFILENAME).st_size

   def SearchMore(self, evt):
       fil = open(LOGFILENAME, 'rb')
       fil.seek(self.lastpos)
       for line in fil:
           if len(line) == len(line.rstrip('\r\n')):
               #we got a partial line, we'll wait until the next pass...
               return
           self.lastpos += len(line)

           for i in xrange(1,7):
               if FirstReadLine.find(globals()['C%iSearchString'%i]):
                   # If the line has been matched, send it off to the
proper container
                   Publisher.sendMessage('container.%i.message'%i,
[FirstReadLine])
           else:
               print "no match on %i"%i

The above will only start scanning from the end of the current log
file, but that should be ok. If you were really ambitious, you could
have it then scan backwards through the file and fill in logs over
time.

As for spit and polish stuff...I'll not comment.

- Josiah

On Fri, Jul 11, 2008 at 1:18 PM, Steve Freedenburg > <stevefreedenburg@charter.net> wrote:

I'm feeling a little scatter brained here... Yesterday I was feeling
victorious... today I'm bummed out, because
now I feel lost again. If you're feeling generous I'm game for a spoiler.
How would I use a wx.Timer this way?

Where would I put the wx.Timer? Either way it's creating a loop correct?

Click "start" in the GUI and the log gets processed and repeats every
second... how does using wx.Timer
differ? It still creates a loop, that would never end. Wouldn't that still
cause the GUI to stop responding?

Or does clicking "start" while using a wx.Timer somewhere in the event
handler (I have no idea where actually,)
trigger the log processing to occur, than revert back to the GUI waiting for
an event to take place (invoke the
wx.Timer somehow like I said) and the event happens again? And doing
something like that will cause the
program to be happy and still able to stop it via another event handle?

The GUI functions 100%... bloated or not, which I may spend some time with
in the future to refine / condense it. The log processing is
questionable.... Just the python code I wrote without a GUI by using the
print command works...

The GUI is complex in some aspects. The largest part is the user config
dialog. The logpath is definable.
Each container for "sorted log entries" is a miniframe with a textctrl. The
user can define it's label, foreground color, background, color, and font.
There are 6 of these, for the major type of log entries in the game.

So the logpath, and all the users preferences for the containers are saved
and pickled in a file.

If the file doesn't exist when the user first starts the program I
predefined all the varables in the beginning of the
program. So there won't be any errrors thrown out. If the log does exist
(the user created it) than when the program loads, it checks, and
automatically loads the users preferences. The config dialog also gets the
current settings, whether they are the ones I preset, or the users so the
user doesn't have to re-enter them all
in.

So let's say the user invokes the config dialog, sets everything up for the
first time. He saves it... uses the program (if i get the timer / thread /
pubsub / 35 lines of code to 6 sorted out) and finds out his foreground
color on Container 3 looks bad with the background color. He can go back
into the config dialog and only has
to change one thing...

Another thing I did was create a "load" menu item, so the user can reload
the data on the fly without having to
restart the program. So in the middle of using it, the user can make
adjustments, and then simply reload them.

The containers are accessed by menu items that are checkitems. The user
clicks the menuitem, the frame shows... the user clicks it again, the frame
hides.

So the objective was to make the container for text, be able to hold any
information the user wants it to hold.
The label could be "Chocolate instances:" and the search string could be
"chocolate" and any log entry that had "chocolate" anywhere in the line
would show up in that container.

Throw in an "about" and "help" dialog to try and be user friendly

----- Original Message ----- From: "Josiah Carlson"
<josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 15:29
Subject: Re: [wxpython-users] Am I on the right path?

I would put money on your GUI code being bloated, probably due to
duplication similar to what I showed earlier. But I would point out
one major thing; you are new to wxPython programming, and probably
programming in Python, and even likely programming generally. As long
as you learn good habits, your code can get better.

Regarding whether or not to use delayedresult...your game produces
output in a log file. You are currently checking the content of the
file to see if it has new lines, and if so, processing them. Rather
than using an entire thread to check to see if there is new log
output, you can just use a wx.Timer(). wx.Timer() is designed to fire
an event after a specified delay...like every 1 second. If you get an
event every second or so, and upon receiving that event, spend some
time processing the log file from the last place you processed it,
which outputs information to the different windows. I'm sure it would
work just fine.

- Josiah

On Fri, Jul 11, 2008 at 10:43 AM, Steve Freedenburg >>> <stevefreedenburg@charter.net> wrote:

Alright Mr. Carlson,

Here is my next question then. Woud the wx.lib.delayedresult accomplish
what I'm looking for?
The "long running task" is the only thing this program really needs to
accomplish. And once it's
started, it is 99% of what needs to be happening. The other frames to
recieve messages are fluff.
By flluff I mean, a nice way to show a result with pretty colors and
fonts.

The comments ARE ugly, and some were even added to the e-mail to try and
explain things better.
When I looked at the code in the e-mail it confused me, and I wrote it,
so I
commented more.

I'm really floored... from 35 lines to 6... that's amazing.... I don't
quite understand your 6 lines of
code, compared to my 35. but I'm sure I can muddle my way through it.

So basically what you saying is, I have a GUI consisting of over 1000
lines
of code, to execute a task
that at best (your code) is 6 lines, and worst (my code) is 35. I wonder
if
my GUI is overly bloated code
as well... The globals only need to be assigned if they are being
changed.... Ok, well that is probably
a few dozen likes that can be removed. I'm depressed now ;P...

----- Original Message ----- From: "Josiah Carlson"
<josiah.carlson@gmail.com>
To: <wxpython-users@lists.wxwidgets.org>
Sent: Friday, 11 July, 2008 13:22
Subject: Re: [wxpython-users] Am I on the right path?

Yes, this is what a LongRunningTask is.

You asked for critique, so here's a bit of critique :wink: .

Unless you are writing to a global variable, you don't need to declare
it as global. You can read globals any time.

You don't need to keep calling Publisher(), it's a singleton, it
always returns itself. Just use Publisher.sendMessage(). If you end
up running it in a secondary thread (what LongRunningTask will give
you the option to do), you can use wx.CallAfter(Publisher.sendMessage,
...) to get it executing in the main thread again. You can also use a
wx.Timer() to schedule calls to check lines.

Your message names, ('container2', 'container2message') seem pretty
redundant. ('container2', 'message') would be sufficient. Then
again, I would personally use 'container.2.message' (you can use
dotted strings instead of tuples of strings to get the same semantics
without all of the parens, quotes, and commas), as it splits types,
then which object of that type, then what functionality of that
particular object. Brevity + clarity = win.

Describing the functionality of every line with a comment at the end
of the line that is doing something is really ugly, plus long lines
can be difficult to read. Don't get me wrong, I've done it myself,
but if I am to comment, I comment the line just before. Speaking of
comments, you've got the same comment and code 6 times. Don't copy
and paste!

for i in xrange(1,7):
if FirstReadLine.find(globals()['C%iSearchString'%i]):
     # If the line has been matched, send it off to the proper container
     Publisher.sendMessage('container.%i.message'%i, [FirstReadLine])
else:
     print "no match on %i"%i

Those 6 lines will replace 35 lines of your original code (including
blank lines).

If you really want to save yourself some time, don't use named search
strings, etc, use a list of strings...

searchstrings = [(1, 'this is search string 1'), ...]

Then you can add and remove new chat windows in your GUI and in this
list, and get dynamic chat windows.

- Josiah

On Fri, Jul 11, 2008 at 9:53 AM, Steve Freedenburg >>>>> <stevefreedenburg@charter.net> wrote:

When I click "start" on my program, a task starts to run. This task
doesn't
stop until the program exits. Maybe a stop button will be better, but
at
the momment the task running casues the program to become "Not
Responding"

Is this what wxPython calls a long running task?

And if it is, is the LongRunningTask on wxPyWiki where i need to be
looking?

Lastly I'm going to post my code for the task. Could you critique it
for
me. I'm not sure it works because the windows "not responding" is more
important. The code below could
be the most Pythonic and dynamic code ever written for it's purpose,
but
the
GUI can't handle it, it doesn't matter. But here it is anyhow.

<CODE>
def LogFileSearch(self):
    global LogDirValue # Globals
    global LineCountFirst
    global C1SearchString # The Search strings contain whatever
the user wants to search for.
    global C2SearchString
    global C3SearchString
    global C4SearchString
    global C5SearchString
    global C6SearchString

    LineCountFirst = len(open(LogDirValue, 'r').readlines()) # Get the
line count of log.
    print LineCountFirst # for debugging, print length of the log
(total
lines)
    FirstReadLine = linecache.getline(LogDirValue, LineCountFirst) #
Defines the last line of the log as FirstReadLine variable.
    if FirstReadLine.find(C1SearchString) >= 0: # If match in search
string = true do stuff, or skip to next elif.
        Text1 = FirstReadLine
        Publisher().sendMessage(('container1', 'container1message'),
[Text1]) # pub sub to sent the line to it's respective frame with
textctrl
widget.
    else:
        print 'no match on 1'

    if FirstReadLine.find(C2SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text2 = FirstReadLine
        Publisher().sendMessage(('container2', 'container2message'),
[Text2])
    else:
        print 'no match on 2'

    if FirstReadLine.find(C3SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text3 = FirstReadLine
        Publisher().sendMessage(('container3', 'container3message'),
[Text3])
    else:
        print 'no match on 3'

    if FirstReadLine.find(C4SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text4 = FirstReadLine
        Publisher().sendMessage(('container4', 'container4message'),
[Text4])
    else:
       print 'no match on 4'

    if FirstReadLine.find(C5SearchString) >= 0: # If match in search
string = true do stuff, or skip to next if.
        Text5 = FirstReadLine
        Publisher().sendMessage(('container5', 'container5message'),
[Text5])
    else:
        print 'no match on 5'

    if FirstReadLine.find(C6SearchString) >= 0: # If match in search
string = true do stuff, or skip to else.
        Text6 = FirstReadLine
        Publisher().sendMessage(('container6', 'container6message'),
[Text6])
    else:
        print 'no match on 6'

    linecache.clearcache() #clear the linecache

    self.LogFileSearchLineCounter() #now go to the line counter to
check
for new lines

def LogFileSearchLineCounter(self):
    global LineCountFirst
    global LogDirValue
    LineCountLast = len(open(LogDirValue).readlines()) # get the count
of
log file again. # read lines again
    if LineCountFirst < LineCountLast: # compare LineCountFirst with
LineCountLast if < go back to LogFileSearch # compare the 2 numbers
        self.LogFileSearch() # if last is larger got back to
LogFileSearch
    elif LineCountLast == LineCountFirst: # if the
the
numbers are ==
        print 'no new lines yet...' # wait one second
        time.sleep(1) # then redo LogFileSearchLineCounter
        self.LogFileSearchLineCounter()
_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

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

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

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

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

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

It seems to me that Python is SUPER flexible. Maybe even Mr. Fantastic
flexible... OR even
flexible on a GALACTIC scale. And for the newbie, that can be hard to
deal with. You can accomplish
the same thing many, many diferent ways.

However, Python tries to provide one "obvious" way to do
things. Usually "simple is better" and "explicit is better
than implicit" apply.

For example. You can create a widget all in one line, because that
widget will let you set all it's flags in one go.
or you can create a widget, and line after line define other aspects of it.

That will depend on which looks (as in readibility) to you
and also whether you already know all the conditions for how
to create the widget.

Karsten

···

On Fri, Jul 11, 2008 at 09:51:36PM -0400, Steve Freedenburg wrote:
--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346

Countermand.
For whom may be interested, I found a solution,
starting from the consideration that the only objects in Python that
can be declared without assigning them at the same time a value are,
AFAIK, lists and dictionaries.

Here below is my code, tested and working:

import wx

class MyFrame(wx.Frame):

buttons =

Buttons map:

‘First’, ‘Second’, ‘Third’

def __init__(self):

    wx.Frame.__init__(self, None, -1, "My Frame", size=(300, 300))
    panel = wx.Panel(self, -1)
    pt = wx.Point(20, 10)
    labels = 'First', 'Second', 'Third'

for i in range(len(labels)):

        bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))

self.buttons.append(bt)

    print self.buttons[2].Position.y

    print self.buttons[1].Label

if name == ‘main’:
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show(True)
app.MainLoop()

Now everywhere I’ll call self.buttons[0] I’ll get the correct button.

Have a nice day:-)

···

2008/7/12 raffaello barbarossa.platz@gmail.com:

Countermand.
For whom may be interested, I found a solution, starting from the consideration that the only objects in Python that can be declared without assigning them at the same time a value are, AFAIK, lists and dictionaries.

Here below is my code, tested and working:

import wx

class MyFrame(wx.Frame):

buttons =

Buttons map:

‘First’, ‘Second’, ‘Third’

def __init__(self):

    wx.Frame.__init__(self, None, -1, "My Frame", size=(300, 300))
    panel = wx.Panel(self, -1)
    pt = wx.Point(20, 10)
    labels = 'First', 'Second', 'Third'

for i in range(len(labels)):

        bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))

self.buttons.append(bt)

    print self.buttons[2].Position.y

    print self.buttons[1].Label

if name == ‘main’:
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show(True)
app.MainLoop()

Now everywhere I’ll call self.buttons[0] I’ll get the correct button.

Have a nice day:-)

2008/7/12 Christopher Barker Chris.Barker@noaa.gov:

Tim Roberts wrote:

No, the storage models are quite different. In Python, names are just references to objects.

Exactly.

In fact, python essentially has only pointers – when you bind a name, you are binding a name to a reference.

one = [1,2,3]

two = one

this is like having one and two both be pointers to the same address.

At this point, both “one” and “two” are bound to the same list, a list with three elements.

one = [4,5,6,7]

now you’ve set one to a new address – this isn’t going to change value of at that address two is pointing to.

This might help you understand python name binding:

http://starship.python.net/~mwh/hacks/objectthink.html

It’s an entertaining read, in any case.

The keys() method creates a new list from the dictionary’s keys at that point in time.

The above discusses this, too.

labels = ‘First’, ‘Second’, ‘Third’

   self.dButton = []   # note list, not dictionary

   for i in range(len(keys)):

       bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))

       self.dButton.append( bt )

       print bt.Position.y

             print self.dButton[1].LabelText

   print 'len(buttons): ', len(self.dButton)

You can use a dict for this two – indeed, I’d be inclined to:

    labels = 'First', 'Second', 'Third'

    self.dButton = {}   # note dictionary, not list

    for i, label in enumerate(labels):

        # if you used sizers, you wouldn't need i

        # and could just do: for label in labels

        bt = wx.Button(panel, label=label, pos=(50, 20 + 30 * i))

        self.dButton[label] =  bt

        print bt.Position.y


    print 'len(buttons): ', len(self.dButton)

I prefer this, as you can refer to the buttons by name:

dButtons[“First”] will get you the first button

As for Binding, I kind of like the lambda with keyword argument trick. Inside that loop:

    bt.Bind(wx.EVT_BUTTON, lambda evt, l=label: self.OnButton(evt, label))

And the handler:

def OnButton(evt, label):

    print "Button: %s clicked"%label

(untested code!)

-Chris

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


wxpython-users mailing list

wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

Marc Tompkins wrote:

···

On Fri, Jul 11, 2008 at 10:10 PM, raffaello <barbarossa.platz@gmail.com > <mailto:barbarossa.platz@gmail.com>> wrote:

    So, the only way to «pinch the wind» (and if you have ever tried it
    you know it is a tiresome matter) is to build all the objects at
    blast speed with a loop and then, manually and painfully, add to
    each its variable .

I'm not entirely convinced that what you want to do is impossible

It's not -- you can use setattr() inside your loop, and assign them to attributes that way -- but I think the list or dict method is the way to go.

-Chris

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

raffaello wrote:

consideration that the only objects in Python that can be declared without assigning them at the same time a value are, AFAIK, lists and dictionaries.

Perhaps it's a language issue, but you don't seem to get what "assignment" means in Python -- did you read Alex Martelli's explanation with the statue analogy?

Anyway, there is no such thing as "declaring" a object in Python - there is only name binding. When you type:

name = object

you are binding "name" to object. You are right, there is no way to create a name without binding it to an object, and that applies to lists, dicts, etc.

MyList =

is binding the name "MyList" to an empty list. It's empty, but it is a specific list created when you type that literal. To test this:

A =
B = A

(now we have two names bound to the same empty list.

B[0] = 5

now that list is no longer empty, and BOTH A and B are still bound to it.

What may be confusing you is that some objects are mutable: they can be changed, like the list above, and dicts, while some are immutable: they can not be changed, like numbers, strings and tuples:

A = ()
A[0] = 5

will raise an error -- you can't change a tuple once it's created -- all you can do is create a new one, and bind a name to it:
A = ()
B = A
A = (5,)

no A is re-bound, so A and B are no longer bound to the same object.

If you want a create an attribute, and don't know what value it should hold yet, you can always assign it to None:

self.A = None

import wx
class MyFrame(wx.Frame):
       buttons =

note that buttons is now a class attribute --every instance of this class will refer to the same list. You probably don't want that (though it may not matter if you only make one of these). I"d initialize it inside you're __init__

    def __init__(self):
        wx.Frame.__init__(self, None, -1, "My Frame", size=(300, 300))
        panel = wx.Panel(self, -1)
        pt = wx.Point(20, 10)
        labels = 'First', 'Second', 'Third'

           self.buttons

        for i in range(len(labels)):
            bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))
            self.buttons.append(bt)

        print self.buttons[2].Position.y
        print self.buttons[1].Label
Now everywhere I'll call self.buttons[0] I'll get the correct button.

yup, but you have to them keep track of which index is which button, so I"d be more inclined to use a dict:

self.buttons[label] = bt

now you can do:

self.buttons["Second"]

and get the right button.

Hope this helps,

-Chris

···

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

I agree with you, Chris: a dictionary saves many a headache, mainly when the controls to be controlled grow in number, or you use them in a class derived from the original skeleton frame. By the way, the dictionary allows to assign to it ALL the variables referring to controls without making a mess.

Since the large employ of mass produced controls must not necessarily be restricted to buttons (sometimes we also have many TextCtrls or ComboBoxes in a frame), I suggest as key of the dictionary the property Name, and not the Label.

Summing up, I think that this long debate has not been useless: at least not for me. My heartfelt thanks.

P.S. Do you have at hand the address of Alex Martelli’s explanation?

···

2008/7/14 Christopher Barker Chris.Barker@noaa.gov:

raffaello wrote:

consideration that the only objects in Python that can be declared without assigning them at the same time a value are, AFAIK, lists and dictionaries.

Perhaps it’s a language issue, but you don’t seem to get what “assignment” means in Python – did you read Alex Martelli’s explanation with the statue analogy?

Anyway, there is no such thing as “declaring” a object in Python - there is only name binding. When you type:

name = object

you are binding “name” to object. You are right, there is no way to create a name without binding it to an object, and that applies to lists, dicts, etc.

MyList =

is binding the name “MyList” to an empty list. It’s empty, but it is a specific list created when you type that literal. To test this:

A =

B = A

(now we have two names bound to the same empty list.

B[0] = 5

now that list is no longer empty, and BOTH A and B are still bound to it.

What may be confusing you is that some objects are mutable: they can be changed, like the list above, and dicts, while some are immutable: they can not be changed, like numbers, strings and tuples:

A = ()

A[0] = 5

will raise an error – you can’t change a tuple once it’s created – all you can do is create a new one, and bind a name to it:

A = ()

B = A

A = (5,)

no A is re-bound, so A and B are no longer bound to the same object.

If you want a create an attribute, and don’t know what value it should hold yet, you can always assign it to None:

self.A = None

import wx

class MyFrame(wx.Frame):

  buttons = []

note that buttons is now a class attribute --every instance of this class will refer to the same list. You probably don’t want that (though it may not matter if you only make one of these). I"d initialize it inside you’re init

def init(self):

    wx.Frame.__init__(self, None, -1, "My Frame", size=(300, 300))

    panel = wx.Panel(self, -1)

    pt = wx.Point(20, 10)

    labels = 'First', 'Second', 'Third'

self.buttons

for i in range(len(labels)):

        bt = wx.Button(panel, -1, labels[i], pos=(50, 20 + 30 * i))

        self.buttons.append(bt)



    print self.buttons[2].Position.y

    print self.buttons[1].Label

Now everywhere I’ll call self.buttons[0] I’ll get the correct button.

yup, but you have to them keep track of which index is which button, so I"d be more inclined to use a dict:

self.buttons[label] = bt

now you can do:

self.buttons[“Second”]

and get the right button.

Hope this helps,

-Chris

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


wxpython-users mailing list

wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

As I said before, you can put them all in a dictionary ctrl_dict, and at the end of the loop, to self.dict.update(ctrl_dict).

That way, you have it both ways :wink:
Might even be marginally faster than calling setattr() all the time, but have no idea if that really matters.

Cheers,

–Tim

···

On Mon, Jul 14, 2008 at 8:24 AM, Christopher Barker Chris.Barker@noaa.gov wrote:

Marc Tompkins wrote:

On Fri, Jul 11, 2008 at 10:10 PM, raffaello <barbarossa.platz@gmail.com mailto:barbarossa.platz@gmail.com> wrote:

So, the only way to «pinch the wind» (and if you have ever tried it

you know it is a tiresome matter) is to build all the objects at

blast speed with a loop and then, manually and painfully, add to

each its variable .

I’m not entirely convinced that what you want to do is impossible

It’s not – you can use setattr() inside your loop, and assign them to attributes that way – but I think the list or dict method is the way to go.

-Chris

this code:

    def ProcessLog(self, event):
        global FirstLineCount
        global CurrentLineCount
        CurrentLineCount = len(open(LogDirValue, 'rb').readlines())
        if CurrentLineCount == FirstLineCount:
            pass
        if CurrentLineCount == FirstLineCount + 1:
            self.FLCplus1()

        FirstLineCount = CurrentLineCount

    def FLCplus1(self):
        LineRead1 = linecache.getline(LogDirValue, CurrentLineCount)
        for i in xrange(1,7):
            if LineRead1.find('C%iSearchString'%i):
                Publisher.sendMessage(('C%i'%i), LineRead1)
        linecache.clearcache()

Cause all log entries no matter if they match the C1SearchString to C6SearchString or not to be published to all 6 of my miniframes with textctrls.
so if I type "blah blah blah" it's entered into the logfile as "[Day Mon Date hh:mm:ss year] you say, 'blah blah blah'" and it shows up on all 6 of my
subscribers from the pubsub. It didn't seem to fail when i was manually copying and pasting log entries from Windows Notepad and saving the log
file.

Did I explain it well enough? It doesn't sort, it just publishes everything it see from the log file.

I tried other stuff:

    def FLCplus1(self):
        LineRead1 = linecache.getline(LogDirValue, CurrentLineCount)
        if LineRead1.find(C1SearchString):
            Publisher().sendMessage(('C1'), LineRead1)

        if LineRead1.find(C2SearchString):
            Publisher().sendMessage(('C2'), LineRead1)

        if LineRead1.find(C3SearchString):
            Publisher().sendMessage(('C3'), LineRead1)

        if LineRead1.find(C4SearchString):
            Publisher().sendMessage(('C4'), LineRead1)

        if LineRead1.find(C5SearchString):
            Publisher().sendMessage(('C5'), LineRead1)

        if LineRead1.find(C6SearchString):
            Publisher().sendMessage(('C6'), LineRead1)

        linecache.clearcache()

And with this code, the same things happens which is essentially the "for i in xrange(1,7):" but split up, at first i thought it was more like a "if elif else" from my previous
message.

using "if elif else" it seems to miss like every other log entry as they are being written to the log.

Steve Freedenburg wrote:

Ok The code that was given to me, I wanted to make sure I understood before I used it.
I didn't want to use code that I couldn't understand.

Anyhow...

how come this seems to work "faster":

for i in xrange(1,7):
   if LineRead1.find(globals()['C%iSearchString'%i]):
       Publisher.sendMessage(('C%i'%i), LineRead1)

than this:
       if LineRead2.find(C1SearchString) >= 0:
           Publisher().sendMessage(('C1'), LineRead2)
       elif LineRead2.find(C2SearchString) >= 0:
           Publisher().sendMessage(('C2'), LineRead2)
       elif LineRead2.find(C3SearchString) >= 0:
           Publisher().sendMessage(('C3'), LineRead2)
       elif LineRead2.find(C4SearchString) >= 0:
           Publisher().sendMessage(('C4'), LineRead2)
       elif LineRead2.find(C5SearchString) >= 0:
           Publisher().sendMessage(('C5'), LineRead2)
       elif LineRead2.find(C6SearchString) >= 0:
           Publisher().sendMessage(('C6'), LineRead2)
       else:

Isn't it essentially the same? It's like the top one catches every log entry on my program... the bottom one seems to miss about half of them.

I'm after real time catching of log entries... So it's not an unwelcome thing, I just want to know why.

Thanks

Steve

Well, this is just a quick observation, but the second version is a bunch of elifs, so only one of those can happen, and then it breaks out until next time. So it is only going to announce the first match it finds. On the other hand the much shorter top version doesn't stop if it finds a match; it isn't equivalent functionality-wise. I assume this is what you mean by "faster"; some messages are getting logged in earlier iterations that co-occur with other messages. To make it equivalent you would need a break:

for i in xrange(1,7):
   if LineRead1.find(globals()['C%iSearchString'%i]):
       Publisher.sendMessage(('C%i'%i), LineRead1)
       break

so that it stops after the first match. But that doesn't sound like what you want.

Does that explain it?
- Mike

Steve Freedenburg wrote:

I guess it does make sense, but now it seems that there is no sorting, everything gets published to every subscriber, but I have 6 diferent topics... something is weird.

I don't see your subscriber code...

anyway, a suggestion:

In order to help yourself figure out how stuff works, and to make it easier to get help on mailing lists, it's very helpful to make small, self contained examples that test just what you are trying to understand at the moment. For example, in this case, you could write a small example that runs a loop and sends a few different messages, with a couple subscribers that you expect to receive those messages -- without the complication of reading your files. The subscribers could simply write to stdout to test them, no need to do any more than that.

-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