Skip navigation.
KDE Developer's Journals

rich's blog

rich's picture

New Plasma Widgets Design

This is the draft design for a new widget API for plasma, it will be appearing on techbase later, but here's what we're thinking.

General Notes

The intention of this API is to provide a very simple way for users to create
plasma applets. The API can be used both for scripting and from C++. For the
simple javascript API, this API will be all that is provided allowing us to
run untrusted applets as they will not have access to any dangerous
facilities.

From C++ or more advanced scripting APIs (such as the 'full' Javascript
bindings) you can gain access to a pointer to the underlying Qt widget
contained within the QGraphicsProxy allowing you to access all of its
methods. For most simple uses however, this should be unnecessary as you can
do most of the customisation using the Qt stylesheet facilities.

All widgets have a stylesheet, which is a string property containing a Qt
stylesheet. This allows for simple but powerful configuration of the widgets -
for example you can configure the alignment of the text in the label using the
text-align property, or the font using the font property. Using this you can
do some extremely advanced interfaces without having to learn how Qt works.

A nice side effect of the way this API is defined is that it is possible to
implement the simple widget API inside a web browser using HTML, javascript
and HTML stylesheets to provide the implementation.
API Reference

Label

Constructors

  • Label(Widget)

Properties

  • Widget parent
  • String text
  • String image
  • String stylesheet
  • QWidget nativeWidget

Note that the image property is a string and specifies the name of the
image within the plasmoid package. The image can be either a bitmap (in any
supported format) or an SVG image.

PushButton

Constructors

  • PushButton(Widget)

Properties

  • Widget parent
  • String text
  • String image
  • String stylesheet
  • QWidget nativeWidget

Signals

  • clicked()

CheckBox

  • CheckBox(Widget)

Properties

  • Widget parent
  • String text
  • String image
  • String stylesheet
  • bool checked
  • QWidget nativeWidget

Signals

  • toggled(bool)

RadioButton

  • RadioButton(Widget)

Properties

  • Widget parent
  • String text
  • String image
  • String stylesheet
  • bool checked
  • QWidget nativeWidget

Signals

  • toggled(bool)

Note that a RadioButton must exist within a ButtonGroup, the group box ensures
that only one radio button is set at any one time.

Widgets To Do

  • WebContent
  • GroupBox
  • ButtonGroup
  • ComboBox
  • LineEdit
  • TextEdit
  • Meter
  • Graph
  • Throbber
  • Simplified versions of the QGraphicsLayout classes

Non-Widgets To Do

  • URLOpenner
rich's picture

Plasma Sprint Day One

After a day's work at the Plasma sprint, there's already quite a lot of news
to report. After a lot of trawling through log files, I was able to fix the
problem that was preventing the Plasma binding plugin from loading. In the end
it was something simple (as usual) namely that the method that allows the
plugin to load was not being compiled into the module since it was missing
from the generated .pri file. Once this was fixed, it was simply a matter of
changing the name of the extension we load from 'qt.plasma' to
'org.kde.plasma' to match a fix in the generator and we had a successfully
loading set of bindings.

Lots of thanks are due to Kent Hansen of Troll Tech
for this - not only did he write the binding generator and the type system for
the Qt bindings, he also fixed up the one for plasma. Oh, and if that wasn't
enough he's even figured out the bug that has been preventing the generated
code from having anti-aliasing enabled.

In order to test that these bindings worked properly, I wanted to create a
small demo that exercised the script engine code, the Qt bindings and the
plasma bindings in the same plasmoid. My initial attempt worked ok, but didn't
look very good (though it had the advantage of requiring only a tiny amount of
code). Instead, I'll show a slightly more complex example that looks prettier.

Obviously, you can't see the animation in the screenshot, but this shows four
squares spinning around a common axis at different speeds and changing
colour. Ok, it's not the best piece of graphic design you've ever seen but it
does illustrate that the crucial facilities are in place.

In addition to the coding, we also spent some time today going over the
results Seele obtained from her interviews with some of the developers. We
were looking at the user populations we are currently supporting well, but
more importantly at those that we could potentially encourage to adopt KDE and
Plasma to consider how we can improve the facilities we offer for them
too. Before anyone gets hot under the collar, this doesn't mean ripping out
functionality to 'dumb down' the interface, in fact a lot of the suggestions
were more about how we can improve the interface consistency.

