Tao of the Machine

Programming, Python, my projects, card games, books, music, Zoids, bettas, manga, cool stuff, and whatever comes to mind.

Wax 0.2.0 released

I just finished updating Wax to work with wxPython 2.5.1.5. As the migration notes warn, this new wxPython version breaks some existing code, and I did indeed have to change a number of things in Wax, although the breakage wasn't *extremely* bad. Some of the things I noticed:

  • Using wx.NULL to indicate that a Frame has no parent does not work anymore. Use None instead. This affected Frame, FlexGridFrame and GridFrame.
  • Some DC methods broke; use a (x, y) tuple or the methods ending in *XY.
  • wx.TreeView.GetFirstChild apparently changed its number of parameters.
  • wx.Sizer.Add now takes a tuple (width, height) or a component, which actually made the Wax code a bit easier.
  • Something strange: in a Notebook, when you change the page or open a new one, the window in that tab does not automatically take focus anymore. Maybe this is a problem in my own code, maybe not.
  • The "choose directory" dialog has changed. (Don't know if this affects the interface; are there additional flags?)

It's possible that some of these things, like the wx.NULL issue, have been deprecated for a while... I don't know. :-)

It's also quite possible that I've overlooked some changes, so use this version with caution, and don't be too surprised if 0.2.1 will be released quickly. Also note that Wax 0.2.0 will not work with wxPythons older than 2.5.1.5. (I suppose you could hack it to make it work, but that is not recommended... in fact, the whole point of this 0.2.0 release is compatibility with 2.5.1.5.)

Downloads in the usual place. Bug reports welcome at the usual address.

Posted by Hans Nowak on 2004-04-03 01:19:47   {link} (see old comments)
Categories: Wax

wxPython 2.5.1.5 available

(via Kevin Altis) The latest version of wxPython, 2.5.1.5, is now available. Important links: Download, recent changes, migration guide.

Wax will have to use this version sooner or later, so I'm going to take a close look at the migration guide. It's possible that the current Wax version is the last one using the 2.4.x line.

Since 2.5.1.5 appears to be using new-style classes, it may be possible to "abstract away" the Wax event methods using metaclasses. Yes, I still think metaclasses are evil, but like the PyCon metaclasses paper suggests, there are situations where they are useful and actually make things clearer, compared to "regular" code... and this may be one of those situations. I will probably write more about this later; first I'll have to see how well Wax plays with 2.5.1.5, and make changes where necessary.

Posted by Hans Nowak on 2004-04-02 22:31:41   {link} (see old comments)
Categories: Wax, Python

Wax 0.1.50

Wax 0.1.50 is available in the download section. New since 0.1.44:

  • Added FlexGridContainer, FlexGridPanel, FlexGridFrame
  • added TaskBarIcon
  • WaxObject grew SetSizeX and SetSizeY methods, which are useful to change just the width or height of a control
  • added some events here and there :-)
  • a bunch of bugfixes

I also added a tools directory, which is going to be a subpackage containing non-rudimentary controls. These will typically be controls that are built from existing Wax controls, rather than wxPython ones. Some existing dialogs qualify, like the ErrorDialog and the TextEntryDialog, which are written in pure Wax. Expect them to move to the tools package somewhere between this release and the 0.2.

Modules in tools will *not* be imported automatically, so you'll have to do from wax.tools import foobar and such. But for now, this directory is still empty.

As for the TaskBarIcon, I added this today, based on code at Norfolk Graphics. Here's how to use it. It also illustrates how to add a pop-up menu to a control.

# trayicon1.py
# Based on wxPython example at http://norfolkgraphics.com/python.php

from wax import *

