23/04/2008

jquery and iframes

I needed to select some attributes that were in an iframe today with jquery. The iframe had an id of "preview" so I thought it would be possible to do:

 $('#preview .some_class')

Unfortunately that doesn't work. A quick google turned up nothing so I dug into the source and soon enough it became obvious you can do it like this:

 $('.some_class', $('#preview')[0].content_document)

Only tested in Firefox so far, I've got no idea if it will work in anything else...

Update: I wrote this little function to (hopefully) make this cross browser and a little easier to use:

jQuery.fn.frameselect = function(selector) {
        var oDoc = (this[0].contentWindow || this[0].contentDocument);
        if (oDoc.document) {
            oDoc = oDoc.document;
        }
        return this.pushStack( $(selector, oDoc) );
    }

This means you can write code like:

  $('#id_of_my_iframe').frameselect('div.something_in_the_iframe')

playing with virtualenv

I was playing with virtualenv yesterday. Seems to work very well. I found it a little annoying that after sourcing the 'bin/activate' command there is no easy way to get out of the configured environment.

One quick bit of pestering spike and he came back with 'bash --init-file bin/activate'. This launches a new bash shell so you can just hit Control+D and your dumped back to where you started. Joy!

22/04/2008

conditional paster templates

I found a few things out tonight, the most important of which is the skip_template(condition=True) command that's inserted into paster contexts. This has allowed me to reduce the code in pyglons quite a bit. There is no more "minimal_project" directory, both "pyglons_minimal" and "pyglons" templates are generated from the same "default_template" directory. Also now there is a new "pyglons_game" template which includes a splash screen over the "pyglons" template.

Ive listed the files generated by each template below:

pyglons_minimal:

./minimal/__init__.py
./minimal/lib/__init__.py
./minimal/lib/base.py
./minimal/load_config.py
./minimal/main.py
./minimal/states/__init__.py
./minimal/states/main.py
./run.py
./setup.py

pyglons:

./regular/__init__.py
./regular/lib/__init__.py
./regular/lib/base.py
./regular/load_config.py
./regular/main.py
./regular/states/__init__.py
./regular/states/help.py
./regular/states/main.py
./regular/states/menu.py
./run.py
./setup.py

pyglons_game:

./game/__init__.py
./game/lib/__init__.py
./game/lib/base.py
./game/load_config.py
./game/main.py
./game/states/__init__.py
./game/states/help.py
./game/states/main.py
./game/states/menu.py
./game/states/splash.py
./run.py
./setup.py

There is also a new option "euclid=<boolean>" which if True includes pyeuclid. At the moment this file is included in the pyglons code but hopefully will be removed to an easy_install dependency when I get the chance to email the owner and get him to change his project page slightly :)

17/04/2008

pyglons 0.3 release

I've just posted an egg and .tar.gz of pyglons 0.3 on the pyglons project site.

0.3 uses a new templating system as I posted about before. I've also done some major work on the front page of the site to try and explain a little better about what the thing actually does. I'm planning on announcing this to the pyglet group soon ...

distutils upload to google code

This is more of a note to myself than a post because I did a few searches and couldnt find it quickly....

The code for uploading to googlecode from distutils/setup tools is here.

Shame pbu's publish cant do it!

Messing with default values

