Development/Tutorials/Plasma4/JavaScript/TrainingSession

From KDE TechBase

This is the transcript of a training session on 2010-02-13 in #plasma-training on IRC for the Plasma Jam Session Contest

The original content can be found at: http://plasma.kde.org/media/javascript_plasmoid_training_sessions_13022010.txt


<aseigo> good morning :)

<aseigo> welcome to today's Javascript Plasmoid training session!: thanks for coming out

<aseigo> before we get started, i'd like to point out that we also have #plasma-training-q where ou can ask questions during the session

<aseigo> and we'll take the questions from there and answer them here as it fits in

<aseigo> we'll start in about 3 minutes, i just need to grab something to drink and then we'll get underway!

<aseigo> alirght! welcome to today's Javascript Plasmoid training session!: thanks for coming out

<aseigo> first, i'd like to give you some pointers to reference materials you an use after this session to continue your exploration. we'll be covering a LOT of ground today, so a set of references will be very useful

<aseigo> the Javascript Plasmoid API is documented on KDE's Techbase here: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API

<aseigo> it's pretty complete, though not all of the methods, properties, etc. are fully documented. they are all, at least, listed and we're adding to it on a continual basis. (it's a wiki, you are more than welcome to help us flesh it out :)

<aseigo> we also have a set of tutorials on Techbase for Javascript components: http://techbase.kde.org/Development/Tutorials/Plasma#Plasma_Programming_with_JavaScript

<aseigo> and finally, there is the KDE Examles module, which you can can grab with: svn co svn://anonsvn.kde.org/home/kde/trunk/KDE/kdeexamples

<aseigo> n there is the plasma/javascript/ directory which contains a number of example plasmoids

<aseigo> in there is the plasma/javascript/ directory which contains a number of example plasmoids

<aseigo> i highly recommend that you bookmark / download those things for later reference

<aseigo> (note that in plasmate, the plasma add-ons creator we're working on and just made a first alpha release of, the documentation browser will take you to those techbase pages as well)

<aseigo> so, prerequisites for today's session:

<aseigo> some knowledge of Javascript is good, and that's about it. you don't need to know anything about Qt itself or KDE develpment either

<aseigo> all you need is KDE SC 4.4, a text editor and your sense of adventure :)

<aseigo> first questions about the reference materials before we get started:

<aseigo> Siju asks: "is the dokument http://techbase.kde.org/Development/Tutorials/Plasma#Plasma_Programming_with_JavaScript compatible to kde4.3 even? and even kde3.5?"

<aseigo> Answer: No. KDE 3.5 had no Javascript scripting of any sort for the workspace; it is somewhat compatible with 4.3, but 4.4 saw a number of changes and is the first release where the Javscript API will not be changing in an incompatible fashion going forward

<aseigo> comawhite asks: is plasmate available for openSUSE yet?

<aseigo> Answer: I don't think it's been packaged for any distribution yet; we just released 0.1-alpha1, the first early development release, two days ago :)

<aseigo> ok, so the first thing to understand is "what are we making exactly?"

<aseigo> in Plasma, "everything" (or at least nearly everything) is a Widget

<aseigo> there are both widgets that use the Plasma API directly (Plasmoids) and other widgets such as Google Gadget, Superkaramba, E17 Edje, etc.

<aseigo> we're going to be creating Plasmoids, of course, using Javscript; but regardless of the API or language used, each widget is a kind of "mini-application" that can be placed almost anywhere in a Plasma application

<aseigo> most of the time, this is plasma-desktop or plasma-netbook, but amarok and a few others are using Plasmoids as well (Skrooge will be using them in an upcoming release, for instance)

<aseigo> small update on the packaging front: [08:16] <physos_> http://download.opensuse.org/repositories/KDE:/KDE4:/Playground/openSUSE_11.2/i586/plasmate-0.1alpha1-1.1.i586.rpm

<aseigo> thanks physos_! :)

<aseigo> the important thing to remember is that our Plasmoid doesn't really have to care about anything other than itself, but it will be sharing a space with other widgets

<aseigo> we'll see more of this in a bit when we talk about Form Factors and Constraints

<aseigo> first, though, let's look at how a Plasmoid is physically put together on disk. this is important so we know how to build one.

<aseigo> just as our Plasmoid is its own little mini-application when run, all of its files and data are also kept in a separate location on disk dedicated to our Plasmoid

<aseigo> and when the Plasmoid is stored or sent over the network, it's usually kept in a zip compressed file. but we will be working on them locally as a directory structure, uncompressed

<aseigo> we call this bundle of files a Package (not very imaginative or suprising ;)

<aseigo> the structure of Packages are documented here: http://techbase.kde.org/Projects/Plasma/Package

<aseigo> we use an application called `plasmapkg` to install, update and remove packages, and we will be using another application called `plasmoidviewer` to preview our Plasmoids even before we install them or zip them up into an archive

