CTK dialogs are highly customizable.
') self += CTK.RawHTML ('By default, CTK dialogs are:
') defaults = CTK.List() defaults.Add (CTK.RawHTML('Modal')) defaults.Add (CTK.RawHTML('Not resizable')) defaults.Add (CTK.RawHTML('Not opened automatically')) defaults.Add (CTK.RawHTML('Not draggabale')) defaults.Add (CTK.RawHTML('Rendered on the center of the screen')) self += CTK.Indenter (defaults) class default: def __call__ (self): opened = CTK.Dialog ({'title': _('Automatically opened dialog'), 'width': 450, 'autoOpen': True}) opened += content_box() opened.AddButton (_('Close'), "close") closed = CTK.Dialog ({'title': _('Manually opened dialog'), 'width': 450}) closed += content_box() closed.AddButton (_('Close'), "close") button = CTK.Button('Open dialog') button.bind('click', closed.JS_to_show()) page = CTK.Page () page += opened page += closed page += button return page.Render() CTK.publish ('', default) CTK.run (port=8000) ---- Tabs ^^^^ As was mentioned before, you can add widgets to other widgets. Thanks to this approach, using tabs is also intuitive. Just instantiate a CTK.Tab object, and add as many tabs as you want. The contents of each tab has to be a widget: an Image, a Box, etc. ----- # tabs.py import CTK IMAGES = ['on', 'off', 'loading', 'tick', 'del'] class default: def __call__ (self): page = CTK.Page() tabs = CTK.Tab() for img in IMAGES: tabs.Add (img.capitalize(), CTK.ImageStock(img)) page += tabs return page.Render() CTK.publish ('', default) CTK.run (port=8000) ----- Refreshable elements ^^^^^^^^^^^^^^^^^^^^ The CTK.Refreshable class allows the creation of objects that can be rendered asynchronously in response to a given event. Normally every CTK widget has a unique identifier that is generated automatically, but since the contents of a Refreshable have to be written into a specific container that you'll need to know in advance, this class demands an explicit 'id' be given on instantiation. ---- # refresh.py import CTK import time class Default: class Content (CTK.Box): def __init__ (self, refresh): CTK.Box.__init__ (self) button = CTK.Button ('Refresh') button.bind ('click', refresh.JS_to_refresh()) self += CTK.RawHTML('Rendered on %s.
' %(time.ctime())) self += button def __call__ (self): r1 = CTK.Refreshable ({'id': 'r1'}) r1.register (lambda: self.Content(r1).Render()) r2 = CTK.Refreshable ({'id': 'r2'}) r2.register (lambda: self.Content(r2).Render()) page = CTK.Page () page += r1 page += r2 return page.Render() CTK.publish ('', Default) CTK.run (port=8000) ---- So, what does it do? It instantiates two CTK.Refreshable elements, each with its unique identifier, and each one is refreshed when the button is clicked. Easy, right? Remember that identifiers must be unique for every DOM element/node. Should you mistakenly provide the same identifier for both Refreshable objects, clicking on anyone of the buttons would probably update the same element (and only one of them). Advanced examples ~~~~~~~~~~~~~~~~~ If the basic examples don't seem enough for you, there are lots of more complex examples at your disposal. The best way to dive into CTK is reading the code of Cherokee-Admin, and checking the CTK code itself from time to time. Most of CTK has been written to provide the specific needs of Cherokee Admin, so most likely you'll find examples as complex as you want that will help you fully understand how things are working. Some considerations ~~~~~~~~~~~~~~~~~~~ So far you've seen some very basic examples. Before you begin hacking on Cherokee-Admin, you'll probably need to know some more about other CTK elements. CTK.post ^^^^^^^^ This is used to access every element submitted using the POST method. You've seen how it works on some of the previous examples. The most useful methods in this class involve accessing reading and popping values and keys. Elements can also be accessed using dictionary-like notation. In the examples above, both: ----- CTK.post['show_delay'] ----- and ----- CTK.get_val('show_delay') ----- Would return the same value, although the second appearance lets you specify a default value as optional argument. CTK.cfg ^^^^^^^ The class CTK.Config seamlessly handles the Cherokee configuration tree. With it you can read and write a configuration file, parse and serialize the configuration tree, clone configuration elements, and manipulate branches ands leaves at will. The class is instanced as soon as Cherokee-Admin loads, and it can be accessed as CTK.cfg. Just like before, it can be accessed and manipulated using its many methods, and dictionary-like syntax can also be used. With little doubt, this is the most manipulated object throughout the whole code-base. Study CTK.Config if you are interested in the full details. The most used methods are the ones involving setting and gettings entry values. This is done just like in a dictionary. Among the other methods available, probably the most used are: . pop: to pop elements from the configuration tree. . keys: access the list of subnodes of any given node . get_val: retrieve the value of a configuration key. CTK.cfg.get_val('server!timeout') would be equivalent to CTK.cfg['server!timeout'] . apply_chunk: to apply a configuration chunk directly to the configuration tree. This is a lifesaver when dealing with wizards, for examples, where you have tamplates as large chunks that only require some customization. . normalize: also very useful when dealing with wizards, since it can be used to renumber configuration entries. It is recommended to always normalize the configuration after playing around with it. . get_next_entry_prefix: provided a path, it will yield the next entry in the sequence of currenlty existing elements.This is very used when having to add any new element to the configuration, such as a new rule, a new virtual server, a new information source, and so on. The tmp configuration node ^^^^^^^^^^^^^^^^^^^^^^^^^^ Whenever the configuration three is altered, a flag is set indicating the configuration tree has been changed. In turn, the 'Save' button on the admin interface becomes clickable, and using it will dump the configuration tree to the configuration file that is being used at the moment. There is one exception to this rule: the *tmp* configuration node. Everything hanging from that branch is ignored when loading and saving the configuration tree. It is used extensively as a temporary repository. For example, on multi-stage wizards, it is used to store the intermediate values gathered along all the different stages. Every configuration entry hanging from this node can be set and retrieved exactly like the rest, but nothing will be saved to disk. Config related widgets ^^^^^^^^^^^^^^^^^^^^^^ You have been some Widgets being used in the example about form submission. What you haven't yet seen are all the variants specially conceived to interact with CTK.Config. Many widgets have a variant with a name ending in *Cfg*. Normally, you would process form submissions in custom commit functions that would extract the required data from CTK.post and inject it into CTK.cfg. The *Cfg* widgets are bound to the configuration tree itself, so the rest of the code can be simplified by avoiding the need of duplicating logic for such repetitive tasks. These widgets are instantiated with a configuration entry, and their values are retrieved from the configuration tree itself. Once submitted, one call to the method CTK.cfg_apply_post will set the new values in the configuration tree as well. HTTP responses ^^^^^^^^^^^^^^ Complex things can be achieved through the use of custom HTTP responses. Of course, CTK provides a convenient way to do this. For example, a custom commit function could decide that the user has to be redirected to another location. Making it return a CTK.HTTP_Redir response would redirect to whatever location was specified. Any response can be issued, just dig into the class and try it out if needed. CTK.url_request ^^^^^^^^^^^^^^^ Sometimes passing parameters through the URL migh come in handy. Just access the CTK.url_request property and you'll be able to parse to your heart's contempt. CTK.cookie ^^^^^^^^^^ Basic cookie support is provided through this object. Cookies can be set an read using the well known bracket-syntax: ----- CTK.cookie['user'] = 'my_user' ----- and ----- user = CTK.cookie['user'] ----- Final notes ~~~~~~~~~~~ Although CTK was conceived as a tool to help in the development of Cherokee-Admin, it has grown in complexity and flexibility to the point that it is an Open Source project all by itself. Detailing every last piece of it is far beyond the scope of this introductory tutorial. You can easily achieve relatively complex tasks using CTK with very little effort. Review the API, check the provided examples, and dig into Cherokee-Admin. As you can see, once you get the hang of it it is really not complicated, and you can always turn to our development mailing list for help.