Tao of the MachineProgramming, Python, my projects, card games, books, music, Zoids, bettas, manga, cool stuff, and whatever comes to mind.
Last 10 archives
:: About me
:: Oasis Digital
Wax 0.2.0 released
I just finished updating Wax to work with wxPython 18.104.22.168. 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:
It's possible that some of these things, like the
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 22.214.171.124. (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 126.96.36.199.)
wxPython 188.8.131.52 available
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 184.108.40.206 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 220.127.116.11, and make changes where necessary.
Wax 0.1.50 is available in the download section. New since 0.1.44:
I also added a
As for the
# 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
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. 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).
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.
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.)
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
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
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
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.
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
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.
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. :-)
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.
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.
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. There's zero content at the moment, so no link yet.
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.
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.
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()
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. 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.
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:
Not very friendly, so there is another task for Wax. Here’s the code that displays an image in a frame:
It can be done even shorter, by the way, using the
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
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:
And this is how you populate it:
You can now use [row,column] syntax to get or set the value of a “cell”:
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…
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:
FONT_COURIER = Font(“Courier New”, 10)
app = Application(MainFrame)
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. ;-)
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
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…