Efectos Especiales

Ramblings, rants, musings, ideas and observations. Topics include (but are not limited to): programming (especially Python), books, games (especially CCGs and board games), astrology, design, writing, painting, etc.

icon:writing1 #569 (old comments) Oud en nieuw

To be honest, I liked my old blog better. Maybe not the design (although that snake was kinda cute :-), but so far, the new one doesn't seem to have much in the way of content. In other words, I need to write more. One of my goals for the new blog was to write when I have something to write about, rather than trying to write every day. It appears that that plan backfires... I end up not writing at all. So much for special effects. emoticon:sweatdrop

I'm not sure if it means anything, but Tao of the Machine still has more Bloglines subscribers than Efectos Especiales. More than three times as much, in fact. Rumors of TotM's demise are greatly exaggerated?

[Update #1] I suspect that many people weren't aware they could leave comments, until I changed the label to the more obvious "N comments"... :-)

/* Posted by Hans Nowak at 2004-07-07 20:22 */

icon:default #568 (old comments) Refactoring

Refactoring can be a strange thing. You change code in ways that seemingly have nothing to do with the problem at hand... tear a long method apart, change names to something more meaningful, isolate code in a separate object. Then all of a sudden, you discover that the code has reached a state where solving your original problem is a piece of cake.

Weird.

/* Posted by Hans Nowak at 2004-07-07 20:18 */

icon:wax #567 (old comments) Wax 0.2.21 available

Another Wax release... I am trying to get to a stable core, which is easier said than done... I hear that wxPython 2.5.2 will revert some of the changes introduced in 2.5.1.5. Sigh.

Anyway, this is what's new since 0.2.15:

Re Menu.Append: Getting the MenuItem back seems useful, but note: When you click a menu item, then for some reason it's not possible to get the same MenuItem back from the event. It's possible to get the *id* of the MenuItem, and from there you can use FindItemById, but this will give you a new instance of MenuItem, rather than the one you created. I learned this the hard way, when I tried to add certain attributes to a menu item, and discovered they were gone later in the program.

It seems that wxPython creates a new instance every time you retrieve the MenuItem. I don't know why. The id/address of the C++ object stays the same.

Re WaxConfig, fonts: Currently, it's possible to define fonts at the top of a file, before anything has been initialized yet. Like this:

WaxConfig.default_font = Font("Tahoma", 8)

FIXED_WIDTH_1 = Font("Courier New", 10)

...later...

app = Application(MainFrame, ...)
app.Run()

Soon, this code will not be allowed anymore by wxPython/wxWidgets. Ditto for setting system-wide fonts in the WaxConfig object. The way I understand it, the reason is that the wx "engine" needs to be started before any wx objects can be created... this includes wx.Fonts. I am not happy with this, but there's not much I can do. Except for allowing a similar construct. It's already possible (since 0.2.20) to do this:

WaxConfig.default_font = ("Courier New", 10)

I will add something like that for fonts too. In other words, SetFont will allow a tuple as well as a Font instance. It's a hack, but I don't want to give up this Very Useful Feature.

Hopefully wxPython 2.5.2 will offer more than just code breakage... a working version of RegisterHotKey would be nice, for example. I have a program that uses it, and I'd like to release it. (Currently it uses an ugly hack that has the same effect... kind of. It's not working too well, sometimes it eats keys, etc.)

/* Posted by Hans "het leven van een programmeur gaat niet over rozen" Nowak at 2004-07-07 19:45 */

icon:rant #566 (old comments) Bah humbug

Grrr, it's really quite annoying if you're trying to keep your code stable and backward compatible, but you're dependent on a platform that has some developers who apparently don't care much about such goals. It gets a bit old when every major release breaks stuff. emoticon:frown

/* Posted by Hans Nowak at 2004-07-01 23:08 */

icon:wax #565 (old comments) A little Wax teaser

The following code demonstrates how to add auto-indentation to a TextBox control. (Auto-indent is defined here as: when you press Enter, the new line will have the same indentation as the previous one.) There's one caveat: it only works in Wax 0.2.19, which is currently not released yet. That's why it's called a teaser. emoticon:devil1

However, since Wax is updated almost daily, a version where it will work should be available Real Soon Now.

So, without further ado, the code...

# editor-with-autoindent-1.py

from wax import *