Following this discussion we did some more coding and also broke down into
smaller groups so we could continue to plan for our inevitable world
domination.

rich's picture

QtScript Web Browser

As some of you may have seen, Kent has released the QtScript binding generator on Troll Tech labs. I've been playing with the code for a bit, and as with KJSEmbed one of the first tests was to make sure you could use it to write a simple web browser. Kent recently added support for QUiLoader to the bindings and as a result, I can use the QWebView designer plugin to make things simple. The result is a basic web browser in less than 10 lines of javascript:

var loader = new QUiLoader(null);
var file = new QFile("browser.ui");
file.open(QIODevice.OpenMode(QIODevice.ReadOnly, QIODevice.Text));

var web = loader.load(file,null);
web.show();

QCoreApplication.exec();

There's also a very simple designer ui file that just contains the web view and a layout. Here's the obligatory screenshot:

rich's picture

Qtscript Binding Generator

In 4.0 plasma had support for writing applets using Qt's built in javascript interpreter QtScript, but the facilities have been fairly limited. In KDE 3.x KJSEmbed gave us reasonably complete bindings to the Qt and KDE api's, allowing us to write applications such as a web browser in 10 lines of javascript. I'm glad to say that some plans that were discussed at the KDE conference in Glasgow are now a reality and Kent has released a qtscript binding generator based on the one used for QtJambi. The result is that we will very soon have good access to the Qt API from qtscript.

I've been playing with the output of the generator for a couple of weeks now and while it still has a lot of rough edges, it's definitely a solid foundation. Even better from my point of view as a KDE developer, the Jambi generator was designed from the start to allow you to use it to write bindings for Qt based code that is not part of the main Qt API. Using it to build bindings to the plasma APIs and in future the kdelibs API is definitely feasible.

rich's picture

XML Doesn't Beep

I learnt a something new about XML today, a part of the specification that deals one of the many edge cases that exist in every file format. To illustate this, lets take a look at a few examples. Why is this XML document well-formed :

<test>X</test>

this one also well-formed:

<test>& #9;</test>

But this document isn't:

<test>& #7;</test>

Note that I've added an extra space to these examples as the blogging software used by kdedevelopers.org seems to quote the characters required to make this appear directly.

To find out why it's broken, read on...

rich's picture

Programming Styles - Why Encapsulation is a Good Thing

I was reading a blog post on beautiful code about different styles of programming earlier this week. The author was comparing the 'ruby style' of direct access to member variables with the getter/setter pattern common in Java code. His basic question was is this simply a matter of your programming background?

Here's the Java version of his example:

public class GlazeObject implements Renderable {
    private ClassStore store = new DefaultClassStore();
    private Formatter formatter = new DefaultFormatter();
    
    public void setStore(ClassStore store) {
        this.store = store;
    }
     
    public void setFormatter(Formatter formatter) {
        this.formatter = formatter;
    }
	
    public void render()
        …
    }
}

As you can see the Java version doesn't permit direct access to the members and instead uses accessor functions. For me, this is a good solution because I expect my code to evolve over time. Lets look at a couple of possible future versions of the same code:

public class GlazeObject implements Renderable {
    private ClassStore store = new DefaultClassStore();
    private Formatter formatter = new DefaultFormatter();
    private int expensive = 0x1234;

    public void setStore(ClassStore store) {
        this.store = store;
    }
     
    public void setFormatter(Formatter formatter) {
        this.formatter = formatter;
        this.expensive = doCalculation(formatter);
    }
	
    public void render()
       // Use precalculatede expensive value in fast path
        …
    }
}

In this future, we've discovered through profiling that one of the calculations we need in our render function is a performance bottle neck. To avoid the issue, we precalculate the value and simply use the cached version. Having accessor functions makes this trivial - we don't need to track down every use of the member variables and fix them as we have acheived proper encapsulation.

Of course, the future could be different. How about if we discover that the particular instances of the above object are often obscured so we don't need to render them. Well, now we might code something like this instead:

public class GlazeObject implements Renderable {
    private ClassStore store = new DefaultClassStore();
    private Formatter formatter = new DefaultFormatter();
    private bool dirty = true;
    private int expensive = 0x0000;

    public void setStore(ClassStore store) {
        this.store = store;
    }
     
    public void setFormatter(Formatter formatter) {
        this.formatter = formatter;
        this.dirty = true;
    }
	
