# application.py: Application class to store properties of the application
# being created
# $Id: application.py,v 1.64 2007/08/07 12:13:44 agriggio Exp $
#
# Copyright (c) 2002-2007 Alberto Griggio <agriggio@users.sourceforge.net>
# License: MIT (see license.txt)
# THIS PROGRAM COMES WITH NO WARRANTY
import wx
from widget_properties import *
from tree import Tree, WidgetTree
import common, math, misc, os, config
import traceback, re
class FileDirDialog:
"""\
Custom class which displays a FileDialog or a DirDialog, according to the
value of the codegen_opt of its parent (instance of Application)
"""
def __init__(self, owner, parent, wildcard="All Files|*",
file_message="Choose a file", dir_message=None, style=0):
self.owner = owner
self.prev_dir = config.preferences.codegen_path
self.wildcard = wildcard
self.file_message = file_message
self.dir_message = dir_message
self.file_style = style
self.dir_style = wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON
self.parent = parent
self.value = None
def ShowModal(self):
if self.owner.codegen_opt == 0:
self.value = misc.FileSelector(
self.file_message, self.prev_dir or "",
wildcard=self.wildcard, flags=self.file_style)
else:
self.value = misc.DirSelector(
self.dir_message, self.prev_dir or "", style=self.dir_style)
if self.value:
self.prev_dir = self.value
if not os.path.isdir(self.prev_dir):
self.prev_dir = os.path.dirname(self.prev_dir)
return wx.ID_OK
return wx.ID_CANCEL
def get_value(self):
return self.value
def set_wildcard(self, wildcard):
self.wildcard = wildcard
# end of class FileDirDialog
class Application(object):
"""\
properties of the application being created
"""
def __init__(self, property_window):
self.property_window = property_window
self.notebook = wx.Notebook(self.property_window, -1)
if not misc.check_wx_version(2, 5, 2):
nb_sizer = wx.NotebookSizer(self.notebook)
self.notebook.sizer = nb_sizer
else:
self.notebook.sizer = None
self.notebook.SetAutoLayout(True)
self.notebook.Hide()
panel = wx.ScrolledWindow(
self.notebook, -1, style=wx.TAB_TRAVERSAL|wx.FULL_REPAINT_ON_RESIZE)
self.name = "app" # name of the wxApp instance to generate
self.__saved = True # if True, there are no changes to save
self.__filename = None # name of the output xml file
self.klass = "MyApp"
self.codegen_opt = 0 # if != 0, generates a separate file
# for each class
def set_codegen_opt(value):
try: opt = int(value)
except ValueError: pass
else: self.codegen_opt = opt
self.output_path = ""
self.language = 'python' # output language
def get_output_path(): return os.path.expanduser(self.output_path)
def set_output_path(value): self.output_path = value
self.is_template = False
self.use_gettext = False
def set_use_gettext(value): self.use_gettext = bool(int(value))
self.for_version = wx.VERSION_STRING[:3]
def set_for_version(value):
self.for_version = self.for_version_prop.get_str_value()
self.access_functions = {
'name': (lambda : self.name, self.set_name),
'class': (lambda : self.klass, self.set_klass),
'code_generation': (lambda : self.codegen_opt, set_codegen_opt),
'output_path': (get_output_path, set_output_path),
'language': (self.get_language, self.set_language),
'encoding': (self.get_encoding, self.set_encoding),
'use_gettext': (lambda : self.use_gettext, set_use_gettext),
'for_version': (lambda : self.for_version, set_for_version),
}
self.name_prop = TextProperty(self, "name", panel, True)
self.klass_prop = TextProperty(self, "class", panel, True)
self.encoding = self._get_default_encoding()
self.encoding_prop = TextProperty(self, 'encoding', panel)
self.use_gettext_prop = CheckBoxProperty(self, "use_gettext", panel,
"Enable gettext support")
TOP_WIN_ID = wx.NewId()
self.top_win_prop = wx.Choice(panel, TOP_WIN_ID, choices=[],
size=(1, -1))
self.top_window = '' # name of the top window of the generated app
self.codegen_prop = RadioProperty(self, "code_generation", panel,
["Single file", "Separate file for" \
" each class"])
ext = getattr(common.code_writers.get('python'),
'default_extensions', [])
wildcard = []
for e in ext:
wildcard.append('%s files (*.%s)|*.%s' % ('Python', e, e))
wildcard.append('All files|*')
dialog = FileDirDialog(self, panel, '|'.join(wildcard),
"Select output file", "Select output directory",
wx.SAVE|wx.OVERWRITE_PROMPT)
_writers = common.code_writers.keys()
columns = 3
self.codewriters_prop = RadioProperty(self, "language", panel,
_writers, columns=columns)
self.codewriters_prop.set_str_value('python')
self.for_version_prop = RadioProperty(self, "for_version", panel,
['2.4', '2.6', '2.8'], columns=3,
label="wxWidgets compatibility")
self.for_version_prop.set_str_value(self.for_version)
# ALB 2004-01-18
self.access_functions['use_new_namespace'] = (
self.get_use_old_namespace, self.set_use_old_namespace)
self.use_old_namespace_prop = CheckBoxProperty(
self, 'use_new_namespace', panel, 'Use old "from wxPython.wx"\n'
'import (python output only)')
# `overwrite' property - added 2003-07-15
self.overwrite = False
def get_overwrite(): return self.overwrite
def set_overwrite(val): self.overwrite = bool(int(val))
self.access_functions['overwrite'] = (get_overwrite, set_overwrite)
self.overwrite_prop = CheckBoxProperty(self, 'overwrite', panel,
'Overwrite existing sources')
self.outpath_prop = DialogProperty(self, "output_path", panel,
dialog)
BTN_ID = wx.NewId()
btn = wx.Button(panel, BTN_ID, "Generate code")
# layout of self.notebook
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.name_prop.panel, 0, wx.EXPAND)
sizer.Add(self.klass_prop.panel, 0, wx.EXPAND)
sizer.Add(self.encoding_prop.panel, 0, wx.EXPAND)
sizer.Add(self.use_gettext_prop.panel, 0, wx.EXPAND)
szr = wx.BoxSizer(wx.HORIZONTAL)
from widget_properties import _label_initial_width as _w
label = wx.StaticText(panel, -1, "Top window", size=(_w, -1))
label.SetToolTip(wx.ToolTip("Top window"))
szr.Add(label, 2, wx.ALL|wx.ALIGN_CENTER, 3)
szr.Add(self.top_win_prop, 5, wx.ALL|wx.ALIGN_CENTER, 3)
sizer.Add(szr, 0, wx.EXPAND)
sizer.Add(self.codegen_prop.panel, 0, wx.ALL|wx.EXPAND, 4)
sizer.Add(self.codewriters_prop.panel, 0, wx.ALL|wx.EXPAND, 4)
sizer.Add(self.for_version_prop.panel, 0, wx.ALL|wx.EXPAND, 4)
sizer.Add(self.use_old_namespace_prop.panel, 0, wx.EXPAND)
sizer.Add(self.overwrite_prop.panel, 0, wx.EXPAND)
sizer.Add(self.outpath_prop.panel, 0, wx.EXPAND)
sizer.Add(btn, 0, wx.ALL|wx.EXPAND, 5)
panel.SetAutoLayout(True)
panel.SetSizer(sizer)
sizer.Layout()
sizer.Fit(panel)
h = panel.GetSize()[1]
self.notebook.AddPage(panel, "Application")
import math
panel.SetScrollbars(1, 5, 1, int(math.ceil(h/5.0)))
wx.EVT_BUTTON(btn, BTN_ID, self.generate_code)
wx.EVT_CHOICE(self.top_win_prop, TOP_WIN_ID, self.set_top_window)
# this is here to keep the interface similar to the various widgets
# (to simplify Tree)
self.widget = None # this is always None
def set_name(self, value):
value = "%s" % value
if not re.match(self.set_name_pattern, value):
self.name_prop.set_value(self.name)
else:
self.name = value
set_name_pattern = re.compile('^[a-zA-Z]+[\w0-9-]*$')
def set_klass(self, value):
value = "%s" % value
if not re.match(self.set_klass_pattern, value):
self.klass_prop.set_value(self.klass)
else:
self.klass = value
set_klass_pattern = re.compile('^[a-zA-Z]+[\w:.0-9-]*$')
def _get_default_encoding(self):
"""\
Returns the name of the default character encoding of this machine
"""
import locale
locale.setlocale(locale.LC_ALL)
try: return locale.nl_langinfo(locale.CODESET)
except AttributeError: return 'ISO-8859-15' # this is what I use...
def get_encoding(self):
return self.encoding
def set_encoding(self, value):
try: unicode('a', value)
except LookupError, e:
wx.MessageBox(str(e), "Error", wx.OK|wx.CENTRE|wx.ICON_ERROR)
self.encoding_prop.set_value(self.encoding)
else:
self.encoding = value
def set_language(self, value):
language = self.codewriters_prop.get_str_value()
ext = getattr(common.code_writers[language], 'default_extensions', [])
wildcard = []
for e in ext:
wildcard.append('%s files (*.%s)|*.%s' % (language.capitalize(),
e, e))
wildcard.append('All files|*')
self.outpath_prop.dialog.set_wildcard('|'.join(wildcard))
# check that the new language supports all the widgets in the tree
if self.language != language:
self.language = language
self.check_codegen()
def get_language(self):
return self.language #codewriters_prop.get_str_value()
def _get_saved(self): return self.__saved
def _set_saved(self, value):
if self.__saved != value:
self.__saved = value
t = common.app_tree.get_title().strip()
if not value: common.app_tree.set_title('* ' + t)
else:
if t[0] == '*': common.app_tree.set_title(t[1:].strip())
saved = property(_get_saved, _set_saved)
def _get_filename(self): return self.__filename
def _set_filename(self, value):
if not misc.streq(self.__filename, value):
self.__filename = value
if self.__saved: flag = ' '
else: flag = '* '
if self.__filename is not None:
common.app_tree.set_title('%s(%s)' % (flag, self.__filename))
else:
common.app_tree.set_title(flag)
filename = property(_get_filename, _set_filename)
def get_top_window(self): return self.top_window
def set_top_window(self, *args):
self.top_window = self.top_win_prop.GetStringSelection()
def add_top_window(self, name):
self.top_win_prop.Append("%s" % name)
if not self.top_window:
self.top_win_prop.SetSelection(self.top_win_prop.GetCount()-1)
self.set_top_window()
def remove_top_window(self, name):
index = self.top_win_prop.FindString("%s" % name)
if index != -1:
if wx.Platform == '__WXGTK__':
choices = [ self.top_win_prop.GetString(i) for i in \
range(self.top_win_prop.GetCount()) if i != index ]
self.top_win_prop.Clear()
for c in choices:
self.top_win_prop.Append(c)
else:
self.top_win_prop.Delete(index)
def update_top_window_name(self, oldname, newname):
index = self.top_win_prop.FindString(oldname)
if index != -1:
if self.top_window == oldname:
self.top_window = newname
if wx.Platform == '__WXGTK__':
sel_index = self.top_win_prop.GetSelection()
choices = [ self.top_win_prop.GetString(i) for i in \
range(self.top_win_prop.GetCount()) ]
choices[index] = newname
self.top_win_prop.Clear()
for c in choices:
self.top_win_prop.Append(c)
self.top_win_prop.SetSelection(sel_index)
else:
self.top_win_prop.SetString(index, newname)
def reset(self):
"""\
resets the default values of the attributes of the app
"""
self.klass = "MyApp"; self.klass_prop.set_value("MyApp")
self.klass_prop.toggle_active(False)
self.name = "app"; self.name_prop.set_value("app")
self.name_prop.toggle_active(False)
self.codegen_opt = 0; self.codegen_prop.set_value(0)
self.output_path = ""; self.outpath_prop.set_value("")
# do not reset language, but call set_language anyway to update the
# wildcard of the file dialog
self.set_language(self.get_language())
self.top_window = ''
self.top_win_prop.Clear()
# ALB 2004-01-18
#self.set_use_new_namespace(True)
#self.use_new_namespace_prop.set_value(True)
self.set_use_old_namespace(False)
self.use_old_namespace_prop.set_value(False)
def show_properties(self, *args):
sizer_tmp = self.property_window.GetSizer()
child = sizer_tmp.GetChildren()[0]
w = child.GetWindow()
if w is self.notebook: return
w.Hide()
self.notebook.Reparent(self.property_window)
child.SetWindow(self.notebook)
w.Reparent(misc.hidden_property_panel)
self.notebook.Show(True)
self.property_window.Layout()
self.property_window.SetTitle('Properties - <%s>' % self.name)
try: common.app_tree.select_item(self.node)
except AttributeError: pass
def __getitem__(self, name):
return self.access_functions[name]
def generate_code(self, *args, **kwds):
preview = kwds.get('preview', False)
if not self.output_path:
return wx.MessageBox("You must specify an output file\n"
"before generating any code", "Error",
wx.OK|wx.CENTRE|wx.ICON_EXCLAMATION,
self.notebook)
if not preview and \
((self.name_prop.is_active() or self.klass_prop.is_active()) \
and self.top_win_prop.GetSelection() < 0):
return wx.MessageBox("Please select a top window "
"for the application", "Error", wx.OK |
wx.CENTRE | wx.ICON_EXCLAMATION, self.notebook)
from cStringIO import StringIO
out = StringIO()
#common.app_tree.write(out) # write the xml onto a temporary buffer
from xml_parse import CodeWriter
try:
# generate the code from the xml buffer
cw = self.get_language() #self.codewriters_prop.get_str_value()
if preview and cw == 'python': # of course cw == 'python', but...
old = common.code_writers[cw].use_new_namespace
common.code_writers[cw].use_new_namespace = True #False
overwrite = self.overwrite
self.overwrite = True
class_names = common.app_tree.write(out) # write the xml onto a
# temporary buffer
if not os.path.isabs(self.output_path) and \
self.filename is not None:
out_path = os.path.join(os.path.dirname(self.filename),
self.output_path)
else:
out_path = None
CodeWriter(common.code_writers[cw], out.getvalue(), True,
preview=preview, out_path=out_path,
class_names=class_names)
if preview and cw == 'python':
common.code_writers[cw].use_new_namespace = old
self.overwrite = overwrite
except (IOError, OSError), msg:
wx.MessageBox("Error generating code:\n%s" % msg, "Error",
wx.OK|wx.CENTRE|wx.ICON_ERROR)
except Exception, msg:
import traceback; traceback.print_exc()
wx.MessageBox("An exception occurred while generating the code "
"for the application.\n"
"This is the error message associated with it:\n"
" %s\n"
"For more details, look at the full traceback "
"on the console.\nIf you think this is a wxGlade bug,"
" please report it." % msg, "Error",
wx.OK|wx.CENTRE|wx.ICON_ERROR)
else:
if not preview:
wx.MessageBox("Code generation completed successfully",
"Information", wx.OK|wx.CENTRE|wx.ICON_INFORMATION)
def get_name(self):
if self.name_prop.is_active(): return self.name
return ''
def get_class(self):
if self.klass_prop.is_active(): return self.klass
return ''
def update_view(self, *args): pass
def is_visible(self): return True
def preview(self, widget, out_name=[None]):
if out_name[0] is None:
import warnings
warnings.filterwarnings("ignore", "tempnam", RuntimeWarning,
"application")
out_name[0] = os.tempnam(None, 'wxg') + '.py'
#print 'Temporary name:', out_name[0]
widget_class_name = widget.klass
# make a valid name for the class (this can be invalid for
# some sensible reasons...)
widget.klass = widget.klass[widget.klass.rfind('.')+1:]
widget.klass = widget.klass[widget.klass.rfind(':')+1:]
#if widget.klass == widget.base:
# ALB 2003-11-08: always randomize the class name: this is to make
# preview work even when there are multiple classes with the same name
# (which makes sense for XRC output...)
import random
widget.klass = '_%d_%s' % \
(random.randrange(10**8, 10**9), widget.klass)
self.real_output_path = self.output_path
self.output_path = out_name[0]
real_codegen_opt = self.codegen_opt
real_language = self.language
real_use_gettext = self.use_gettext
self.use_gettext = False
self.language = 'python'
self.codegen_opt = 0
overwrite = self.overwrite
self.overwrite = 0
frame = None
try:
self.generate_code(preview=True)
# dynamically import the generated module
FrameClass = misc.import_name(self.output_path, widget.klass)
if issubclass(FrameClass, wx.MDIChildFrame):
frame = wx.MDIParentFrame(None, -1, '')
child = FrameClass(frame, -1, '')
child.SetTitle('<Preview> - ' + child.GetTitle())
w, h = child.GetSize()
frame.SetClientSize((w+20, h+20))
elif not (issubclass(FrameClass, wx.Frame) or
issubclass(FrameClass, wx.Dialog)):
# the toplevel class isn't really toplevel, add a frame...
frame = wx.Frame(None, -1, widget_class_name)
if issubclass(FrameClass, wx.MenuBar):
menubar = FrameClass()
frame.SetMenuBar(menubar)
elif issubclass(FrameClass, wx.ToolBar):
toolbar = FrameClass(frame, -1)
frame.SetToolBar(toolbar)
else:
panel = FrameClass(frame, -1)
frame.Fit()
else:
frame = FrameClass(None, -1, '')
# make sure we don't get a modal dialog...
s = frame.GetWindowStyleFlag()
frame.SetWindowStyleFlag(s & ~wx.DIALOG_MODAL)
def on_close(event):
frame.Destroy()
widget.preview_widget = None
widget.preview_button.SetLabel('Preview')
wx.EVT_CLOSE(frame, on_close)
frame.SetTitle('<Preview> - %s' % frame.GetTitle())
# raise the frame
frame.CenterOnScreen()
frame.Show()
# remove the temporary file (and the .pyc/.pyo ones too)
for ext in '', 'c', 'o', '~':
name = self.output_path + ext
if os.path.isfile(name):
os.unlink(name)
except Exception, e:
#traceback.print_exc()
widget.preview_widget = None
widget.preview_button.SetLabel('Preview')
wx.MessageBox("Problem previewing gui: %s" % str(e), "Error",
wx.OK|wx.CENTRE|wx.ICON_EXCLAMATION)#, self.notebook)
# restore app state
widget.klass = widget_class_name
self.output_path = self.real_output_path
del self.real_output_path
self.codegen_opt = real_codegen_opt
self.language = real_language
self.use_gettext = real_use_gettext
self.overwrite = overwrite
return frame
def get_use_old_namespace(self):
try: return not common.code_writers['python'].use_new_namespace
except: return False
def set_use_old_namespace(self, val):
#print "set use old namespace"
try:
common.code_writers['python'].use_new_namespace = not bool(int(val))
except:
pass
def check_codegen(self, widget=None, language=None):
"""\
Checks whether widget has a suitable code generator for the given
language (default: the current active language). If not, the user is
informed with a message.
"""
if language is None: language = self.language
if widget is not None:
cname = common.class_names[widget.__class__.__name__]
if language != 'XRC':
ok = cname in common.code_writers[language].obj_builders
else:
# xrc is special...
xrcgen = common.code_writers['XRC']
ok = xrcgen.obj_builders.get(cname, None) is not \
xrcgen.NotImplementedXrcObject
if not ok:
common.message('WARNING',
'No %s code generator for %s (of type %s)'
' available',
language.capitalize(), widget.name, cname)
else:
# in this case, we check all the widgets in the tree
def check_rec(node):
if node.widget is not None:
self.check_codegen(node.widget)
if node.children:
for c in node.children:
check_rec(c)
if common.app_tree.root.children:
for c in common.app_tree.root.children:
check_rec(c)
# end of class Application
syntax highlighted by Code2HTML, v. 0.9.1