<aseigo> Plasmoid packages are divided into two main parts: a metadata.desktop file which tells Plasma all about the Plasmoid (what it's called, who made it, etc), and the contents/ directory which holds all of the code, images, etc.

<aseigo> here's an examle metadata.desktop file: http://websvn.kde.org/*checkout*/trunk/KDE/kdeexamples/plasma/javascript/plasmoids/tiger/metadata.desktop

<aseigo> Vamp898: questions in #plasma-training-q please :)

<aseigo> the most important things in that file are the Name, Type, ServiceTypes, and all the X-KDE / X-Plasma entries at the bottom

<aseigo> (all of the Comment[xx] and Name[xx] entries are translations. you can ignore them for now, but people who end up translating your widget for you will be adding such entries.)

<aseigo> the Type and ServiceTypes entries are _always_ the same for all Plasmoids, and when we're writing using the Javascript API we _always_ have this: X-Plasma-API=javascript

<aseigo> that tells Plasma which of the several APIs this Plasmoid is written in (other possible values are python, ruby, googlegadgets, dashboard (mac os), superkaramba, edje ..)

<aseigo> the X-KDE-PluginInfo entries are pretty self-explanatory with the exception perhaps of X-KDE-PluginInfo-Name

<aseigo> X-KDE-PluginInfo-Name defines what this Plasmoid will be called by the Plasma application internally

<aseigo> it isn't seen by the user ever and it _must_ be unique across all other widgets installed and in use

<aseigo> so we highly recommend using a naming scheme that will ensure uniquenes; you could do the "java style" reverse domain thing (org.foobar.widgetname), or prepend a unique name/d (aseigo_widgetname)

<aseigo> the one other quirk with the metadata.desktop file is the optional Icon= entry

<aseigo> in a Plasmoid package, that can refer either to an icon, by name, that is installed as part of a normal desktop icon theme, or it can be the name of a png file that is included in your package

<aseigo> if the icon png is in your package, it will be installed along with the package itself when the widget is installed for usage

<aseigo> once we have our metadata.desktop file, we move on to the interesting stuff: the contents/ directory

<aseigo> but first a couple of questions from #plasma-training-q

<aseigo> Questions from Vamp898: so is a plasmoid in compressed format as long i dont use it?

<aseigo> Answer: yes, Plasmoids are shared and stored as compressed zip files. since they will be made up of several files as we will see, this is convenient

<aseigo> Question from amrit: can you plz give an example of any plasmoid?? screenshots... ??


<aseigo> Answer: if you are running Plasma Desktop or Plasma Netbook or Amarok 2, you've see them. the taskbar, application launcher (e.g. kickoff), clocks, systm tray, pager in Plasma Desktop, all of the items in the Context view of Amarok ... those are ALL plasmoids

<aseigo> you can also see more examples here: http://kde-look.org/index.php?xcontentmode=70x77x78&PHPSESSID=54c0310c54f0b53a128f3ca4fb6dc7f4

<aseigo> Question from Vamp898: are plasmoids only written in JavaScript?

<aseigo> Answer: No, they can be written in C++, XHTML/Javascript, Ruby and Python; Javascript is the most portable and secure, however

<aseigo> ok, back to our package, and the contents/dir

<aseigo> all of our scripts, image files, configuration descriptions, etc. will be under the contents/ directory

<aseigo> inside of contents/, we have a small number of important directories: code, images, ui, config and locale

<aseigo> contents/code/ is where we'll put all of our scripts. strictly speaking, our main script can be anywhere in the package, but when we use the include() statement to pull in other javascript files, those must be in contents/code/

<aseigo> contents/images is where we'll put any SVGs, pngs, jpegs, etc we wish to use in our widget

<aseigo> contents/ui/ is where we will put Qt Designer files for our configuration dialog

<aseigo> contents/config/ is where our configuration files go that define what configuration options are available

<aseigo> and finally, contents/locale/ is where we put all of our translations so that our widget can be used in multiple languages

<aseigo> you can see several examples of such packages in the KDE Examples module i posted a link to at the begining

<aseigo> so let's dive straight in and get writing the main script file

<aseigo> when our Plasmoid is started the first thing that happens is that the main script is loaded and executed

<aseigo> remember that the user can create as many different instances of the same widget as they want, and each time they do, the main script will be started again

<aseigo> by default, the main script file is called contents/code/main

<aseigo> if we wish, we can change that by putting an entry like this in our metadata.desktop:

<aseigo> since we're using Javascript, we probably want some nice syntax highlighting in our text editor, and most text editors look to the file suffix, so let's do this:

<aseigo> X-Plasma-MainScript=code/main.js

<aseigo> with that in our metadata.desktop file, Plasma will try and load contents/code/main.js as the main script ..

<aseigo> this would work just as well: X-Plasma-MainScript=code/foobar.js

<aseigo> as would: X-Plasma-MainScript=some/strange/directory/foobar.js

<aseigo> in other words, we can put anything there, as long as it points to somewhere in our package. but code/main.js is pretty typical

<aseigo> this main script gets loaded in its entirety when the Plasmoid is creatd, so this is where we will do all of the initial set up of the Plasmoid

<aseigo> here's an example main script: http://websvn.kde.org/*checkout*/trunk/KDE/kdeexamples/plasma/javascript/plasmoids/fileOperations/contents/code/main.js

<aseigo> a number of functions are created in that file, but if we skip down near the bottom we can see some pushbuttons being created and add to a layout

<aseigo> so this brings us to the first "fun" part of writing our widget, aka "something we can actually SEE!" :)

<aseigo> and that's user interface elements and layouts for them.

<aseigo> we'll be looking at how to create things like pushbuttons and arrange them in layouts so that they will resize along with the plasmoid; then we'll be looking at how to paint directly onto the Plasmoid without using UI elements

<aseigo> Question from naproxeno: X-Plasma-MainScript only accepts relative paths, isn't it?

<aseigo> Answer: yes, the path is relative to contents/, and it must point inside your package. A path like ../../../trying/to/escape/my/package will be rejected. Plasmoids are, for security reasons, kept inside their Packages

<aseigo> (and since they may be installed anywhere on disk, we never use absolute paths in our code, as we will see later on)