class MainFrame(Frame):

    def Body(self):
        self.tbicon = TaskBarIcon()
        self.status = 0
        self.UpdateIcon()
        
        self.tbicon.OnLeftDoubleClick = self.RestoreWindow
        self.tbicon.OnRightUp = self.ShowTaskBarMenu
        
        self.Show(1)
        
    def UpdateIcon(self):
        """ Update icon based on current state. """
        if self.status == 0:
            self.tbicon.SetIcon('icon1.ico', 'Icon demo - state 1')
        else:
            self.tbicon.SetIcon('icon2.ico', 'Icon demo - state 2')
            
    def ToggleIcon(self, event):
        self.status = not self.status
        self.UpdateIcon()
        
    def RestoreWindow(self, event=None):
        """ Show/restore main window. """
        self.Show(1)
        self.Iconize(0)
        
    def HideWindow(self, event=None):
        self.Iconize(1)
        
    def ShowTaskBarMenu(self, event=None):
        menu = Menu(self.tbicon)
        
        # choose Show/Hide based on current window state
        if self.IsIconized():
            menu.Append('&Show window', self.RestoreWindow)
        else:
            menu.Append('&Hide window', self.HideWindow)
            
        # these entries are always present
        menu.Append('&Toggle Icon', self.ToggleIcon)
        menu.Append('E&xit', self.ExitApp)
        
        self.tbicon.PopupMenu(menu)
        
    def ExitApp(self, event):
        self.Close()
        
    def OnIconize(self, event=None):
        self.Iconize(1) # minimize
        self.Show(0)    # hide taskbar button
        
        
if __name__ == "__main__":

    app = Application(MainFrame, title='trayicon demo')
    app.Run()

The original wxPython code was only tested on Windows NT 4, 2000 and XP. I'm running Windows 2000 and it works for me... I'm not sure about other versions of Windows or other operating systems. This TaskBarIcon thingy is hardly essential, but it may contribute to giving Wax apps a "native look and feel" on Windows.

Posted by Hans Nowak on 2004-02-22 22:16:11   {link} (see old comments)
Categories: Wax

Wax at PyPI

Much to my surprise, Wax appeared in the PyPI "recent updates" feed today. Here's the entry. It's surprising, because I didn't add it. emoticon:smile Apparently done by a fan (?). I don't mind, except for the minor nitpick that the version is called "1.44" (it's 0.1.44).

Posted by Hans Nowak on 2003-12-18 23:26:20   {link} (see old comments)
Categories: Wax

Wax 0.1.43 available

In the usual place. New are BitmapButton, MDIChildFrame, and MDIParentFrame, contributed by Gandalf.

Much to my dismay, wxPython's wxBitmapButton seems to display the bitmap only, and no label. In Delphi, there is a button that can display both, and I want one too for Wax (because I am in the process of converting a Delphi GUI to Wax, and because it's a cool thing to have :-). Investigation is pending. It's possible to grab a Panel, throw a bitmap and a label on it, and make them clickable. I built an experimental control that does this, but it doesn't show a border for some reason. [Update: wxGenBitmapTextButton does what I want.]

Also, it appears that the wxToggleButton doesn't exist on the Mac, so that one is now imported conditionally.

Posted by Hans Nowak on 2003-12-13 17:48:10   {link} (see old comments)
Categories: Wax

Can't stop the bumrush

I'm currently working on a little file explorer app. It may or may not end up being useful, but more important is that hacking on GUI apps helps me improve Wax. In version 0.1.42, two new objects were added: FileTreeView and ImageList.

To find out how they work, see the demo. (Which is really a rough draft for my application.)

Posted by Hans Nowak on 2003-11-27 23:31:05   {link}
Categories: Wax

Waxy Fruvous

Wax 0.1.40 is available. I noticed that it didn't have a CheckBox and RadioButton yet, which I apparently overlooked. So those have been added now.

Also, I'm trying to get GroupBox (wxStaticBox in wxPython) to work. Something's missing, but I don't know what. If you're interested in helping, here's the idea: Look at the wxPython demo, under RadioButton. The demo groups radiobuttons nicely, but the code is kind of a mess, using various sizers. I want to achieve the same effect without explicit sizers.

The code I have so far is in groupbox.py, containers.py (GroupBoxContainer) and examples/groupbox1.py. It works "almost", but not quite... it's like the radiobuttons are placed outside of the GroupBox. Which is probably what happens, but I can't figure out how to get them in the right place. Any help is welcome.

Posted by Hans Nowak on 2003-11-25 20:26:01   {link}
Categories: Wax