How to change/set default values for a function ( I wouldn't actually encourage anyone to use this! ):

>>> def a(b, c):
...     print b, c
...
>>> a()
Traceback (most recent call last):
  File "", line 1, in 
TypeError: a() takes exactly 2 arguments (0 given)
>>> a.func_defaults = (1, 2)
>>> a()
1 2

pyglons 0.3 on its way

I just commited the new templating code for pyglons based on what pylons is now doing using the latest PasteScript and tempita modules. This adds the possibility to pass options on the command line when creating your project to better describe what you do and dont want.

For example its now possible to do:

$ paster create -t pyglons my_project_name configobj=true

This will create a new project with the required code to load your config via ConfigObj. Disabling this means your project will use nothing but a simple dictionary for its initial config.

Actually you could always set options on the command line but only now do they include/exclude code from the pyglons templates. If you dont specify the option on the command line you will still get prompted for it ( unless your using --no-interactive ).

There will be more options in the future for adding code for pymunk, windoh etc in future versions of pyglons but this is essentially it for the 0.3 version I think. Ill post new downloads later today.

15/04/2008

Base class from callable

Sometimes I randomly wonder if something is possible in python the language itself ( and it often is ), this time I wondered if base classes could be callable objects... guess what?...they can!

>>> def b(c):
...     class B(object):
...             d = c
...     return B
...
>>> class A(b(1)):
...     pass
...
>>> A.d
1
>>>

14/04/2008

JSONType for SQLAlchemy

I wanted to save/load JSON to SQLAlchemy transparently, the same way you can with PickleType. Also I wanted to know how to do value conversion generally in SQLAlchemy. So here is a custom SQLAlchemy type: JSONType, it uses simplejson to do the actual JSON parsing/serialization.

import simplejson
from sqlalechmy.types import Text, MutableType, TypeDecorator

class JSONType(MutableType, TypeDecorator):
    impl = Text

    def __init__(self):
        super(JSONType, self).__init__()

    def process_bind_param(self, value, dialect):
        return simplejson.dumps(value)

    def process_result_value(self, value, dialiect):
        return simplejson.loads(value)

    def copy_value(self, value):
        return simplejson.loads(simplejson.dumps(value))

13/04/2008

windoh

Another day another project... >_< well i got to thinking i'd really to have something like Ant Tweak Bar but pure python which led on to thinking of a quick and simple UI library for pyglet... which led onto a not so simple UI library for pyglet... :)

windoh uses pyglets event.EventDispatcher class heavily to make a reasonably nice widget system. It includes a resizeable/draggable frame class and fully inheritable style support. There is also a button and a label class at the moment, I expect there will be more as time goes on.

I need to work out whats going on with pyglets batches a bit better but otherwise it all seems to work pretty ok.

Ive also checked how pylons is doing the --with- stuff so ill be adding pymunk, windoh and maybe atb-ctypes to --with- options to pyglons soon.

The source is in the pyglons repository and there is a very basic page at the pyglons wiki.

True to form there is absolutely zero documentation at the moment, you can check the test.py file, other wise may the source be with you...

12/04/2008

11/04/2008

Ant Tweak Bar ctypes module

For every 3d project iv made with python ive always written a way to use Ant Tweak Bar so I thought for historical reasons I should again for use with pyglet ( and pyglons ). So..

atb-ctypes is a ctypes wrapper around Ant Tweak Bar. It includes a pyglet event handler that can easily be pushed onto the event stack. It is of course perfectly possible to use atb-ctypes with any other windowing/event library, it does not depend on pyglet.

A very minimal sample:

import pyglet
from pyglet.gl import *

import atb
from atb.pyglet_handler import ATBEventHandler

window = pyglet.window.Window()

@window.event
def on_close():
    pyglet.app.exit()

atbhandler = ATBEventHandler(window)

if __name__ == '__main__':
    atb.init()

    for x in range(2):
        bar = atb.Bar('my bar %d' % x)
        myVar = c_float()
        COLOR4F = c_float * 4
        myColor = COLOR4F()
        bar.add_var("some float", myVar)
        bar.add_var("some color", myColor)

    def say_hello(*args):
        print args, "hello"

    bar = atb.Bar('buttons')
    bar.add_button("my button", say_hello)

    pyglet.app.run()

The code is in the pyglons googlecode repo now.

Now I have to go socialize... Its friday!! :]

10/04/2008

pyglons_minimal

pyglons now provides a second paster template: 'pyglons_minimal'. This does away with the states directory and puts just one state in main.py for you to start with. The template is aimed at people who know their way around pyglons and want to do things "their way" or just for very quick test scripts where the developer is used to how pyglons works ;)

Im about to try clutter now inside pyglet as a possible pyglons "recommended" UI library...

Automatic list of actions from a controller

A little recipe to list all the actions off a controller automatically:

class SomethingController(BaseController):

    def index(self):
        # magic to return a list of actions this controller supports 
        html = [h.link_to(f.replace('_', ' '), h.url_for(controller='something', action=f)) + "<br>"
                for f in dir(self) 
                if (not f.startswith('_') and 
                    callable(getattr(self, f)) and 
                    f not in ('index', 'start_response')
                )]
        return "\n".join(html)

09/04/2008

Google Web Engine

Can everyone just stop talking about it please?

/me bored...

pyglons in pyglons in pyglons in...