<aseigo> ok, so user interface elements!

<aseigo> we offer a large number of stock UI elements, which are listed here: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#UI_Element_Gallery

<aseigo> these include push buttons, checkboxes, radio buttons, tab widgets, group boxes, text edits, text browsers even a video widget (also plays audio) and a web browser ; quite a selection, and they are all properly themed and fit for use in a Plasma application

<aseigo> all of the widgets have some common properties: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Common_Properties

<aseigo> you will never have need for many of those, but a few are quite useful

<aseigo> this includes enabling/disabling a UI element. for example, if we have a simple message taking app, we may have a LineEdit to type the message into and a PushButton to "send" the message

<aseigo> we may want to disable the button so the user can't click on it if the LineEdit is empty

<aseigo> widgets can be easily enabled/disabled like this:

<aseigo> var myPushButton = new PushButton

<aseigo> myPushButton.enabled = false

<aseigo> we can also check if it is enabled with:

<aseigo> if (myPushButton.enabled)

<aseigo> this is a common pattern in the Javascript API: most of the set/get functions are provided as read/write properties

<aseigo> this means you can use these properties as if they were simple variables; assignment, for instance, works to set the value for properties that are writable

<aseigo> this keeps the API pretty lean :)

<aseigo> the way to create a widget is with the 'new' operator:

<aseigo> var myPushButton = new PushButton

<aseigo> and we have a new button in our widget

<aseigo> when a UI element (or anything else, for that matter) is 'new'd without any arguments, our Plasmoid will automatically be used as the parent for it

<aseigo> so in this case the pushbutton will belong to and appear in our Plasmoid

<aseigo> if we had a Frame (which just draws a nice little containing frame around other widgets) and we wanted the PushButton associated with it instead, we coudl do:

<aseigo> var myFrame = new Frame; var myPushButton = new PushButton(myFrame)

<aseigo> in this case, the Frame "belongs to" the Plasmoid, and the button to the frame

<aseigo> the nice thing here is that usually we don't have to care about who owns what or how to make a UI element appear in the plasmoid: we just go around happily creating new items as we wish

<aseigo> to ensure that our wiget expands/shrinks nicely when we have widgets in them, we need to use Layouts

<aseigo> there are three kinds of layouts supported: Linear, Grid and Anchor

<aseigo> Linear layouts put widgets in vertical or horizontal rows

<aseigo> Grid layouts are tabular, with rows and colums

<aseigo> and Anchor layouts let us define "anchor points" such as "the top left corner" in which to place things

<aseigo> in the LinearLayout and GridLayout cases we use addItem to add widgets to our layouts

<aseigo> var layout = new LinearLayout

<aseigo> layout.addItem(myPushButton)

<aseigo> with that bit of code, when our Plasmoid is expanded, the button will grow as well because of the layout

<aseigo> we can also add multiple items to our layout:

<aseigo> layout.addItem(myOtherPushButton)

<aseigo> and if we wanted the layout to be horizontal instead of vertical:

<aseigo> layout.orientation = QtHorizontal

<aseigo> we can switch the orientation any time we want and the Plasmoid contents will shift around automatically for us

<aseigo> note that if you are ever positioning widgets manually with "myWidget.pos = " calls, you are probably Doing It Wrong(tm)

<aseigo> you should instead be using layouts

<aseigo> all of the widgets API is documented here: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#User_Interface_Elements

<aseigo> some quick questions:

<aseigo> Question from f3lip3: Can you create classes? is this similar to web browser's js?

<aseigo> Answer: Yes, you can create classes, methods and split your code out across multiple files even. We'll be covering these things a bit later

<aseigo> Ok, continuing...

<aseigo> UI elements aren't the only way to get something useful onto your Plasmoid's interface, though

<aseigo> you can also paint directly to your Plasmoid

<aseigo> painting supports things like drawing images, lines, rects, text, etc. using different colors, line widths, etc.

<aseigo> if you are familiar with other canvases, like the html 5 canvas, it's not much different

<aseigo> the way we set this up is we define the plasmoid.paintInterface function

<aseigo> and here we get our first taste of some fun thing we can do with Javascript Plasmoids: overriding and otherwise using the global plasmoid object

<aseigo> when our Plasmoid starts, there is a global obect called "plasmoid" which gives us access to quite a few interesting and useful bits

<aseigo> the plasmoid global object is documented in detail here: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#The_Global_plasmoid_Object

<aseigo> the plasmoid object includes a number of properties, but also some functions that we can define ourselves and which will be called at the "right time"

<aseigo> in the case of painting, that's the plasmoid.paintInterface function. if we implement this function, it will get called whenever the Plasmoid needs to be repainted

<aseigo> you can see an example of paintInterface here: http://websvn.kde.org/*checkout*/trunk/KDE/kdeexamples/plasma/javascript/plasmoids/tiger/contents/code/main

<aseigo> it's very simple, as you can see, we simple do:

<aseigo> plasmoid.paintInterface = function() {.. put some code here to do some painting ... }

<aseigo> now, we NEVER call plasmoid.paintInterface ourselves

<aseigo> instead, we call plasmoid.update() and the canvas will schedule an update and call paintInterface for us

<aseigo> we can also update just a portion of the Plasmoid using plasmoid.update(someRect)

<aseigo> in fact, plasmoid.update() is equivelent to calling plasmoid.update(plasmoid.rect())

<aseigo> in that example main script above from the tiger Plasmoid, there was use of an Svg file in the paintInterface

