Kommander Kommander writings
Tips and tricks for power dialogs
- By Eric Laffoon
Tips and tricks for power dialogs in Kommander
So you've played with Kommander a little and now you want to do something cool. Maybe you're even ready to share it. So what cool things can you know about how to do this? Well you've come to the right place my friend. We have the good stuff for you. Let's start with the first thing you should know.
Grid all dialogs you intend to share!
I can't count how many new Kommander applications I've seen on kde-apps.org where the dialog wasn't gridded. The following is an example of what a really cool program looked like after I downloaded it and tried to run it. (click to enlarge) As you can see it's not very pretty. The reason it looks like this is my system has different fonts, resolution and dpi than the system it was designed on. This is pretty easy to fix. Kommander has a little grid icon on the layout toolbar and on the context menu. Simply select your dialog by clicking on empty space and click the icon. Wait! First you may need to add some spacers by clicking and dragging them into place. If you have a button for instance on the bottom of the dialog it could end up really tall and wide. Add a spacer on the left and right and the grid will auto size it. Add a spacer on top up to your content and it won't get really tall. There are other grid functions to line up groups of widgets in horizontal or vertical lines. The important thing is when you are done creating your perfect layout make sure to grid your dialog. You can find more on Qt Designer layouts here. Even though it references Designer for Qt 4 it is still pretty much the same layout wise. while doing layouts don't forget the Property Editor. You can tweak layoutSpacing and layoutMargin and you can set new defaults for the dialog under Edit::Form Settings. Below is a the same dialog I showed earlier but fixed. (click to enlarge) As you can see, gridding your dialogs makes all the difference in the world!
Don't forget the Function Browser!
Sometimes I wonder about this one. Say what you will about docs. We knew they would be lacking becuase we were always up against string freezes. We wanted to reduce the need for docs and this is how we did it. When you are editing text click the lower left button in the text editor and see what you get. On the upper left is where you select groups. Have a look at them. As you change them they change your functions below. If DCOP is selected then you can change the widget in the middle and see the functions available for it. On the right is always a short explanation and syntax for the function. By using this tool you can completely write whatever you want without knowing any syntax. That's pretty slick. I confess I use it all the time. Like you I don't feel like learning one more language as I already learned lots of them.
Interacting with your environment
Now let's say you want to launch another Kommander dialog from the one you're currently using. It's in the same directory so you just add this to a button... @exec(kmdr-executor mydialog.kmdr) and run it. Whoops! Dialog not found? what's the deal? Your dialog that called it was in that directory... but it did not set up the environment. So everything that dialog does is from home. So how do we launch it? Simple! Use this instead. @dialog(mydialog.kmdr) Works like a charm. Let's say you also wanted to pass a named variable. You can do indexed vairables like bash, but named variables are easier to work with and that's all I use. Let's pass my name. @dialog(mydialog.kmdr, name=Eric) Now you can reference it in your dialog with @global(name). But what if what you really wanted to do was to read a file you had in the same directory...and you don't know if it will be the same directory if someone else is running it? No problem. Use the global for where you are, @global(_KDDIR). Speaking of your environment, @env(HOME) is the same as $HOME in the shell.
Start up and shut down
When you look at the Kommander Text (the text edits available) for different widgets there are two special ones on the main dialog. When the editor widget combobox says Form1(EditorDialog) on the left change the combobox on the right to Initialization. This is where you can put anything you want run when the dialog initializes. Destroy is where you put anything you want for when it closes. These are useful places to populate widgets with @readSetting() and save their values with @writeSetting(). Unfortunately due to problems with converting the Qt Designer editor these are not available for wizards or MainWindow. You can use signals and slots on the creation of a widget with some degree of safety.
Ifs and Maybes
One of the nice things about Kommander is it's ability to do logic. One of the frustrations with the old parser is it's inability to do it very well. You can use
@if(true) @#do something @endifin Kommander and it works great. There are some bugs prior to 1.3.0 where you do not want to make a comparison like @if(@global(mystring) == "a string") because of a bug (use @string.length() or @string.Compare() instead) but it's otherwise great. The problem comes when you want to do something simple like else or nested ifs. Here's the simple rule, a work around and a new solution. First the new solution. The new parser handles logic without restrictions and as of 1.3.0 should now have bugs fixed and missing features like the _KDDIR global. The rule for the old macro parser is you can have one @if()...@endif and one loop or switch ending with @end in a structure. Now, even though we're doing the new parser this may get fixed for simple sanity of developers and poor confused users. Remember this has been a work in progress. Here's the work around.
@if(true) @setGlobal(somevar, "some value") @scriptObject1.execute @endifNow you can run an @if()...@endif in your script object accessing the global you declared. Here's an ugly hack for else.
@setGlobal(somevar, false) @if(true) @setGlobal(somevar, true) @endifThe way to deal with multiple conditions is to use @switch().
@switch(@global(somevar) @case("a") @# do something @case("b") @# do something else @case(*) @# this is an else condition @endOne thing we did with @switch() is to eliminate the need for break statements since 99% of the problems are forgetting them as opposed to needing multiple true conditions. Remember, you can use @if()..@endif in @case() statements or call @scriptObject.execute.
People ask all the time if there are examples for various wigets. This is easy, and it's all about whether you were a real open source buy or just another binary install type. That's right, we put them in with the source... but we didn't make them automatically install. Duh? Oops. You'll never see it, but it's there. Look in the kommander/ directory in kdewebdev or the Kommander release package. They are here kommander/examples/tutorial/. The ones in example are really old, so more of a curiousity, but in tutorial you can find dialogs that simply have only one concept illustrated for easy study. Take for instance the progressbar.kmdr demo. In it you can find the following code in the progressloop script.
@# set up a loop using loop variable named "file" @forEach(file, @exec(cd @FileSelector1.text && find * -maxdepth 0)) @# set the scale using the generated @[loopvar]_count @ProgressBar1.setMaximum(@file_count) @# show file name in statusbar @StatusBar1.setText(@file) @TreeWidget1.insertItems(@exec(cd @FileSelector1.text && find @file -name "*"), -1) @# use the generated @[loopvar]_index to update progressbar @ProgressBar1.setText(@file_index) @end
Fixing the wizard
I've been frustrated with problems with the wizard. We have spent days of programming time trying to integrate it into the editor. Suffice it to say it is not going to get substantial work during KDE 3. However one day Andras provided me with a forehead slapping sesssion for just how easy this is to work around. Note the attached dialog which you can open with the Kommander editor. You will get a security warning because it's not local. Wizard example. (right click and select open with Kommander editor) This simple DCOP string @dcop(@dcopid, KommanderIf, enableWidget(QString,bool), next, @CheckBox1.checked) enabes or disables the widget based on the value of the checkbox. This is handled by using signals and slots. Have a look. Notice we also enabled help and we use the selected(const &Qstring) signal to store the value in a widget. The widget can be hidden and referenced to provide you with the current page name which would be useful for any help functions.
Good old fashioned widget logic
This is something Kommander has done for a long time. You can reference a widget's text by using @myWidget.text but you can do something else very cool You can use a checkbox for instance and include something in the checked property an then in the unchecked property put @null. Now you can directly reference the widget by it's name alone @checkBox1 and it will work like an include. Kommander will read inline whatever is in the current state of the widget. That's why you need to put the @null in the unselected state if there is nothing in there, so you don't get an error at run time. This technique is how we used to create strings. BTW text strings are easy to output into applications that can call shell programs. Try it in quanta or open up a konsole and watch how you can @echo(text).
Hiding a dialog
Kommander has message dialog functios and you can do a lot with this, but sometimes you need just a little quick custom dialog and you don't want to set up a full blown application. This is easy enough to do. You create a dialog that you want to use, save it and then read it into a text editor and paste it into a button. Okay, I'll admit to some inconsistency as I recall having done this and having to escape the quotes with a backslash. This time I saved it to a global and it wanted to save the string exactly as it was. There is one caveat regardless. Any functions called will be interpretted in the dialog calling this so they must be escaped. That is done with an extra @ like so @@LineEdit1.text. Now when it is written out to a file the function will be written instead of the value it returns. Now for the fun part. You can actually create a temporary dialog, call it with @dialog() and then delete it all in one pass. This runs the dialog but doesn't leave it anywhere. Pretty slick. Here's an example (right click and select open with Kommander editor) that also passes named data.
For our next edition...
It may be surprising as long as this is that there are lots more little goodies I can tell you. Sure some of this is in the docs and some isn't. So we will talk about string functions, how Kommander uses text blocks, creating MainWindow applications with menus and toolbars and using the HTTP form data plugin to make our own little web form. We can also look at using some more interesting widgets like tree/detail widgets and tables. There's lots to share. In closing, I hope you enjoy Kommander and get a lot out of it. Our goal is to make it so everyone has access to developer power, but don't forget this is a large complex undertaking and we depend on user support to sponsor developers. If you are moved to contribute you can do so here.