class AutoIndentTextBox(TextBox):
    """ A TextBox with autoindent.  When Enter is pressed, the next line
        will have the same indentation as the current line. """
    def __init__(self, parent):
        TextBox.__init__(self, parent, multiline=1)
    def OnChar(self, event):
        if event.KeyCode() == keys.enter:
            # what's the indentation of the line we're in?
            indent = self.GetCurrentIndent()
            self.AppendText('\n')
            # indent the next line with the same number of spaces
            self.AppendText(indent)
        else:
            event.Skip()
    def GetCurrentIndent(self):
        """ Return the indent of the current line.  This returns the actual
            indent *string*, e.g. a number of spaces, tabs, or a mix 
            (hiss! boo!). """
        lineno = self.GetCurrentLineNumber()
        line = self.GetLineText(lineno)
        indent = ""
        while line and line[0] in " \t":
            indent = indent + line[0]
            line = line[1:]
        return indent

class MainFrame(Frame):
    def Body(self):
        self.editor = AutoIndentTextBox(self)
        self.editor.Size = (500, 400)
        self.editor.Font = Font("Courier New", 10)
        self.AddComponent(self.editor, expand=1, stretch=1)
        self.Pack()
        
app = Application(MainFrame)
app.Run()

1 -- Actually, most of the code should work in 0.2.15, but TextBox.GetCurrentLineNumber is new in 0.2.19.

/* Posted by Hans Nowak at 2004-06-30 22:53 */

icon:wax #564 (old comments) Wax 0.2.15 available

Wax 0.2.15 is available in the usual place. New since 0.2.10:

Re pseudo-properties: In short, if an object has methods GetFoo and SetFoo, then a "property" Foo can be used as a shorthand for these methods. For example:

print widget.Size         # short for widget.GetSize()
widget.Size = (200, 200)  # short for widget.SetSize((200, 200))

These are not "real" properties like those introduced in Python 2.2. Rather, they are emulated with __getattr__ and __setattr__.