<aseigo> there was also use of a pixmap file

<aseigo> using such image data files is really, really simple with the Javascript API: you simply refer to them by name!

<aseigo> and so we see lines like this: svg = new PlasmaSvg('tiger')

<aseigo> PlasmaSvg is a painting-centric SVG class. what this means is that it provides all the useful bits you need to load an SVG file from disk and start putting it on the screen

<aseigo> documentation: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Svg

<aseigo> you'll notice that nothing more than "tiger" is given to the PlasmaSvg constructor

<aseigo> what happens is this: a file called tiger.svg or tiger.svgz is looked for in our package's contents/images/ directory

<aseigo> (side note: a .svgz file is a gzip compressed SVG, can really save on disk space and loading time!)

<aseigo> if it doesn't exist there, then it looks for an SVG by that name in the Desktop Theme

<aseigo> so if we wanted to use the standard calendar SVG that is used elsewhere in plasma, we could do this:

<aseigo> var svg = new PlasmaSvg("calendar")

<aseigo> and it would load it from the desktop theme for us

<aseigo> the benefit of this is that it would then match _whatever theme the user has selected_

<aseigo> it also means we don't have to draw too many svg's for ourselves :)

<aseigo> desktop themes and their contents are documented here: http://techbase.kde.org/Projects/Plasma/Theme

<aseigo> a special kind of SVG is what we call a "frame svg". this is used to paint, well, frames around things

<aseigo> the background used in most/many Plasmoids is a frame svg, for instance

<aseigo> they have tops, bottoms, sides and you can paint the entire frame at once. very handy.

<aseigo> FrameSvg objects have all the same properties and functions as a regular Svg, but with one addition

<aseigo> they have a resizeFrame() function that will change the size of the frame pixmap it will paint

<aseigo> speaking of resizing, when an Svg object is created, it will load the SVG at its "natural" size, which is whatever the artist used when drawing it in Inscape or Karbon or Illustrator or whatever

<aseigo> this may not be what you want :)

<aseigo> so you can change the size of the SVG by calling resize() on it

<aseigo> this will change the size of the output of the svg the _next time_ it is used to paint with

<aseigo> sometimes people run into the problem where the svg only paints part of the image and it's REALLY REALLY big. that's because they forgot to call resize on it

<aseigo> in the tiger example above, you'll note that it does resize the svg in the paintInterface function, like this:

<aseigo> rect = plasmoid.rect()

<aseigo> svg.resize(rect.width, rect.height)

<aseigo> this is actually not the best way to do it, and we'll come back to that in a moment :)

<aseigo> in the same tiger example, we can also see usage of a QPixmap object

<aseigo> it's quite similar to the Svg in how we load it: by name, and it will be looked for in contents/images/

<aseigo> to paint it, though, we need to use the painter object's drawPixmap function

<aseigo> the painter object lets us paint things like text (drawText), images (drawImage / drawPixmap), rects, lines and more

<aseigo> again, though, most often you'll be using stock user interface elements Part exename has left this channel.

<aseigo> once we have things in our Plasmoid, such as buttons or things we've drawn in paintInterface, we may want to define some size parameters.


<aseigo> first rule is this: NEVER call resize() in your main script!

<aseigo> you can call plasmoid.resize(height, width) and it will indeed resize your Plasmoid

<aseigo> but that will overwrite the settings the user set it to previusly, and this is not what they will want :)

<aseigo> instead, we can set a default initial size. this is done by adding an entry to the metadata.desktop file, like this: X-Plasma-DefaultSize=200,400

<aseigo> that would make all NEW instances of our widget be 200x400 pixels in size

<aseigo> the user is then free to resize it later on

<aseigo> we can also give some size HINTS at runtime

<aseigo> so if my Plasmoid "looks great" at 400,500 but can be as small as 100,150 and still be ok, i could do this:

<aseigo> plasmoid.setMinimumSize(100, 150)

<aseigo> plasmoid.setPreferredSize(400, 500)

<aseigo> questions:

<aseigo> From Vamp898: do i have to do anything that the plasmoid accepts the local Plasma Theme or is that automatic

<aseigo> Answer: it is automatic

<aseigo> nor can you disable the theme; the goal is consistency rather than a splattering a clashing images.

<aseigo> how CAN, however, include SVGs and pixmaps in the Plasmoid package as covered earlier

<aseigo> Question from comawhite: if we gave hints, what about users with huge resolutions, wouldn't it look worst for them? if say we set a small hint?

<aseigo> Answer: yes, be careful not set the hints too small, but the user can alway resize them; there are even containments that do this automatically

<aseigo> now, i mentioned that calling svg.resize inside the paintInterface wasn't the best way to go about things

<aseigo> and some people are asking about sizes in panels

<aseigo> this brings us to the idea of "Constraints"

<aseigo> in Plasma, our Plasmoid has a certain amount of awareness about its surroundings

<aseigo> we call these things Constraints

<aseigo> these Constraints include: size, location, form factor, immutability, and activity

<aseigo> they are documented here: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Environment

<aseigo> and we'll cover each briefly now

<aseigo> let's start with sizing... whenever our Plasmoid is resized, a SizeConstraint event will be triggered.

<aseigo> we can get our Plasmoid's size with plasmoid.size()

<aseigo> and whenever a SizeConstraint happens, plasmoid.sizeChanged will get called

<aseigo> this means we could do the svg resizing like this:

<aseigo> plasmoid.sizeChanged = function() { rect = plasmoid.rect; svg.resize(rect.width, rect.height) }

