CVS: /nondist/sandbox/other/jonathan README,NONE,1.1 anygui.dtd,NONE,1.1 anygui_xml.py,NONE,1.1 fooo,NONE,1.1 sample.py,NONE,1.1 sample.xml,NONE,1.1

Magnus Lie Hetland <[email protected]> Wed, 11 Sep 2002 08:56:52 -0700
Newsgroups gmane.comp.python.anygui.cvs
Message-ID <[email protected]>
Update of /cvsroot/anygui//nondist/sandbox/other/jonathan
In directory usw-pr-cvs1:/tmp/cvs-serv6567/other/jonathan

Added Files:
	README anygui.dtd anygui_xml.py fooo sample.py sample.xml 
Log Message:


--- NEW FILE: README ---
Basic XML support prototype by Jonathan Claggett.

Email describing the code (2002-09-11):


Hello Magnus,

About a month ago, I volunterred to work on a Glade-like xml reader for
anygui. Well, I've completed a working first pass at the concept and
would like to run it by you (and the rest of the Anygui developers). I
have to say that XML is quite nifty once you've wrapped your head around
it.

At this point, I've got:
 - an XML DTD which tries to mimic anygui's API,
 - a SAX handler which reads the aforementioned XML files, creating
anygui components along the way and
 - some chessy sample code which I've been using for testing.

I haven't tested the canvas component or the menu components since the
version of anygui that I installed didn't know about those components.
However, the rest of the components seem to be working.

I've attached the above files to this e-mail for your review (The
attachments are why I haven't sent this out to the anygui maling list as
a whole). Please feel free forward the code, check it in, or do whatever
else you want to do with it. The sample code is run by putting all these
files in a directory and running: python sample.py.

Enjoy,

Jonathan

--- NEW FILE: anygui.dtd ---

<!ENTITY % menu_components "menu | menucommand | menucheck | menuseparator">
<!ENTITY % components "
	button | canvas | checkbox | frame | label | listbox | radiogroup |
	textarea | textfield">
<!ENTITY % core_attributes "id ID #REQUIRED">
<!ENTITY % core_visible_attributes "
	%core_attributes;
	x CDATA #IMPLIED
	y CDATA #IMPLIED
	width CDATA #IMPLIED
	height CDATA #IMPLIED
	geometry CDATA #IMPLIED
	hmove (true|false) #IMPLIED
	vmove (true|false) #IMPLIED
	hstretch (true|false) #IMPLIED
	vstretch (true|false) #IMPLIED
	visible (true|false) #IMPLIED
	enabled (true|false) #IMPLIED">

<!ELEMENT anygui ((application | window)*, link*)>

<!ELEMENT link EMPTY>
<!ATTLIST link 
	source IDREF #IMPLIED
	handler CDATA #REQUIRED
	event CDATA #IMPLIED
	weak (true|false) #IMPLIED
	loop (true|false) #IMPLIED>

<!ELEMENT application (window*)>
<!ATTLIST application %core_attributes;>

