tutorial
2009-01-16
LearningJourney: A Zope 3 tutorial
Last year, in December, I held a 3 day Zope 3 training for about 10 Zope 2 and Python programmers. For this training I had prepared a 50 pages booklet with some tutorials and reference information (most of it written by me especially for this occasion). The feedback was pretty positive, but only time will tell if I was succesful or not with my training.
I have published the training material on Google Code in a project called z3ergo. My intention is to publish there more packages and tutorials, as they come. The beef of the tutorial was a simple but complete application called LearningJourney. My initial intention was to have an application where students could enter a log of the things that they learn, but it could be thought as a simple blog system as well. This is a full Zope 3 tutorial application, complete with a site, skin and registration process. I hope somebody can find it useful. The leaflet is published there in two languages (English and Romanian), in pdf format. You can find the source code for the docs in the LearningJourney docs folder.
The materials cover a lot of the Zope 3 landscape, as this training was for knowledgeable Zope 2/Plone programmers who were evaluating Zope 3 and also had access to other Zope 3 docs, such as Philipp's Zope 3 book. Keep this in mind when going through the docs and wonder why they're so slim.
The training itself was pretty non-formal. I've used the booklet and the sourcecode to guide them through what a Zope 3 application means. There were a lot of questions (practically, the training flow was more question based, with just a few interventions from me to guide it to a different direction), but many things that were covered mostly at surface. We didn't have any practical exercises, although I wish I was able to have them, it might have helped the students feel a little bit less lost. A more structured approach could have helped as well, who knows. It was both tiring and fun and I got to meet lots of cool people, with which I will hopefull colaborate in the future.
If there's an interest in getting more out of these docs, or you might need me as a Zope 3 trainer, feel free to contact me using this site's contact form.
2008-10-08
RichText control with wxpython: saving, loading and converting from internal XML to HTML
I tend to be an angry caveman these days (see my previous post) and this doesn't lead to too much clear thinking. I've been dealing with the wx.richtext.RichTextCtrl for the past couple of days and I think I'm now close to finishing the tasks that I set myself to do with it. I'm trying to run a bunch of richtext controls on the same page, that would increase their size and show a toolbar when focused. My first problems came from the fact that all 5 of them flashed a caret which I couldn't hide. Trying to do ctrl.GetCaret().Hide() would result in a crash. After struggling with various possible solutions - none of which worked, I've realized that I should try the latest wxpython distribution, I was even decided to compile it manually. Fortunately, the wxwidgets project offers a repository for Ubuntu and sure enough, after I've upgraded, things started to work (I still needed to hide the caret for all the richtext controls and show it when they were focused, but that's no problem).
Now, unto the next tasks: saving and loading the content of these fields. I wanted to display the content in an HtmlWindow control, so converting the content to html was one thing I had to discover how to do. First step that I've tried, saving the html and loading it through the RichTextCtrl and its closely related buffers and handlers didn't work, so I've settled for a solution that involves one RichTextHTMLHandler and one RichTextXmlHandler. To help me understand how this thing works, I've created a clean example class where I could play with this thing. I hope it helps someone, as I haven't found too much info on this on the web (the Load/SaveStream methods are not even documented anywhere).
The toolbar class is taken from my project, created with wxGlade, while the frame class is created by me, manually (actually, this is the first frame that I have created by hand). To demonstrate the loading and saving, a variable is used to keep the content (self.content). Change the text, add some formatting, hit Save and then Load. This will take the text that was saved from the richtext control (in xml format), load it in a buffer and convert it to html which is then displayed in the HtmlWindow. I've only tried this on Linux, hope it works on Windows too. Oh, and you need to supply your own icons if you want to try the code (hint: I've used /usr/share/icons/gnome/32x32/actions as source of my icons.
#!/usr/bin/env python
from StringIO import StringIO
import wx
import wx.html
import wx.richtext
class TextFormatToolBar(wx.ToolBar):
def __init__(self, *args, **kwds):
self.text_ctrl = kwds.pop('text_ctrl')
kwds["style"] = wx.TB_FLAT|wx.TB_3DBUTTONS
wx.ToolBar.__init__(self, *args, **kwds)
self.AddLabelTool(wx.ID_CUT, "Cut", wx.Bitmap("edit-cut.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "Cut selection", "")
self.AddLabelTool(wx.ID_COPY, "Copy", wx.Bitmap("edit-copy.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.AddLabelTool(wx.ID_PASTE, "Paste", wx.Bitmap("edit-paste.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.AddSeparator()
self.AddLabelTool(wx.ID_UNDO, "Undo", wx.Bitmap("edit-undo.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.AddLabelTool(wx.ID_REDO, "Redo", wx.Bitmap("edit-redo.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.AddSeparator()
self.AddLabelTool(wx.ID_BOLD, "Bold", wx.Bitmap("format-text-bold.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.AddLabelTool(wx.ID_ITALIC, "Italic", wx.Bitmap("format-text-italic.png", wx.BITMAP_TYPE_ANY),
wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.AddLabelTool(wx.ID_UNDERLINE, "Underline", wx.Bitmap("format-text-underline.png",
wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "", "")
self.Realize()
self.Bind(wx.EVT_TOOL, self.handle_bold, id=wx.ID_BOLD)
self.Bind(wx.EVT_TOOL, self.handle_italic, id=wx.ID_ITALIC)
self.Bind(wx.EVT_TOOL, self.handle_underline, id=wx.ID_UNDERLINE)
self.Bind(wx.EVT_TOOL, self.handle_paste, id=wx.ID_PASTE)
self.Bind(wx.EVT_TOOL, self.handle_copy, id=wx.ID_COPY)
self.Bind(wx.EVT_TOOL, self.handle_cut, id=wx.ID_CUT)
self.Bind(wx.EVT_TOOL, self.handle_undo, id=wx.ID_UNDO)
self.Bind(wx.EVT_TOOL, self.handle_redo, id=wx.ID_REDO)
def handle_bold(self, event):
self.text_ctrl.ApplyBoldToSelection()
def handle_italic(self, event):
self.text_ctrl.ApplyItalicToSelection()
def handle_underline(self, event):
self.text_ctrl.ApplyUnderlineToSelection()
def handle_paste(self, event):
self.text_ctrl.Paste()
def handle_copy(self, event):
self.text_ctrl.Copy()
def handle_cut(self, event):
self.text_ctrl.Cut()
def handle_undo(self, event):
self.text_ctrl.Undo()
def handle_redo(self, event):
self.text_ctrl.Redo()
class TopFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.Freeze()
sizer = wx.BoxSizer(wx.VERTICAL)
rt = wx.richtext.RichTextCtrl(self, -1)
toolbar = TextFormatToolBar(self, text_ctrl=rt)
rt.SetMinSize((300,200))
htmlwindow = wx.html.HtmlWindow(self)
htmlwindow.SetMinSize((300,200))
save_button = wx.Button(self, label="Save")
load_button = wx.Button(self, label="Load")
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
btn_sizer.Add(load_button, 0, wx.EXPAND|wx.ALL, 6)
btn_sizer.Add(save_button, 0, wx.EXPAND|wx.ALL, 6)
self.Bind(wx.EVT_BUTTON, self.handle_save, save_button)
self.Bind(wx.EVT_BUTTON, self.handle_load, load_button)
sizer.Add(toolbar, 0, wx.EXPAND|wx.ALL, 6)
sizer.Add(rt, 1, wx.EXPAND|wx.ALL, 6)
sizer.Add(htmlwindow, 1, wx.EXPAND|wx.ALL, 6)
sizer.Add(btn_sizer)
self.SetSizer(sizer)
sizer.Fit(self)
self.Thaw()
self.rt = rt
self.htmlwindow = htmlwindow
def handle_save(self, event):
out = StringIO()
handler = wx.richtext.RichTextXMLHandler()
buffer = self.rt.GetBuffer()
handler.SaveStream(buffer, out)
out.seek(0)
self.content = out.read()
def handle_load(self, event):
out = StringIO()
handler = wx.richtext.RichTextXMLHandler()
buffer = self.rt.GetBuffer()
buffer.AddHandler(handler)
out.write(self.content)
out.seek(0)
handler.LoadStream(buffer, out)
self.rt.Refresh()
cio = StringIO()
cio.write(self.content)
cio.seek(0)
cout = StringIO()
xmlhandler = wx.richtext.RichTextXMLHandler()
htmlhandler = wx.richtext.RichTextHTMLHandler()
newbuff = wx.richtext.RichTextBuffer()
newbuff.AddHandler(htmlhandler)
xmlhandler.LoadStream(newbuff, cio) #load xml into buffer
newbuff.SaveStream(cout, wx.richtext.RICHTEXT_TYPE_HTML)
cout.seek(0)
self.htmlwindow.SetPage(cout.read())
class MyApp(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
frame = TopFrame(None, - 1, "")
self.SetTopWindow(frame)
frame.Show()
return 1
def start():
app = MyApp(0)
app.MainLoop()
if __name__ == "__main__":
start()