OverlayPanel

In Delphi, the TabbedNotebook control can optionally be displayed without tabs. wxPython's Notebook doesn't seem to have this ability, most likely because the wxWindows Notebook doesn't support it. So I wrote my own version for Wax.

The new control is called an OverlayPanel, by lack of a better name. It's simple to use; you just add windows to it (anything that derives from wxWindow, much like a NoteBook). By default these windows will be hidden. With the Select() method you can pick one to be shown.

What good is this? In Delphi, the notebook-without-tabs is often used to display different information in the same panel. Usually, the window/form has a main area, and depending on what menu option you choose, different information and controls appear there. In Wax, I needed it for a Wizard control. (I will not derive from wxWizard, but rather try to come up with a more Pythonic control.)

Download the experimental Wax version 0.1.37 here. And here's some sample code illustrating its usage.

# overlay1.py

from wax import *

class MainFrame(Frame):
    def Body(self):
        self.toolbar = self.MakeToolbar(self)
        self.AddComponent(self.toolbar, border=1)
    
        self.op = OverlayPanel(self)
        self.op.SetSize((400,300))
        self.AddComponent(self.op)

        for s in (
            'Joho en een fles prik',
            'Blah blah...',
            'Sleeze beez',
            'This is a really long text for you',
        ):
            win = self.GenWindow(self.op, s)
            self.op.AddComponent(win)
            
        self.op.Select(0)
        
        self.Pack()
        
    def GenWindow(self, parent, text):
        p = Panel(parent)
        p.SetBackgroundColour((0, 0, 127))
        b = Button(p, text)
        p.AddComponent(b, border=20)
        p.Pack()
        return p
        
    def MakeToolbar(self, parent):
        p = Panel(parent, direction='h')
        for i in range(4):
            def f(event, self=self, i=i):
                self.op.Select(i)
            b = Button(p, "Select %d" % (i,), event=f)
            p.AddComponent(b)
        p.Pack()
        return p
        

if __name__ == "__main__":

    app = Application(MainFrame, title='test test...', direction='v')
    app.Run()

Ultra-short usage example:

op = OverlayPanel(parent)
op.AddComponent(window1)
op.AddComponent(window2)  # ...etc...
op.Select(0)  # select first window

GenWindow is just a quick way to create separate windows, and MakeToolbar creates a toolbar with buttons that cause selected windows to appear in the OverlayPanel.

Important note: I had to write my own sizer, to be used by the OverlayContainer control, which in turn is the basis for the OverlayPanel. I don't know much about sizers, so wxPython adepts, if you see anything strange/missing, or have suggestions for improvement, then I'd love to hear about it. Also, this Container's AddComponent method doesn't have the usual options (expand, align, etc) that other versions have. I'd like to hear if/how these can be implemented. It does now. Check out version 0.1.38.

Posted by Hans Nowak on 2003-11-24 16:14:40   {link}
Categories: Wax

Improving Wax

To everybody who is interested in Wax, here's a request. Please download the latest version and look at the controls that are currently available. (They're all listed in __init__.py.) What *crucial* controls are, in your opinion, missing? I'm talking important components here, not a Throbber or something like that.

Please mail any suggestions to the usual address, or leave a comment. 1) If possible/necessary, I'd like to hear a motivation of why you think this control is important.

The idea is to have a "roadmap" towards a stable version of Wax that has all the necessary ingredients for building an (average) application.

1) Although Haloscan seems to have problems with comments lately... sometimes it doesn't show the correct number of comments, and occasionally comments just disappear. I never deleted any, so that can't be it. Hmm.

Posted by Hans Nowak on 2003-11-12 16:28:28   {link}
Categories: Wax

Update

New versions of Wax and Charm are available in the download area. (What is Charm?)

One might wonder what the deal is with all these unfinished and unstable projects. :-) The situation is, that they are not ready yet for prime time... they have missing features, possibly glaring errors, unrefactored code, etc. However, they are usable. Charm isn't quite done, but I'm using it to edit my code, also for work, and I've been doing so for several months. The same for Firedrop... I have been managing this site with it for a few months now. Both of them use Wax, which is even further away from a 1.0 release, but which happily powers these programs nonetheless. Oh, and then there's Testipy and Sextile...