<aseigo> and then the SVG would ONLY get resized when the Plasmoid has been resized .. probably more efficient than calling that on every repaint :)

<aseigo> the same pattern holds for the other Constraints

<aseigo> Location refers to where on the screen our Plasmoid is .. if it is on the desktop, it will be Floating, if it is in a plasma-desktop panel attached to the edge of the screen it will be LeftEdge, TopEdge, RightEdge or BottomEdge

<aseigo> Documentation: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Location

<aseigo> when the Location changes, plasmoid.locationChanged() gets called

<aseigo> FormFactor tells us what kind of area our Plasmoid is in. http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#FormFactor

<aseigo> the documentation explains each; this can be useful if we want to lay out the widget differently depending on whether we are in a horizontal/vertical layout (this is the FormFactor used by panels in plasma-desktop) or on the desktop

<aseigo> Immutability refers to whether or not this Plasmoid is locked

<aseigo> when the user selects "lock widgets" Immutability will be set to UserImmutable; when the Plasmoid is free to be altered (configured, resized, etc) then plasmoid.immutability will be Mutable

<aseigo> and Activity refers to the name of the desktop activity that is currently active

<aseigo> going back briefly to the idea of FormFactor though ..

<aseigo> when we are in a Horizontal FormFactor, our widget is free to resize in the horizontal, but will be constrained in the vertical axis (and vice versa for Vertical FormFactor)

<aseigo> these are often very useful hints for our Plasmoid

<aseigo> there is also one more trick we have up our sleeve here!

<aseigo> you may have noticed that some Plasmoids expand when they are on the desktop, but collapse down to an icon when they are put onto a desktop panel

<aseigo> this happens automagically using PopupApplet ... to trigger this behaviour, we put this in our metadata.desktop file:

<aseigo> ServiceTypes=Plasma/Applet,Plasma/PopupApplet


<aseigo> this then gives us a few new properties on our global plasmoid object

<aseigo> popupIcon <-- which is an Icon object (that class is document on techbase)


<aseigo> we can then assign a UI element using the plasmoid.graphicsWidget property

<aseigo> (NOTE: there is a small bug in this in 4.4.0, which will be fixed 4.4.1 .. erf)

<aseigo> ok, so now we can create a Plasmoid package, we can create user interface elements, we can paint, we can load images and use them, we can react to changes in the environment our Plasmoid is in.

<aseigo> but that's kind of boring ;)

<aseigo> we want to be able to do some more exciting things, including:

<aseigo> * read and write configuration

<aseigo> * access sources of data and interact with services


<aseigo> * get information from local files and/or the internet

<aseigo> let's look at configuration first.

<aseigo> * animations, too, of course!

<aseigo> first some questions though.

<aseigo> Question from JJanz: can I use PopupApplet from an icon like kickoff but without this magic?

<aseigo> Answer: Yes! You can manage things yourself if you wish; there is one caveat here: we don't currently provide access to QMenus directly in the Javascript API; you can use an Extender, though (documented on techbase)

<aseigo> ok, configuration:

<aseigo> if we look at this plasmoid we can see it in action: http://websvn.kde.org//trunk/KDE/kdeexamples/plasma/javascript/plasmoids/javascript-config-test/contents/

<aseigo> there is a config/ directory in contents/ and it contains two xml files

<aseigo> these describe configuration values .. main.xml is the default (if any) configuration set used in the plasmoid

<aseigo> the XML files are in a format called "KConfigXT", described in more detail here: http://techbase.kde.org/Development/Tutorials/Using_KConfig_XT

<aseigo> basically, it lets us define configuration keys, put them into groups, say what kind of data is in them (String, Bool, Rect,e tc, etc)

<aseigo> as well as define what the defaults are

<aseigo> getting to the config data is really simple:

<aseigo> var value = plasma.readConfig("KeyName")

<aseigo> that will get the value of the configuration item named "KeyName" in our main.xml

<aseigo> writing an entry is similarly simple: plasma.writeConfig("KeyName", someVariable)