pyglons application class now also subclasses from the state class... what does this mean? With a few more tweaks to the base application, applications can now run inside other applications ( inside other applications ( inside other applications (... enough!

Why? Well its kind of cool for one! Two: it allows you to write loaders for other games ( with a little entry point magic you could create nice loaders for your other apps ). And three, because thats what you can do with pylons and wsgi in general!

Im thinking it could also be used for generic settings screens or something.. somehow..

pyglet application entry point specification anyone...?

asyncore, asynchat and pickle

A little demo I did for spike using asycore and asynchat that sends pickles around.

(yes the delimiter sucks, and i could really not of used asynchat at all... :)

easymsg.py ------------------------------------------------------------------
import asyncore, asynchat
import os, socket, string
import pickle

PORT = 8000

class EasyMsgRequest(asyncore.dispatcher):

    def __init__(self, host, obj, port=PORT):
        asyncore.dispatcher.__init__(self)
        self.obj = obj
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))

    def handle_connect(self):
        self.send(pickle.dumps(self.obj)+'\r\n')
        self.close()

    def handle_expt(self):
        self.close()

    def handle_close(self):
        self.close()


class EasyMsgChannel(asynchat.async_chat):

    def __init__(self, server, sock, addr):
        asynchat.async_chat.__init__(self, sock)
        self.set_terminator("\r\n")
        self.data = ""
        self.server = server

    def collect_incoming_data(self, data):
        self.data = self.data + data

    def found_terminator(self):
        obj = pickle.loads(self.data)
        self.route(obj)

    def route(self, obj):
        print "dont know how to route %s" % str(obj)


class EasyMsgServer(asyncore.dispatcher):

    channel_class = EasyMsgChannel

    def __init__(self, port=PORT):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(("", port))
        self.listen(5)

    def handle_accept(self):
        con, addr = self.accept()
        self.create_channel(con, addr)
    
    def create_channel(self, con, addr):
        self.channel_class(self, con, addr)


client.py ------------------------------------------------------------------
import asyncore
from easymsg import EasyMsgRequest, PORT
from code import InteractiveConsole

def sender(host, port=PORT):
    def send(obj):
        req = EasyMsgRequest(host, obj, port=port)
        asyncore.loop()
    return send

if __name__ == '__main__':
    import sys
    import readline

    if len(sys.argv) < 2:
        print "usage: client.py  []"
        sys.exit(1)

    try:
        port = int(sys.argv[2])
    except (IndexError, ValueError):
        port = PORT

    v = globals().copy()
    v['s'] = v['send'] = sender(sys.argv[1], port)
    console = InteractiveConsole(v)
    console.interact()

server.py ------------------------------------------------------------------
import asyncore
from easymsg import EasyMsgServer, PORT

if __name__ == '__main__':
    import sys
    try:
        port = int(sys.argv[1])
    except (IndexError, ValueError):
        port = PORT
    s = EasyMsgServer(port)
    print "serving..."
    asyncore.loop()

forwarder.py ------------------------------------------------------------------
from easymsg import EasyMsgServer, EasyMsgChannel, EasyMsgRequest, PORT

class ForwarderChannel(EasyMsgChannel):
    def route(self, obj):
        server = self.server
        print "forwarding %s" % str(obj)
        EasyMsgRequest(server.dest_host, obj, port=server.dest_port)


class ForwarderServer(EasyMsgServer):
    
    channel_class = ForwarderChannel

    def __init__(self, dest_host, dest_port=PORT, port=PORT):
        self.dest_host = dest_host
        self.dest_port = dest_port
        EasyMsgServer.__init__(self, port)


if __name__ == '__main__':
    import sys
    import asyncore

    try:
        dest_host = sys.argv[1]
        dest_port = int(sys.argv[2])
    except (IndexError, ValueError):
        print "usage: forwarder.py   []"
        sys.exit(1)
    try:
        port = int(sys.argv[3])
    except (IndexError, ValueError):
        port = PORT
    s = ForwarderServer(dest_host, dest_port, port)
    print "serving..."
    asyncore.loop()

Graphing Pylons SQLAlchemy model paster command

Lovely title i know...

I already posted how to make pretty graphs from your sqlalchemy models, the following is a complete paster command using that code.

import os
import sys

from paste.script.command import Command, BadCommand
from paste.script.filemaker import FileOp
from paste.deploy import loadapp, appconfig
from paste.script.pluginlib import find_egg_info_dir

import pydot

def graph_meta(meta, filename="dbgraph.jpeg"):
    d = pydot.Dot()
    nodes = {}
    
    for table in meta.tables.itervalues():
        n = nodes[table.name] = pydot.Node(table.name)
        d.add_node(n)

    for table in meta.tables.itervalues():
        for c in table.c:
            for fk in c.foreign_keys:
                e = pydot.Edge(nodes[table.name], nodes[fk.column.table.name], 
                    label=fk.column.name)
                d.add_edge(e)

    d.write_jpeg(filename)

 
def can_import(name):
    """Attempt to __import__ the specified package/module, returning True when
    succeeding, otherwise False"""
    try:
        __import__(name)
        return True
    except ImportError:
        return False


class DBGraph(Command):
    summary = 'create a graph of the model'
    parser = Command.standard_parser(simulate=True)
    parser.add_option('--output', '-o',
                      default="dbraph.jpg",
                      dest='output',
                      help="output graph to file (default: dbgraph.jpg)")

    group_name = 'pylons'

    def command(self):
        if len(self.args) == 0:
            # Assume the .ini file is ./development.ini
            config_file = 'development.ini'
            if not os.path.isfile(config_file):
                raise BadCommand('%sError: CONFIG_FILE not found at: .%s%s\n'
                                 'Please specify a CONFIG_FILE' % \
                                 (self.parser.get_usage(), os.path.sep,
                                  config_file))
        else:
            config_file = self.args.pop()

        config_name = 'config:%s' % config_file
        here_dir = os.getcwd()
        locs = dict(__name__="pylons-admin")

        wsgiapp = loadapp(config_name, relative_to=here_dir)

        # Determine the package name from the .egg-info top_level.txt.
        egg_info = find_egg_info_dir(here_dir)
        f = open(os.path.join(egg_info, 'top_level.txt'))
        packages = [l.strip() for l in f.readlines()
                    if l.strip() and not l.strip().startswith('#')]
        f.close()

        # Start the rest of our imports now that the app is loaded
        found_base = False
        for pkg_name in packages:
            # Import all objects from the base module
            base_module = pkg_name + '.lib.base'
            found_base = can_import(base_module)
            if not found_base:
                # Minimal template
                base_module = pkg_name + '.controllers'
                found_base = can_import(base_module)

            if found_base:
                break

        if not found_base:
            raise ImportError("Could not import base module. Are you sure "
                              "this is a Pylons app?")

        base = sys.modules[base_module]
        base_public = [__name for __name in dir(base) if not \
                       __name.startswith('_') or __name == '_']
        for name in base_public:
            locs[name] = getattr(base, name)
        locs.update(dict(wsgiapp=wsgiapp))

        graph_meta(locs['model'].meta, self.options.output)

If you want to use this drop it in a file called "commands.py" inside you pylons app, add the following to your setup.py:

    [paste.paster_command]
    dbgraph = .commands:DBGraph

Then run "python setup.py egg_info" and youll be able to run the command.

If your all very good ill move this to a package and post the egg here... ;)