    public void render()
       // Calculate expensive on first use then reuse it
        …
    }
}

Again, we're saved by our encapsulation. It's easy to make when the cached value needs updating because all accesses are via a single method. The downside here is that the first call to render() after the formatter is updated is slower than subsequent calls, but that is generally a good trade off.

Now all of the above examples are in Java, where we have a much more modern object format than is available to us in C++. In C++ we have to consider the dreaded binary compatibility. A C++ version might look something like this:

class GlazeObject : public Renderable
{
public:
  void setStore( ClassStore *store );
  void setFormatter( Formatter *formatter );
  void render();

private:
  class GlazeObjectPrivate *d;
};

class GlazeObjectPrivate
{
public:
  ClassStore *store;
  Formatter *formatter;
};

Here we're using the PIMPL pattern (a d pointer) to conceal the internal structure of the object from callers. This means we can change the implementation without needing to recompile things that are calling our object. Again as you see encapsulation gives us a big win. Programming is always a matter of trade offs, but frequently the good choice for the long term is to write a little bit more code and separate concerns.

rich's picture

EEE PC

I finally got my hands on an EEE PC on Friday after having waited for nearly a month for the one I ordered by mail order to arrive. I found out that 'Toys R Us' had some stock and checked that the one in manchester had some. Now I've just got the difficult decision of whether to cancel my original order or not!

The device itself feels a lot more solid than I was expecting, and the pearly white look is actually rather nice. It comes with KDE 3.4 installed, but with a cut down launcher UI. Switching from the 'easy mode' to a real KDE desktop only took a couple of minutes, so I now have a tiny KDE laptop!

I had some trouble getting it onto my home wifi network, but giving it a static IP address has meant things now work fine. Given that the forums indicate no one else has had a problem, I suspect the problem was something specific to the way my network here is set up.

The set of installed software is impressive - in fact most of the things I was planning to add in turned out to be there already! Some of the highlights I've noticed so far are:

  • Python 2.4.4
  • ssh
  • krdc (VNC client)
  • mplayer (all the video files I've tried worked first time)
  • konqueror, konsole, ksnapshot, etc. etc. Basically all of the common KDE applications.

One thing that really impressed me is that it comes with the python Qt bindings included making it very easy to write tools to overcome any missing pieces I find. One that I've started some UI mockups for is a backup application which will be my first chance to play with Qt programming in python.

Another neat feature is that the supplied media includes a tool to put a bootable rescue image onto a pen drive - this means that I can feel quite free to replace the Xandros install it comes with if I want to. Putting the image from my mandriva pen drive (or the image for the intel classmate) onto the EEE for example should be easy.

Now it's time to get started trying to put some code around the UI mockups for my backup tool, and learning the python side of Qt programming.

rich's picture

QtScript is also good for tiny things

Here's a quick example of why it's nice to have a script interpreter embedded in Qt: Plasma's KRunner has a calculator which used code borrowed from the KDE 3.x minicli. The old code started up the bc command line calculator then displayed the result - not exactly an efficient way to do things. I've just committed a change that makes it use QtScript and the code is trivial:

QString CalculatorRunner::calculate( const QString& term )
{
    QScriptEngine eng;
    QScriptValue result = eng.evaluate( term );
    return result.toString();
}

This replaces 23 lines of code and is quite a bit more powerful. Nice!

rich's picture

Plasma Scripting

I've made some decent progress in the scripting support for plasma today with the addition of the ability to access QPainter, QTimer and QFont from scripts. I've also improved a few other bits of the code. The result is that I've now been able to reimplement a functioning version of the plasma analog clock applet in Javascript. There's obviously more to be done, but I think this shows that things are progressing pretty well and along the right lines. The clock looks just like the C++ one (except I turned on the standard background so it can be distinguished as being the js one).

rich's picture

Back from Glasgow

Well, I'm back from Glasgow and have now almost recovered. The conference was great, and I'd like to thank all the organising team for their efforts. For me things were quite productive, with some nice steps forward in my QtScript code (my bindings are now dynamically loaded plugins for example) and lots of useful discussions about topics from improving the library facilities for scripts. I also managed to make a start on a plasma applet container that lets you write applets in Javascript.

Isn't it nice to be able to blog again Smiling

EDIT: I forgot to say, my slides and the video of my talk are online now if anyone wants to see them.

Syndicate content