<aseigo> if we want to have multiple sets of configuration items (usually you don't need to, but it is possible to do so) then you can have additional .xml files in config/ and switch between them, as seen in the javascript-config-test example aboe

<aseigo> plasmoid.activeConfig = "secondary" <-- this will try and load secondary.xml

<aseigo> from that point forward, all readConfig/writeConfig calls will use secondary.xml instead of main.xml until we switch back to main.xml

<aseigo> you can check the activeConfig with plasmoid.activeConfig

<aseigo> and if the config doesn't exist or you set it to an empty string, it will go back to main.xml

<aseigo> these files can also be paired up with .ui files that are created by Qt Designer

<aseigo> if there is a contents/ui/config.ui file in our package, for instance, then when the user goes to configure the Plasmoid, config.ui will be loaded to create the configuration UI

<aseigo> the only trick there is that the names of the items in the Qt Designer file must match the names of the keys in the files in contents/config/

<aseigo> in the javascript-config-test example, there is a config item called "Test"

<aseigo> so in our Qt Designer file, we would prepend kcfg_ to any widget that should be used to set that value in the configuration dialog.. so we might create a QLineEdit in our layout and we'd call it kcfg_Test

<aseigo> from that point forward, everything else is handled for us by plasma (saving configuration information to disk, loading it, creating the config UI, etc)

<aseigo> speaking of configuration data and what not, if you want to get some debug or other output to console there are two global functions provided for that:

<aseigo> debug(String) and print(String )

<aseigo> debug output will only be visible for users who are using a debug build of KDE Libs; print will output text to console even on stripped production builds

<aseigo> ok, DataEngines and Services.

<aseigo> this is how your Plasmoid is most likely to interact with the outside world


<aseigo> there is a nice example of using DataEngines and Services in the script-nowplaying example plasmoid in KDE Examples

<aseigo> if you run "plasmaengineexplorer" you'll see a window with a combobox listing a number of DataEngines

<aseigo> each of these DataEngines can provide access to information for your Plasmoid

<aseigo> DataEngines provide "sources" which are named; for instance, in the time dataengine, the name of a source is the timezone we are interested in

<aseigo> each source gives us a list of key/value pairs

<aseigo> in the time engine, each source represents a timezon and each timezone gives us data like like Timezone, Time, Date, Timezone City, Timezone Continent

<aseigo> we have DataEngines for all kinds of things: rss feeds, online comics, weather, microblogging, windows, system information (networing, cpu's, disk, etc), dictionaries, etc, etc.

<aseigo> this means you don't need to write anything yourself to use these kinds of services, you juse them :)

<aseigo> accessing an engine is really simple:

<aseigo> var engine = dataEngine("time")

<aseigo> we now have access to the time engine!

<aseigo> if we wanted to get access to the local time, we could connect to the "Local" source in this time engine:

<aseigo> engine.connectSource("Local", plasmoid)

<aseigo> at this point, whenever the data for Local changes, plasmoid.dataUpdated(string source, Array[any] data) will get called

<aseigo> this is great for things that update on their own. e.g. the windows DataEngine will update whenever a window maximizes

<aseigo> some times, though, we want to have the data refresshed for us on a regular time interval

<aseigo> so if we wanted to get the time twice every second, we could define that time interval with:

<aseigo> engine.connectSource("Local", plasmoid, 500)

<aseigo> that last number is the time in millisecnds between updates

<aseigo> many DataEngines will put minimiums and maximums on the update times, but that all happens in the background and you don't have to worry about it

<aseigo> DataEngines, however, offer READ ONLY data

<aseigo> this is great for efficiency, as the data is shared between all Plasmoids using it

<aseigo> but to make changes or write settings to them, we need to use Services

<aseigo> Services are asyncronous and behave a lot like webservices: you request the service definition, you set up a function call and then you send the service on its way to make that call

<aseigo> the Service will give you back a Job object which you can use to get results and be notified of when it the call completes

<aseigo> this can seen in action in the "plasmoid.setProgress = function(progress)" in the script-nowplaying example Plasmoid

<aseigo> our Plasmoid gets a Service using service("engineName", "sourceName)

<aseigo> note that in 4.4.1 and above you can also do: engine.serviceForSource("sourceName")

<aseigo> we can get operations from the Service using service.operationDescription("name")

<aseigo> we can set parameters on the operation object it returns: plasmoid.setProgress = function(progress)

<aseigo> we can set parameters on the operation object it returns: operation.seconds = progress;

<aseigo> we can then start the operation: controller.startOperationCall(operation);

<aseigo> this will give us a ServiceJob object

<aseigo> and we can connect it to any function we want like this:

<aseigo> and we can connect it to any function we want like this:

<aseigo> service.finished.connect(plasmoid.myJobFinishedFunction)

<aseigo> now, that's a pretty odd looking line, perhaps :)

<aseigo> but what it is doing is connecting a "signal" from the service object to a function of our choosing

<aseigo> which means that whenever the service has a job that finishes, it will call plasmoid.myJobFinishedFunction

<aseigo> in this case we might do something like:

<aseigo> plasmoid.myJobFinishedFunction = function(job) { print(job.result) }

<aseigo> whenever the job is finished .. it will print out the result to console

<aseigo> this signals-connected-to-functions pattern is repeated throughout the API, and not just for services Part Spell has left this channel ("Konversation terminated!").

<aseigo> whenever there is mention of a Signal in the API docs here: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/AP .. you can use this same method of connecting

<aseigo> for instance, PushButton offers a clicked signal whenever the user clicks on it

<aseigo> to react to that, we create a function:

<aseigo> plasmoid.buttonClicked = function() { print("clicked!") }

<aseigo> and then connect it to the signal from the pushbutton:

<aseigo> var button = new PushButton

<aseigo> button.clicked.connect(plasmoid.buttonClicked) Part amrit has left this channel.

<aseigo> some signals include parameters that tell us more about what just happened

<aseigo> for instance, DataEngine has a signal that is sent whenever a new source becomes available: sourceAdded(String)

<aseigo> to access that information we would do something like this:

<aseigo> plasmoid.newSource = function(sourceName) { print("we got a new source from the DataEngine: " + sourceName) }

<aseigo> var engine = plasmoid.dataEngine("someengine")

<aseigo> engine.sourceAdded.connect(plasmoid.newSource)

<aseigo> so as you can see, signals are a way of getting information from one object to another

<aseigo> and of course, if you are into the somewhat messy javascript ways of doin things, you can also make connections like:

<aseigo> engine.sourceAdded.connect( function(sourceName) { print("we got a new source from the DataEngine: " + sourceName) } )

<aseigo> i find that a bit.. hard to read though :)

<aseigo> back to Services briefly ...

<aseigo> Service is the therefore the writer to DataEngine's read. a Service object belongs to whoever calls it and is not shared; it can also be used over and over again

<aseigo> Question from JJanz: Does sourceAdded have any relationship with dataUpdated? Like running order (which goes first), priority (which overrides each) or alike?

<aseigo> Answer: the sourceAdded(String) signal is emitted whenever a new source appears in the DataEngine; dataUpdated is called only when the Plasmoid is connected to the source Part VenomVelvet has left this channel.

<aseigo> there is also a sourceRemoved(String) signal in case anyone was curious (this is all documented on techbase :)

<aseigo> you can also see what sources exist currently with engine.sources

<aseigo> so the pattern is: sourceAdded emitted, connect to the source if you wish, dataUpdatd would then get called, sourceRemoved will be emitted if the source ever goes away

<aseigo> so if you want to respond to new windows appearing and you were using the tasks DataEngine, you would connect to sourceAdded. maybe something like this:

<aseigo> plasmoid.windowAppeared = function(id) { tasksEngine.connectSource(id, plasmoid) }

<aseigo> var tasksEngine = dataEngine("tasks")

<aseigo> tasksEngine.connect.sourceAdded(plasmoid.windowAppeared)

<aseigo> Question from bendie: What if I want to write some data (not configuration data) to disk? Do I need to implement a Service for that?

<aseigo> Answer: right now, yes. however, in KDE SC 4.5 we will be including a Storage system that handles that for you

<aseigo> ok, second to last topic: Animations!

<aseigo> Animations let us, well, animate things :) you can create Animation objects and attach them to any UI Element or even to the Plasmoid itself

<aseigo> they can be set up in parallel to run multiple animations simultnaeously on the same object, e.g. to simultaneously slide and fade something

<aseigo> or in sequence to have one animation after another occur

<aseigo> or just individually in the simple (and common) case

<aseigo> we provide a number of stock animations (fade, rotate, etc) but you can also define your own using a PropertyAnimation

<aseigo> Documentation: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Animations

<aseigo> http://websvn.kde.org/trunk/KDE/kdeexamples/plasma/javascript/plasmoids/animations

<aseigo> Example --^

<aseigo> animations are created using the animation global function: pulseAnimation = animation('pulse')

<aseigo> you can create as many animations as you wish, of course, even of the same type

<aseigo> you then associate the animation with a UI Element or the plasmoid itself

<aseigo> var button = new Pushbutton

<aseigo> pulseAnimation.targetWidget = button

<aseigo> you can set durations in milleseconds and animation-specific properties such as in the fade animation which supports setting the target and starting opacity for the fade

<aseigo> we then use the start() method on the animation to kick it off

<aseigo> what's neat is that start() can be connected to a signal (as can many other similar functions). so to make clicking the button start our pulse animation we'd just do

<aseigo> button.clicked.connect(pulseAnimation.start)

<aseigo> and now whenever that button is clicked, the pulseAniamation will start.

<aseigo> this makes it all rather easy to add nice bits of bling to your widgets :)