07/04/2008

pyglons and pymunk brick game

In the pyglons repository is now a complete brick game based on pyglons! It uses pymunk (using chipmunk) for physics. The game features splash screens, menu, and fully working game with lives, scoring, paused state, etc etc ... everything a "real" game has :) The only change from the templates pyglons created has been the game state class so far ( as it should be! ) altho I think a little customisation might be on the way...

I think Ive got a few ideas to improve pyglons a bit now...

A little Pylons/Routes helper

If you have trouble remembering route names in Pylons using Routes you might like to try adding these few lines to the end of the make_map function in config/routing.py before "return map":

    if config['debug']:
        route_names = map._routenames.keys()
        route_names.sort()
        for name in route_names:
            log.debug("%s = %s" % (name, map._routenames[name].routepath))

Now when pylons starts up you will get a list of all named routes printed to the log.

06/04/2008

more pyglons

pyglons development has been going full steam ahead and now includes config file support ( including loading logging config ), much nicer event handling in the base state, the possibility to play videos in the splash screen and, as they say, much much more :)

Actually pyglons should hopefully be finished now except for bugs so I can go back to what I was making in the first place..

05/04/2008

pyglons!

I had a crazy idea yesterday evening and Ive just finished the first version. pyglons takes ideas from pylons and utilises pastes paster command to make creating pyglet games a snap.

At the moment pyglons will generate you a small working application with splash screen, menu, help and vary basic game state skeleton ready for you to add your code. I plan to use the pylons idea of not forcing the developers to use anything they dont want to whilst still maintaining some sane defaults

Example and code can be got from http://code.google.com/p/pyglons/

04/04/2008

Pylons Paster Shell Logging

For anyone else looking to see the SQL SQLAlchemy generates while using the paster shell command you need to have the following in your development.ini:

sqlalchemy.default.echo = true

and then in the shell type the following:

>>> g.sa_engine.logger.disabled = 0

Browsing the pylons source quickly I cant see why this is needed, the logging looks to be setup properly...

I found the solution here for anyone interested http://groups.google.hu/group/pylons-discuss/msg/057e70d62dead733