import gtk
import gnome.ui # so we can use the GnomeCanvas later on
def play():
while gtk.events_pending():
gtk.mainiteration()
Most GTK+ commands from python don't "just happen". They generally rely on some events propagating about. Hence, it is useful to make the above "play" function. If you just call gtk.mainloop(), you'll get stuck. Avoid that by making the play() command above. This will cause everything to run that needs to run, and then return control to the python prompt.
Make it so that you can quickly get to these commands. Include the above in a file ("gtkplay.py") and put it in the python module path. Figure out what that is by doing the following:
Python 1.5.2 (#1, Aug 25 2000, 09:33:37) [GCC 2.96 20000731 (experimental)] on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import sys
>>> sys.path
['', '/usr/lib/python1.5/', '/usr/lib/python1.5/plat-linux-i386', '/usr/lib/python1.5/lib-tk', '/usr/lib/python1.5/lib-dynload', '/usr/lib/python1.5/site-packages']
That's what it says on my machine, anyways.
Put the file gtkplay.py into the path as root.
Then you can type from gtkplay import *
,
and then type play()
and anything
that you just did will propagate through the event
system and actually happen.
Keep aware of your gtkplay file; You'll probably want to add a bit more to it. Open a window with your root account active, and have it constantly open to modifying the file with your favorite editor[, emacs].
Here's another thing you can do:
def round_once():
while 1:
play()
time.sleep( .01 )
import time
import thread
thread.start_new( round_once, () )
That will create a thread that, once a second, will update the canvas. Tres Cool!
import gtk
import gnome.ui
w = gtk.GtkWindow()
w.set_title( "Just a Gnome Canvas" )
w.signal_connect( "delete_event", gtk.mainquit )
c = gnome.ui.GnomeCanvas()
w.add( c )
w.show_all()
gtk.mainloop()
Add the following to the "How to Build a Gnome Canvas from Scratch" file:
# get the root group #
r = c.root()
# add a line..! #
r.add( "line", points=(50,50, 150,50, 150,150, 50,150, 50,50 ), fill_color="red", width_pixels=5 )
# add a rectangle..! #
r.add( "rect", x1=0, y1=0, x2=50, y2=50, fill_color="blue" )
There are... some problems with the GnomeCanvasGroup.
For example, if you set the "x" attribute on it, it... Might move the contents of the GnomeCanvasGroup. (I've had plenty of experience with bits and pieces of the GnomeCanvasGroup deciding to stay behind.)
Or you might notice that the events from the GnomeCanvasGroup aren't delivered unless you are in the original region.
They way around this is to call the .move
method on the GnomeCanvasGroup, rather than setting it's
x and y attributes.
Unfortunately, .move is always relative in addressing. <sigh>
<copout> I'd fix this bug, but I'm working in Python, and it's hard for me to do a step-through. </copout>
It's probably a matter of changing assignment to x & y attributes to trigger a call to move. I wonder how people on the lists would feel about that.
I haven't fully deciphered the funky coordinate scheme, but here is what I have figured out so far..!
If "c" is your canvas, you can do the follow things:
c.set_scroll_region( 0,0, 100, 100 )
Now, one might almost be tempted to believe that the canvas now has (0,0) in the topleft corner, and (100,100) in the bottomright corner. You would be thinking... incorrectly..!
What happens frequently is that the canvas "center" becomes (0,0)-(100,100). That is, if the canvas is larger than 100x100 screen pixels. In other words, the canvas won't let you scale. Ack! How can we make the canvas scale?
For some reason, you have to do this explicitly:
c.set_pixels_per_unit( 2.0 )
Larger numbers make things bigger, Smaller numbers make things smaller.
Unfortunately, things like "width_pixels" for a line don't scale... You'll have to scale those by yourself manually, if you want it at all.
I guess they did it so that the x and y scaling factors couldn't be different. This is a limitation, if you ask me. Perhaps it would be better to manipulate the affine transformation under the root() item.
Here's a routine I've written to get close to what we want:
set_observing_region_xyscalewarning = """
size_to can only accept arguments in which x2-x1 equals y2-y1.
This is because the Gnome Canvas requires that x and y scale equally.
"""
def set_observing_region( canvas, x1,y1,x2,y2 ):
if x2-x1 != y2-y1:
raise set_observing_region_xyscalewarning
curx1,cury1,curx2,cury2 = canvas.get_allocation()
scale = ((curx2-curx1)*1.0)/(x2-x1)
canvas.set_pixels_per_unit( scale )
canvas.set_scroll_region( x1,y1, x2, y2 )
Assuming you have a widget "w
":
>>> w.get_allocation()
(0, 0, 200, 200)
>>> w.set_usize( 50, 50 ) # are you SURE you really want to?
>>>
I'm not sure what the significance of calling set_usize before/after calling show() is, if there is any at all.
def handle_canvas_mouse_click( canvasitem, event, *args ):
if event.type == GDK.BUTTON_PRESS:
(x,y) = (event.x, event.y)
print "Click at (%d, %d)" % (x, y)
canvas.signal_connect( "event", handle_canvas_mouse_click )
I don't know how, but the source code doesn't lie.
import gnome.ui
at the python prompt,
and then type "ui" alone, and python will tell you
where to find the source.
Ah-HaH! The .children() method.
You can get and set attributes using python map indexing.
Suppose we have a GnomeCanvasLine inside "line
".
We can make it dashed and blue by using map indexing:
line[ "line_style" ] = GDK.LINE_ON_OFF_DASH
line[ "fill_color" ] = "blue"
Unfortunately, it's not a complete map;
You can't call keys()
, values()
,
and items()
yet.
Just imagine; no need to look in the docs,
you could just type... line.keys()
..!
Someone will have to get the
args gtk_object_query_args( type, flags, n_args)
signature supported before we can get the
awesome power that this would grant us..!
(It needs to go in _gtkmodule.so
as well as gtk.py
)
>>> import _gtk
>>> _gtk
<module '_gtk' from '/usr/lib/python1.5/site-packages/_gtkmodule.so'>
>>> _gtk.gtk_object_set
<built-in function gtk_object_set>
>>> _gtk.gtk_object_query_args
Traceback (innermost last):
File "<stdin>", line 1, in ?
AttributeError: gtk_object_query_args
The interpreter documentation can be nice!
>>> print gnome.ui.GnomeCanvasLine.__doc__
has arguments 'points', 'fill_color', 'width_pixels',
'width_units', 'cap_style', 'join_style', 'first_arrowhead',
'last_arrowhead', 'smooth', 'spline_steps', 'arrow_shape_a',
'arrow_shape_b', 'arrow_shape_c'
>>> print gnome.ui.GnomeCanvasRE.__doc__
has arguments 'x1', 'y1', 'x2', 'y2', 'fill_color', 'outline_color',
'width_pixels', 'width_units'
Neat! If you want to customize it with your own comments, edit ui.py - it's /usr/lib/python1.5/site-packages/gnome/ui.py on my RedHat installation.
Can I have a mainloop that goes on WHILE I am still working on the program? That would be really cool... Then I could have agents- each element could have a timeout_add of it's own...
It seems like these are the two ways to do it:
Then I can have simulation objects. We could make it so that you have these things:
That would be so much fun!! You could write video games, little movies, little program sculptures, clocks, everything! It'd be so... animated!
A little program that makes birds that flap their wings.
from gtkplay import *
import random
w = gtk.GtkWindow()
w.set_title( "Just a Gnome Canvas" )
w.signal_connect( "delete_event", gtk.mainquit )
c = gnome.ui.GnomeCanvas()
w.add( c )
w.show_all()
# get the root group #
r = c.root()
# BIRD CODE
def make_bird( x, y, color ):
return { "line": r.add( "line",
points=(0,0,0,0),
fill_color=color,
width_pixels=3,
line_style=GDK.LINE_ON_OFF_DASH ),
"x": x,
"y": y,
"flapping": random.randint( 10, 90 ),
"delta": random.randint( 5, 15 ) }
def draw_bird( bird ):
(x,y) = (bird["x"],bird["y"])
(flapping,delta) = (bird["flapping"],bird["delta"])
bird[ "line" ][ "points" ] = ( x,y+flapping,
x+50,y+50,
x+75,y,
x+100,y+50,
x+150,y+flapping )
flapping = flapping + delta
if (flapping >= 100) or (flapping <= 0):
delta = -delta
bird[ "flapping" ] = flapping
bird[ "delta" ] = delta
def fly( birds ):
for bird in birds:
draw_bird( bird )
return 1
birds = [ make_bird( -50, 0, "red" ),
make_bird( 50, 30, "blue" ),
make_bird( 140, 110, "white" ) ]
gtk.timeout_add( 10, # milliseconds
fly,
birds )
gtk.timeout_add( 15000,
gtk.mainquit )
gtk.mainloop()
I'm not aware of any way of doing this using the current GtkRGB; Only of a way to do it with the deprecated Imlib. Consider yourself warned.
You have to set some things up before you start mucking with the canvas:
import GDK
import gtk
import GdkImlib
import gnome.ui
from gtkplay import * # Lion Kimbro's computer only!
w = gtk.GtkWindow()
w.set_title( "Just a Gnome Canvas" )
w.signal_connect( "delete_event", gtk.mainquit )
GdkImlib.push_visual()
c = gnome.ui.GnomeCanvas()
w.add( c )
r = c.root()
w.show_all()
imlibimage = GdkImlib.Image( "hukilau.jpeg" )
#imlibimage.render()
canvasimage = r.add( "image", image=imlibimage, x=0, y=0, width=imlibimage.rgb_width, height=imlibimage.rgb_height, anchor=gtk.ANCHOR_NW )
play() # Lion Kimbro's computer only!
I'm not really sure, but here's what I've played with so far:
import gnome.affine
rot = gnome.affine.rotate( degrees = 10 )
...(codecodecode)...
something.affine_relative( rot )
That's a great question. I too have wondered this very thing.
I've exhausted all links to the word "GnomeCanvasText" in google, and poured through samples, trying to find a working GnomeCanvasText.
I have not found a single one.
Yep! And it's really quite simple.
First, here are the basic states, and their rough meanings:
There are basic colorings:
When drawing to something, instead of saying fill_color="blue"
, you can say, fill_color_gdk=canvas[ "style" ].fg[ gtk.STATE_NORMAL ]
, and whatever is the "normal" forground color will be used.
If you need a font, you can say, font_gdk=canvas[ "style" ].font
.
Read Havoc Pennington's materials on GtkStyles in GTK+/Gnome Application Development to see more things that styles will give you.
The best thing about styles is that the user customizes your application as part of their UI customisation, and you don't have to dig up GdkFont*'s and stuff like that.
This is a reference sheet I use:
Here's a little snippet of code I frequently use for the autoconnect:
handlers = {}
for (k,v) in globals().items():
if type( v ) == type( lambda x:x ):
# function
handlers[ k ] = v
xml.signal_autoconnect( handlers )
Basically, it defaults to connecting handlers to functions of the same name.
Use text.get_chars( 0, -1 )
to read the characters.
I don't know how to set them, immediately, but here's how to add to them:
text.insert_text( str + "\n" )