So, I hope that all these projects (and some others) eventually will earn the "stable" status. With help from others, this will go a little faster; but while contributions are greatly appreciated, the projects will still live without them.

In the meantime, if you write code based on a certain Wax version, it may be safer to keep that version around... backward-incompatible changes *may* occur in the future. I will try to keep this to a minimum, but the version number is 0.1.x for a reason. :-)

Posted by Hans Nowak on 2003-09-29 23:58:28   {link}
Categories: Wax, Charm

Things and stuff...

I uploaded some thingies to the download area... this includes development versions of Charm and Firedrop. These programs have been usable for months, but the problem was (and still is) that there wasn't sufficient documentation. Maybe I can get around to writing some, one of these days.

I also uploaded the latest version of Wax, 0.1.32. Like I said before, there's also a wiki now, currently with a bare minimum of information.

Nobody Not many people seem to like the "Self-style" objects. :-) I am currently pondering some use cases for this. One area where they could be useful is adventures/interactive fiction. Long ago I found that traditional classes are not a really good match for laying out an adventure's rooms, characters and objects... maybe Self-style objects will fare better.

The nice thing is that you can "isolate" certain behavior (methods and attributes) in an object, and then grant any other object access to this behavior by making the original object its parent. Objects don't have introspection, cloning facilities, etc, but you can add them at will whenever necessary. This allows for an entirely different design than traditional classes.

Posted by Hans Nowak on 2003-09-25 23:33:50   {link}
Categories: Charm, Wax, Firedrop, Python

Wax Wiki in progress

I've created a wiki for Wax. (Well, and maybe other projects, in the future.) More later, when I've figured out how to use the damn thing. emoticon:smile There's zero content at the moment, so no link yet.

Posted by Hans Nowak on 2003-09-23 19:05:53   {link}
Categories: Wax

Wax primer

A Wax primer, in two parts. As always, the latest (development) version of Wax can be found in the download area. By the way, any cool ideas and suggestions for new (and existing) Wax controls are welcome.

Posted by Hans Nowak on 2003-09-21 13:19:51   {link}
Categories: Wax

Wax teaser: drag & drop

(Wax 0.1.29) Making a control accept drag & drop is as easy as:

class MyNoteBook(NoteBook):
    def __init__(self, ...):
        ...other code here...
        filedrop = FileDropTarget(self, self.OnDropFiles)
        self.SetDropTarget(filedrop)
    def OnDropFiles(self, x, y, filenames):
        print "Dropped:", filenames
        # do something with filenames...

Currently FileDropTarget takes two arguments, a window (the drop target) and a function (event) which is called when things are dropped. I wonder if I even need the window argument.

Posted by Hans Nowak on 2003-09-11 22:45:59   {link}
Categories: Wax

A little Wax teaser

The following mini-application shows a notebook with two pages. One shows PyCrust (Patrick O'Brien's Python shell), the other PyCrust with filling (a tree-based object/namespace inspector). (Note: this is all in the development version of Wax, currently at 0.1.26.)

from wax import *

class MainFrame(Frame):
    def Body(self):
        nb = NoteBook(self)
        self.AddComponent(nb)
        
        nb.AddPage(PyCrust(nb), "PyCrust")
        nb.AddPage(PyCrustFilling(nb), "PyCrustFilling")
        self.Pack()
        
app = Application(MainFrame, title="PyCrust demo")
app.Run()

PyCrust and PyCrustFilling are (under different names) staples in wxPython, and now in Wax as well. Being able to stick a (high-quality) shell in your programs is a very useful feature.

Some more code. I added a TextEntryDialog today. This is the source:

from dialog import Dialog
from textbox import TextBox
from label import Label