Re event system: This is an important change that hopefully does not break too much code. (If it does, I'll be the first to notice, with my editor and blogging tool relying on Wax.)

The old system created a bunch of dummy methods for events. For example, Button always had an OnClick method, empty by default. There was also an _OnClick method that functioned as a wrapper. This way worked, but had several drawbacks:

The new system works differently:

  1. The BindEvents() method (which should be called upon instantation of a class) checks if a class has any methods with names like OnFoo, and if this is the case, binds those methods to the appropriate event.
  2. Similarly, if a WaxObject instance gets an attribute of the same form (object.OnFoo = f, then object.OnFoo is bound to the appropriate event.

In other words, we can still define event methods like we used to:

class MyButton(Button):
    def OnClick(self, event):
        ...
        # will be bound to wx.EVT_BUTTON upon instantiation

# or:

b = Button()
b.OnClick = f

There's currently no case for dynamically adding such methods to *classes*. So Button.OnClick = f will not have the desired effect.

"Global" events are added to the new events.py module. "Local" events (like wx.EVT_BUTTON etc) are added to the __events__ attribute of the appropriate class. BindEvents() does the rest.

So, things changed behind the scenes, but the behavior should still be the same. It will be easier to add a number of methods now, as soon as I figure out what they do. :-)

Re PlainContainer etc: These are plain vanilla containers, that have no sizers associated with them. Instead, it is possible to add controls at a certain position:

p = PlainPanel(parent)
b = Button(p, "Click me")
p.AddComponent(10, 10, b)  # add at position (10, 10)

# add another widget at (30, 30) with size (150, 100):
p.AddComponentAndSize(30, 30, 150, 100, widget)

(Come to think of it, maybe this should be AddComponent(30, 30, widget, size=(150,100))?) Maybe so. Don't get too attached that AddComponentAndSize method too much. :-)

I don't plan to use these containers much, especially not when writing cross-platform code, but I included them for completeness.

:::

Since this weblog doesn't have real categories, Wax-related posts don't appear on a separate page anymore. You'll have to go through the torture of reading the whole blog. Apparently not everybody is aware of the changes yet. The "pyblagg" aggregator updated their links, but Planet Python currently still points to Tao of the Machine.

/* Posted by Hans Nowak at 2004-06-28 18:29 */

icon:default #563 (old comments) A taste of Poplog (2)

Like some other languages, Pop-11 definitely believes that there's more than one way to do it. Take function definitions, for example. The first way I learned in the Pop-11 primer was something like this:

: define foo(x) -> result;
:       x + 42 -> result;
: enddefine;
:
: foo(4) =>
** 46

The named result value is a nice touch, but sometimes feels a bit redundant. For example, what should its name be when we have a function named area, to compute the area of a shape? area as well? But that's already the name of the function, so you often end up using vague names like total, data and result (and in fact this happens a few times in the tutorial).

Another way to define a function is more C-like:

: define foo(x);
:       return (x+42);
: enddefine;
:
: foo(4) =>
** 46

Note the parentheses around the return value... they're mandatory.

Pop-11 also allows for a Perl-like "shifting" of parameters passed to a function. For example:

: define bar();
:       lvars x;
:       -> x;
:       pr('Parameter: '); pr(x); pr('\n');
: enddefine;
:
: bar(3);
Parameter: 3
: bar('fred');
Parameter: fred

It somehow uses the open stack for this, and since a stack is last-in-first-out, calling function bar with multiple parameters might yield surprising results:

: bar(1, 2, 3);
Parameter: 3

At this point, I don't know yet how to get *all* the parameters passed. Of course, you can just use the "normal" way of defining arguments.

/* Posted by Hans Nowak at 2004-06-28 01:16 */

icon:default #562 (old comments) A taste of Poplog (1)

In my search for a language that is Different, Poplog seems promising. Actually, it's not a language, but a system that can be programmed in various languages... Pop-11, but also Lisp, Prolog and ML. One of its many interesting features is that code can be mixed between the languages. For example:

Sussex Poplog (Version 15.5 18/11/1996 12:12:27)
Copyright (c) 1982-1996 University of Sussex. All rights reserved.
Common Lisp (Version 2.0)

Setlisp
== (pop11)
: define drizzle(x, y) -> result;
:       2 * x + y -> result;
: enddefine;
:
: clisp;
== (pop11:drizzle 3 4)
10

Or mixing POP-11 and Prolog:

(C:\lang\Poplog-15.5\pop) $ pop11 +prolog

Sussex Poplog (Version 15.5 18/11/1996 12:12:27)
Copyright (c) 1982-1996 University of Sussex. All rights reserved.
Prolog (Version 3.2)

Setprolog
?- prolog_language(pop11).
: /* now we are in pop11 */
:
: define foo(a, b, c) -> result;
:       a * b + c -> result;
: enddefine;
:
: :- prolog_language(prolog).
|
| foo(A, B, C, Result) :-
|   prolog_eval(foo(A, B, C), Result).
|
| ?- foo(1, 2, 3, X).
X = 5 ?
yes

| ?- foo(3, 4, 5, X).
X = 17 ?
yes

All this is done from the interactive prompt, by the way, but of course it can be (and in fact, is) done in modules too.

Poplog is a strange, strange beast. It has a really "old" flavor to it, yet offers many things that are available in modern languages as well. (For example, years of Python programming has caused me to expect an interactive interpreter, and Poplog has one.) It kind of feels like a Perl gone right. POP-11 is a rich language, with features low-level enough that you can implement Lisp, ML and Prolog in it, yet with features high-level enough that you can use it instead of those languages. It has weird syntax quirks (a + b -> c rather than c = a + b), an open stack, first-class functions, strange operators, lists not unlike Lisp, an OO system based on Flavors, and much more. I love it. emoticon:kwijl

<theorizing> Imagine if the open source version of Poplog could be made up to par with modern dynamic languages, e.g. by giving it a decent Windows version, a GUI, maybe an IDE, libraries for sockets, portable OS stuff, etc. You wouldn't need anything else anymore. Hack in POP-11, Lisp, Prolog, ML, or in a mixture of any of the above. Or implement your own language on top of it (Python?). Ah, one can always dream... </theorizing>

More later. In the meantime, here are some relevant links:

/* Posted by Hans Nowak at 2004-06-27 14:03 */

icon:games #561 (old comments) Gish

Apparently it's still possible to create an original game these days. Check out Gish. In short, you are a ball of tar (!) crawling through the sewers. Among other things, you can make yourself sticky to climb on things, or slick to slide through narrow holes, or heavy to break through floors. Very playable, very original.

/* Posted by Hans "although in some ways it reminds me of Pac-In-Time" Nowak at 2004-06-27 00:42 */

icon:voetbal #560 (old comments) Czech Republic - Netherlands 3-2

I'd rather see them play like this and lose, than play like the previous game and draw...

/* Posted by Hans "wel een rare wissel trouwens" Nowak at 2004-06-19 22:14 */