<!ELEMENT button (#PCDATA)>
<!ATTLIST button %core_visible_attributes;>

<!ELEMENT canvas (#PCDATA)>
<!ATTLIST canvas %core_visible_attributes;>

<!ELEMENT checkbox (#PCDATA)>
<!ATTLIST checkbox %core_visible_attributes;
	on (true|false) #IMPLIED>

<!ELEMENT frame ( ( %components; )* )>
<!ATTLIST frame %core_visible_attributes;>

<!ELEMENT label (#PCDATA)>
<!ATTLIST label %core_visible_attributes;>

<!ELEMENT listbox (listitem*)>
<!ATTLIST listbox %core_visible_attributes;
	selection CDATA #IMPLIED>

<!ELEMENT listitem (#PCDATA)>
<!ATTLIST listitem
	selection CDATA #IMPLIED>

<!ELEMENT menubar ( (%menu_components;)* )>
<!ATTLIST menubar %core_attributes;
	contents CDATA #IMPLIED
	enabled (true|false) #IMPLIED>

<!ELEMENT menu ( (%menu_components;)* )>
<!ATTLIST menu %core_attributes;
	contents CDATA #IMPLIED
	enabled (true|false) #IMPLIED>

<!ELEMENT menucheck (#PCDATA)>
<!ATTLIST menucheck %core_attributes;
	enabled (true|false) #IMPLIED>

<!ELEMENT menucommand (#PCDATA)>
<!ATTLIST menucommand %core_attributes;
	enabled (true|false) #IMPLIED>

<!ELEMENT menuseparator (#PCDATA)>
<!ATTLIST menuseparator %core_attributes;
	enabled (true|false) #IMPLIED>

<!ELEMENT radiobutton (#PCDATA)>
<!ATTLIST radiobutton %core_visible_attributes;
	value CDATA #REQUIRED
	on (true|false) #IMPLIED>

<!ELEMENT radiogroup (radiobutton*)>
<!ATTLIST radiogroup %core_attributes;>

<!ELEMENT textarea (#PCDATA)>
<!ATTLIST textarea %core_visible_attributes;
	editable (true|false) #IMPLIED
	selection CDATA #IMPLIED>

<!ELEMENT textfield (#PCDATA)>
<!ATTLIST textfield %core_visible_attributes;
	editable (true|false) #IMPLIED
	selection CDATA #IMPLIED>

<!ELEMENT window ( (menubar | %components;)* )>
<!ATTLIST window %core_visible_attributes;
	title CDATA #IMPLIED>


--- NEW FILE: anygui_xml.py ---
from anygui import *
from xml.sax import saxlib, saxexts

# Anygui XML parser - used to build GUI's from xml files.
#
# The XML parser works through a SAX handler class and a supporting cast of XML
# tag handler classes. The SAX handler passes incoming tags and their
# attributes off to an appropriate XML tag handler. Here is the current class
# heirarchy for the XML tag handlers:
#
#	+ BaseTag: A trivial tag handler which remembers the parent tag.
#	  + AttrTag: Adds attribute parsing functionality.
#		- LinkTag: handler responsible for <link> tags.
#	    + ComponentTag: Adds component creation functionality.
#	      - ${COMPONENT}Tag: $COMPONENT stands for various anygui components.

class BaseTag(object):
	def __init__(self, parent, components, event_handlers):
		self.parent = parent
		self.components = components
		self.event_handlers = event_handlers
		self.component = None
	def start(self, name, attr):
		pass
	def char(self, text, start, length):
		pass
	def end(self, name):
		return self.parent

class AttrTag(BaseTag):
	def start(self, name, attr):
		self.options = {}
		self.define_attr_filters() # self.attr_filters is defined here

		# Convert attributes into options
		for key,value in attr.items():
			if self.attr_filters.has_key(key):
				filter = self.attr_filters[key]
				self.options[key.encode()] = filter(value)
			else:
				print 'Warning: Unknown option: %s (value = %s)' % (key, value)

	# These functions are used to convert xml attribute values into
	# standard anygui option values.
	def filter_string(self, str): return str
	def filter_eval(self, str): return eval(str)
	def filter_eval_list(self, str): return eval('[' + str + ']')
	def filter_boolean(self, str): return str == 'true'
	def define_attr_filters(self):
		self.attr_filters = {}

class ComponentTag(AttrTag):

	def start(self, name, attr):
		AttrTag.start(self, name, attr)

		id = self.options['id']
		del self.options['id']

		self.create_component() # self.component is defined here
		self.components[id] = self.component
		self.add_component()

	# I was lazy and defined all possible component attributes at once. As a
	# result, these attributes do not apply to all components. Since the DTD
	# knows which attributes belong to which component, this isn't really
	# shouldn't be too much of an issue.
	def define_attr_filters(self):
		self.attr_filters = {
			'id': self.filter_string,
			'x': self.filter_eval,
			'y': self.filter_eval,
			'width': self.filter_eval,
			'height': self.filter_eval,
			'geometry': self.filter_eval_list,
			'hmove': self.filter_boolean,
			'vmove': self.filter_boolean,
			'hstretch': self.filter_boolean,
			'vstretch': self.filter_boolean,
			'visible': self.filter_boolean,
			'enabled': self.filter_boolean,

			'value': self.filter_eval,
			'on': self.filter_boolean,

			'items': self.filter_eval_list,
			'selection': self.filter_eval,

			'contents': self.filter_eval_list,

			'editable': self.filter_boolean,

			'title': self.filter_string,
			}

	def add_component(self):
		if self.parent.component != None:
			self.parent.component.add(self.component)
	def create_component(self):
		pass # other classes must redfine this method!
	def char(self, text, start, length):
		self.component.set(text = text[start:start+length].strip())

class ApplicationTag(ComponentTag):
	def create_component(self):
		self.component = Application(**self.options)

class ButtonTag(ComponentTag):
	def create_component(self):
		self.component = Button(**self.options)

class CanvasTag(ComponentTag):
	def create_component(self):
		self.component = Canvas(**self.options)

class CheckBoxTag(ComponentTag):
	def create_component(self):
		self.component = CheckBox(**self.options)

class FrameTag(ComponentTag):
	def create_component(self):
		self.component = Frame(**self.options)

class LabelTag(ComponentTag):
	def create_component(self):
		self.component = Label(**self.options)

class LinkTag(AttrTag):

	def start(self, name, attr):
		AttrTag.start(self, name, attr)

		if self.options.has_key('source'):
			source = self.components[self.options['source']]
		else:
			source = 'any'

		if self.options.has_key('event'):
			event = self.options['event']
		else:
			event = 'default'

		if self.options.has_key('weak'):
			weak = self.options['weak']
		else:
			weak = 0

		if self.options.has_key('loop'):
			weak = self.options['loop']
		else:
			loop = 0

		if self.event_handlers.has_key(self.options['handler']):
			handler = self.event_handlers[self.options['handler']]
			link(source, event, handler, weak=weak, loop=loop)

	def define_attr_filters(self):
		self.attr_filters = {
			'source': self.filter_string,
			'handler': self.filter_string,
			'event': self.filter_string,
			'weak': self.filter_boolean,
			'loop': self.filter_boolean,
			}

class ListBoxTag(ComponentTag):
	def create_component(self):
		self.component = ListBox(**self.options)
		self.items = []
		self.selection = self.options.get('selection', -1)
	def end(self, name):
		self.component.items = self.items
		self.component.selection = self.selection
		return ComponentTag.end(self, name)

class ListItemTag(BaseTag):
	def char(self, text, start, length):
		self.parent.items.append(text[start:start+length].strip())

class MenuTag(ComponentTag):
	def create_component(self):
		self.component = Menu(**self.options)

class MenuBarTag(ComponentTag):
	def create_component(self):
		self.component = MenuBar(**self.options)

class MenuCheckTag(ComponentTag):
	def create_component(self):
		self.component = MenuCheck(**self.options)

class MenuCommandTag(ComponentTag):
	def create_component(self):
		self.component = MenuCommand(**self.options)

class MenuSeparatorTag(ComponentTag):
	def create_component(self):
		self.component = MenuSeparator(**self.options)

class RadioButtonTag(ComponentTag):
	def add_component(self):
		ComponentTag.add_component(self)
		# Add radio buttons to their grandparents too.
		if self.parent.parent.component != None:
			self.parent.parent.component.add(self.component)
	def create_component(self):
		self.component = RadioButton(**self.options)

class RadioGroupTag(ComponentTag):
	def add_component(self):
		pass # don't add radio groups to the component heirarchy.
	def create_component(self):
		self.component = RadioGroup(**self.options)

class TextAreaTag(ComponentTag):
	def create_component(self):
		self.component = TextArea(**self.options)

class TextFieldTag(ComponentTag):
	def create_component(self):
		self.component = TextField(**self.options)

class WindowTag(ComponentTag):
	def create_component(self):
		self.component = Window(**self.options)

class AnyguiHandler(saxlib.HandlerBase):
	"""SAX Handler for anygui xml files"""

	def __init__(self, event_handlers):
		self.components = {}
		self.tag = None

		# map the XML tag handler classes to XML tags.
		self.tag_classes = {
			'anygui': BaseTag,
			'application': ApplicationTag,
			'button': ButtonTag,
			'canvas': CanvasTag,
			'checkbox': CheckBoxTag,
			'frame': FrameTag,
			'label': LabelTag,
			'listbox': ListBoxTag,
			'listitem': ListItemTag,
			'link': LinkTag,
			'menu': MenuTag,
			'menubar': MenuBarTag,
			'menucheck': MenuCheckTag,
			'menucommand': MenuCommandTag,
			'menuseparator': MenuSeparatorTag,
			'radiogroup': RadioGroupTag,
			'radiobutton': RadioButtonTag,
			'textarea': TextAreaTag,
			'textfield': TextFieldTag,
			'window': WindowTag,
			}

		# build a dictionary out of the passed in event handlers using their
		# function names as the key.
		self.event_handlers = {}
		for func in event_handlers:
			self.event_handlers[func.func_name] = func

	def startElement(self, name, attr):
		if self.tag_classes.has_key(name):
			tag_class = self.tag_classes[name]
		else:
			print 'Warning: Unknown Tag: %s' % (name)
			tag_class = BaseTag

		self.tag = tag_class(self.tag, self.components, self.event_handlers)
		self.tag.start(name, attr)

	def endElement(self, name):
		self.tag = self.tag.end(name)
	def characters(self, text, start, length):
		self.tag.char(text, start, length)

# load an anygui XML file returning a dictionary of created components
def load(file_name, event_handlers=[]):
	handler = AnyguiHandler(event_handlers)

	parser = saxexts.XMLValParserFactory.make_parser()
	parser.setDocumentHandler(handler)

	file = open(file_name, 'r')
	parser.parseFile(file)
	file.close()

	return handler.components


--- NEW FILE: fooo ---
Hello Magnus,

About a month ago, I volunterred to work on a Glade-like xml reader for
anygui. Well, I've completed a working first pass at the concept and
would like to run it by you (and the rest of the Anygui developers). I
have to say that XML is quite nifty once you've wrapped your head around
it.

At this point, I've got:
 - an XML DTD which tries to mimic anygui's API,
 - a SAX handler which reads the aforementioned XML files, creating
anygui components along the way and
 - some chessy sample code which I've been using for testing.

I haven't tested the canvas component or the menu components since the
version of anygui that I installed didn't know about those components.
However, the rest of the components seem to be working.

I've attached the above files to this e-mail for your review (The
attachments are why I haven't sent this out to the anygui maling list as
a whole). Please feel free forward the code, check it in, or do whatever
else you want to do with it. The sample code is run by putting all these
files in a directory and running: python sample.py.

Enjoy,

Jonathan




--- NEW FILE: sample.py ---
import anygui_xml as gui

def on_button(event):
	print 'button was pressed!'

components = gui.load('sample.xml', event_handlers=[on_button])
components['app1'].run()
 

--- NEW FILE: sample.xml ---
<?xml version = "1.0"?>
<!DOCTYPE anygui SYSTEM "anygui.dtd">

<anygui>
	<application id="app1">
		<window title="demo window (this is dynamic)" id="window1">
			<button id="button1" x="20" y="70">
				Click me
			</button>
			<label id="label2">
				junk goes here
			</label>
			<button id="button2" geometry="120,70,100,30">
				Don't click me!
			</button>
			<checkbox id="checkbox1" x="120" y="100" width="200" on="true">
				Checking this does nothing...
			</checkbox>
			<radiogroup id="radiogroup1">
				<radiobutton id="rbutton1" value="1" x="10" y="150">
					selection 1
				</radiobutton>
				<radiobutton id="rbutton2" value="2" x="10" y="170" on="true">
					selection 2
				</radiobutton>
				<radiobutton id="rbutton3" value="3" geometry="10,190,200,15" >
					Last selection
				</radiobutton>
			</radiogroup>
		</window>

		<window title="a second window" id="window2" x="40" y="40">
			<textfield id="textfield1">Remove this text</textfield>
			<frame id="frame1" geometry="30,30,300,230">
				<textarea id="textarea1" width="200" height="100">
					To be or not to be. That is the question.
							- Hamlet
				</textarea>
			</frame>
		</window>

		<window id="window3" title="testing list boxes" x="80" y="80">
			<listbox id="listbox1" selection="1" geometry="10,10,200,40">
				<listitem>Text goes here</listitem>
				<listitem>blah, blah, blah</listitem>
				<listitem>yuck, blah, bllpp</listitem>
			</listbox>
		</window>
	</application>

	<link source="button1" handler="on_button" weak="true"/>
</anygui>



-------------------------------------------------------
In remembrance
www.osdn.com/911/