<aseigo> and finally for today: API Extensions

<aseigo> because the Javascript Plasmoids are meant to be able to be run without causing the user too much stress because they are running code from who-knows-who-from-who-knows-where, there is no way to access local or remote files or the network or things like file dialogs by default

<aseigo> but you can REQUEST access to these features using API Extensions

<aseigo> this way Plasma knows which Plasmoids will be trying to do what and can even impose security restrictions if needed

<aseigo> Documentation: http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Extensions

<aseigo> in our metadata.desktop file we can have two optional entries: X-Plasma-RequiredExtensions= and X-Plasma-OptionalExtensions=

<aseigo> if a Required extension can not be loaded, then the Plasmoid will simply not start up

<aseigo> if the extension is Optional, you can check to see if it was actualy loaded: if (plasmoid.hasExtension("nameOfExtension")

<aseigo> so, to be able to make http calls, we might do:

<aseigo> X-Plasma-OptionalExtensions=http

<aseigo> and then in our javascript, we can do:

<aseigo> if (plasmoid.hasExtension("http")) { .. do some web requests .. }

<aseigo> what's realy interesting is that ANY QtScript C++ plugin can be called this way, in addition to the built in ones (HTTP, FileDialog, LaunchApp, NetworkIO, LocalIO)

<aseigo> so when the Qt Javascript Smoke bindings are done (any day now, i hear ;) we can load any part of Qt's API into a Javascript Plasmoid, security willing, using the extension loader

<aseigo> finally, if you have additional Javascript you'd like to pull in (such as a third party Javascript library, or just something you've written but put in a separate file to keep your project sane :), put those files in contents/code and use the include() method:

<aseigo> plasmoid.include("nameOfFile.js")

<aseigo> right now, those files must be local and in your package; in future versions we will likely support HTTP requests as well as provide some stock .js files in the base install

<aseigo> you can also check which version of the API you are using with plasmoid.apiVersion (which is currently at 1)

<aseigo> there is a LOT more you can do with your Plasmoid, including using things like Extenders. i do encourage you to read the documentation links provided :)

<aseigo> we'll wrap up by looking at how to test and package our Plasmoids and then take final questions

<aseigo> so now how do we test our plasmoids?

<aseigo> the simplest way is to just cd into the package directory and run plasmoidviewer, e.g.:

<aseigo> ~/kdeexamples/plasma/javascript/plasmoids> plasmoidviewer tiger

<aseigo> this will show our plasmoid running in a window

<aseigo> you can set the formfactor, location and more when starting plasmoidviewer, run `plasmoidviewer --help` to see the various options available

<aseigo> Plasmate includes a previewer as well that lets you change these things on the fly

<aseigo> plasmoidviewer also accepts absolute paths to packages (plasmoidviewer ~/kdeexamples/plasma/javascript/plasmoids/tiger) and if isn't given any path will try and load the current directory as a Plasmoid:

<aseigo> ~/kdeexamples/plasma/javascript/plasmoids/tiger> plasmoidviewer

<aseigo> when you want to test the plasmoid from, say, plasma-desktop, you then want to run plasmapkg:

<aseigo> plasmapkg -u tiger

<aseigo> you don't need to package it up to install it, just point plasmapkg to the directory

<aseigo> then run kbuildsycoca4 just to be sure the system cache is updated (it shoudl do that automatically, but on some systems this doesn't happen instantly)

<aseigo> it will now appear in plasma-desktop and elsewhere

<aseigo> when you want to send your plasmoid to someone else (e.g. to the Javascript Jam Session contest ;), do something like this:

<aseigo> ~/myCreation> zip -r myCreation.plasmoid *

<aseigo> this may result in a file called myCreation.plasmoid.zip .. if so, just move it: mv myCreation.plasmoid.zip myCreation.plasmoid

<aseigo> that .plasmoid file can then be easily installed, uploaded to kde-look.org, etc. ... or sent in to the Javascript Jam Session competition ;)

<aseigo> great, that concludes today's session! we have 10 minutes or so for questions!

<aseigo> Question from arreche: How to check for syntax errors?

<aseigo> when you run plasmoidviewer, you will get syntax error messages in the pasmoidviewer window itself as well as on the console

<aseigo> Question from naproxeno: is there a way to know when an animation has finished? does it emit a signal?

<aseigo> Answer: yes, and the signal is called finished .. again, you can reference the API documentation on Techbase in case you forget any of this (I know it's been quite a lot for one go!)

<aseigo> Question from bendie: How do I implement services? Is it possible with javascript or do I need C++/Python/whatever?

<aseigo> Answer: 4.4 does not support Javascript services unfortuantely; dataengines and services can be written in C++, Python or Ruby; Javascript DataEngines/Services are slated for 4.5

<aseigo> Question from megu: plasmoidviewer report this error when running tiger: "Script failure on line 5: TypeError: Result of expression 'print' [true] is not a function. Other plasmoid give the same error. any clue? Part Siju has left this channel.

<aseigo> Answer: that has to be one of the worst worded errors from QtScript :/ what it -really- means is that you're doing something in the print() statment that isnt a string or can't be turned into a string. for instance, if you call a non-existent function or do something else that results in a syntax error, you'll get that one

<aseigo> oh, also, please note that you are ALL welcome to ask questions or just come hang out with us whenever you'd like in #plasma or ask questions on [email protected] Part zzAMzz has left this channel ("http://quassel-irc.org - Chat comfortably. Anywhere.").

<aseigo> Question fromt sixtripleeight: is javascript slower than python?

<aseigo> Answer: that really depends on the runtimes used; right now QtScript is almost certainly slower than the stock python runtimes. however, the Javascript API has a lot less overhead, is guaranteed to work in more places and has usable security features

<aseigo> I don't expect to see too many devices ship Python Plasma for instance, due to this

<aseigo> while Javascript is a great option


<aseigo> oh, Kubuntu users! you may run into a problem with the default 4.4.0 packages. there was a packaging mistake (been corrected for future package updates) and plasma-scriptengine-javascript was not included by default in kdebase-runtime as it is supposed to be

<aseigo> so if you are on Kubuntu and you get a "couldn't create a Javascript ScriptEngine" message in plasmoidviewer, do: sudo apt-get install plasma-scriptengine-javascript

<aseigo> Question from simion314: can a plasmoid crash entire plasma?

<aseigo> Answer: in theory, no. in reality, if there are bugs in the C++ libraries underneath (e.g. Qt or libplasma), a Javascript could trigger a crash in them, but it can't crash itself

<aseigo> when a Javascript fails ("crashes"), it simply stops executing and fires an exception for Plasma to catch (which it uses to communicate an error message about what just went wrong)

<aseigo> but the Plasma application as a whole continues on just fine

<aseigo> there is one problem in Qt right now, that i hope will be fixed in 4.7, and that is there is no good way of catching infiinite looping in a Javascript

<aseigo> we know how it should be done to catch those, it just needs to be implemented by the QtScript team at Qt Development Frameworks and then even _that_ can't harm the Plasma ;)

<aseigo> Question bitshuffler: Is there some api available by default for getting a xml file from an url and parsing it?

<aseigo> Answer: You can use the HTTP (or NetworkIO if you aren't using HTTP to grab the xml) API Extension to fetch the XML. right now, we don't have a full XML parsing system in the Javascript API, but you can use a Javascript library for this. xml_for_script is a great project, for instance, and it's DOM parser API is a 47k Javasript file (they also hav SAX and w3dom files)

<aseigo> put that in your contents/code/ and use plasmoid.include("xmldom.js") (or whichever)

<aseigo> Question from simion314: if you want to get something from the network like pages or images do you need to create a data engine? the engine can store files for you?

<aseigo> Answer: for web pages, the simplest thing to do is use the HTTP extension or use the Plasma::WebBrowser widget (which can also display it for you!). Similarly for images, though we do have some DataEngines that do help with images, such as the potd (picture of the day) engine or the web comics engines

<aseigo> if you are going to be working with general "class" of web content, such as the web comics example there, i really do recommend a DataEngine as it makes life much simpler and results in lower runtime overhead when multiple Plasmoids use the same DataEngine

<aseigo> great, i think we're done for today. i'll be saving this log, cleaning it up and posting it online later on (probably tomorrow at this point)

<aseigo> thanks everyone for your time and for coming out today! :)