class TextEntryDialog(Dialog):

    def __init__(self, parent, title="Enter some text", 
     prompt="Enter some text"):
        self.prompt = prompt
        Dialog.__init__(self, parent, title)
        
    def Body(self):
        label = Label(self, self.prompt)
        self.AddComponent(label, stretch=1, border=7)
                
        self.text = TextBox(self, size=(100,25), process_enter=1)
        self.text.OnChar = self.OnTextBoxChar
        self.AddComponent(self.text, stretch=1, border=5)
        
    def OnTextBoxChar(self, event=None):
        # pressing Enter in the TextBox is the same as clicking OK
        if event.GetKeyCode() == 13:
            self.OnClickOKButton(event)
        else:
            event.Skip()
            
    def GetValue(self):
        return self.text.GetValue()

Some thoughts:

  • You shouldn't have to know the key codes. On the other hand, having tons of special variables around is not the way to go either. I plan on adding a module or class, called keys or whatever, containing attributes with the codes... so you can say keys.enter, etc.

  • Note that you just create controls in the Body() method, and add them. The Dialog class will stack them vertically, and add the OK/Cancel buttons automatically.

  • A little extra code was necessary to make the TextBox behave correctly when pressing Enter (this has the same effect as clicking OK). We create the TextBox with argument process_enter=1, meaning it will treat Enter as any other keypress, rather than moving to the next control (which is the default behavior). Then, we add an event that does the right thing when Enter is pressed.

  • Wax is not meant as a replacement for wxPython. Rather, it's a layer on top of it. You can use wxPython controls in Wax, and vice versa (I think; I haven't actually tried this). Also, it's technically easy to "Wax-ify" a wxPython control.

Posted by Hans "ik poets de plaat" Nowak on 2003-09-02 23:09:34   {link}
Categories: Wax

Wax 0.1.20

I wrote about Wax before (1, 2, 3, 4, 5)... it's a layer on top of wxPython. Although it's far from complete, I am already using it for some projects. I find it a joy to use, but then again I might be a little bit biased. emoticon:smile A first version is available now. Don't expect too much; the version number should tell you enough about its state. ^_^;

Bear in mind that it's not complete, though usable, and that I am open to suggestions, bugfixes, contributed code, etc. Read more here. Latest version can be found in the download section. Documentation will be added Real Soon Now.

Posted by Hans Nowak on 2003-08-29 16:59:54   {link}
Categories: Wax

More wxPython woes: wxImage, wxBitmap

So I thought the wxListCtrl class was hairy. Enter wxImage. What a pain. Judging from the demo, you’d think it’s relatively easy to load and display an image. Wrong; all my attempts failed. Even copying the exact demo code to my file would not work. Eventually it comes down to two problems:

  1. wxImage is not forgiving when it comes to path names.
  2. The appropriate image handler needs to be installed before an image of a certain type can be loaded.

Not very friendly, so there is another task for Wax. Here’s the code that displays an image in a frame:

from wax import *

class MainFrame(Frame):
def Body(self):
jpg = Image(‘moo.jpg’)
bitmap = jpg.ConvertToBitmap()
bmp = Bitmap(self, bitmap)
self.AddComponent(bmp)
self.Pack()

app = Application(MainFrame)
app.MainLoop()

The line jpg = Image(‘moo.jpg’) not only loads an image, but performs some trickery behind the scenes: it detects the type, and installs the JPG handler. (Autodetect and install-on-demand are the default, but can both be turned off if necessary.)

It can be done even shorter, by the way, using the BitmapFromFile function, but (currently) this only works if the appropriate handler is already installed.

Posted by Hans Nowak on 2003-05-30 22:38:22   {link}
Categories: Wax

Do you hate the wxListCtrl class too? ;-)

No? Well, bully for you. I happen to dislike the class greatly, because I never seem to be able to figure out how to get it to work. The example in the demo uses an image; what if I don’t want an image? And what’s up with having to call InsertImageStringItem, SetStringItem and SetItemData in order to insert an item?

That can, no, must be done better. So I added a class named ListView to Wax, which is going to take care of this. This is how you create it:

listview = ListView(self, size=(500,300), 
columns=[“Sender”, “Subject”, “Date”])

(Note that you can pass the column names in the constructor, but you don’t have to.)

And this is how you populate it:

listview.AppendRow(“Fred”, “foo”, “bar”)

You can now use [row,column] syntax to get or set the value of a “cell”:

listview[0,1] = “Re: Programming”
print listview[1,1]

More Leckerbissen will be added later. But, damn, I cannot release the code yet. The main reason is that it’s too unstable. Everything so far works fine, but I have only covered 20-odd wxPython controls, and some are vastly incomplete (lacking common events for example). On top of that, everything is still subject to change. Maybe tomorrow I’ll decide that the layout stuff needs rewritten, or something like that… major code breakage could be the result.

To put it another way: I don’t want to get angry mails when I release a version that’s not backwards compatible, and I don’t want to get mails pointing out that it’s not complete. I know that.

If you really, really want the development code, you can drop me a mail and I’ll send it to you, but I strongly discourage using it for anything else but testing. At this point I also cannot accept other people’s classes, because I’m still trying to figure out “the Wax philosophy”.

In the meantime, let’s ponder the idea of adding data-aware controls to Wax…

Posted by Hans Nowak on 2003-05-30 01:08:32   {link}
Categories: Wax

Put it on Wax... *

Wax is coming along nicely. I whipped up a rudimentary GUI for Firedrop2 in 5 minutes. Here’s what the code looks like:

from wax import *

FONT_COURIER = Font(“Courier New”, 10)

class MainFrame(Frame):

def Body(self):
self.SetTitle(“Firedrop2 GUI”)

self.splitter = Splitter(self)
self.listbox = ListBox(self.splitter, [])
self.notebook = NoteBook(self.splitter)
self.splitter.Split(self.listbox, self.notebook,
direction=‘vertical’)

self.editor = TextBox(self.notebook, multiline=1)
self.editor.SetFont(FONT_COURIER)
self.notebook.AddPage(self.editor, “Editor”)
self.output = TextBox(self.notebook, multiline=1,
readonly=1)
self.output.SetFont(FONT_COURIER)
self.notebook.AddPage(self.output, “Output”)

self.statusbar = StatusBar(self, numpanels=3)
self.SetStatusBar(self.statusbar)

app = Application(MainFrame)
app.MainLoop()


(This creates a window with a splitter; a listbox on the left, a notebook on the right; the notebook has two pages, both with editors.)

The Wax controls are far from complete yet (for example, they don’t have any events at the moment), but they are already usable. And easy to use! Maybe I’m on to something here. It sure feels easy after spending some hours in .NET hell. ;-)

Posted by Hans Nowak on 2003-05-20 22:13:41   {link}
Categories: Python, Firedrop, Wax

Wax/Wane *

So far, Wax is coming along quite well. There was a nasty bug, probably due to the fact that it’s not a good idea to create any windows before the Application has been started and initialized. (Although it worked with some controls… this is a bit of a mystery.) So now you have to create classes for everything, then call Application with the main class (not instance) and a list of parameters. Not perfect, but better than overriding OnInit, IMHO.

I would like to add more dynamism, like creating frames on the fly and then sticking them in a mainloop, but you can’t force wxPython to have the Tkinter nature. :)

To give y’all a taste, here’s a small Wax program that shows off some components…

from wax import *

class MainFrame(Frame):

def Body(self):
b = Button(self, “a button”)
self.AddComponent(b, align=‘center’, stretch=1)

l = Label(self, “a label”)
self.AddComponent(l, align=‘center’)

t = TextBox(self, “some text”)
self.AddComponent(t, align=‘center’, stretch=1)

cb = ComboBox(self, [“one”, “two”, “three”])
self.AddComponent(cb, align=‘center’, stretch=1)

lb = ListBox(self, [“hava”, “nagila”, “hava”])
self.AddComponent(lb, stretch=1, border=1)

self.Pack()

app = Application(MainFrame, title=“foo”, direction=‘vertical’)
app.MainLoop()


There’s lots of stuff to add and fix, but I’ll get there when I need it. It’s time for the next 1-day project… :-)

Posted by Hans Nowak on 2003-05-18 12:46:12   {link}
Categories: Python, Wax

--
Generated by Firedrop2.