{January 5, 2008}   what I’ve learnt about plasma

warning: the majority of this post is basically just a braindump of random data. it may or may not make any sense. if it looks too long, then just don’t read it.
[this post was written over several weeks. if it sounds a little disjointed, that’s why.]

I feel like I’ve been learning a lot about how plasma works recently. it’s like some tipping point has been reached, and suddenly I *get it*. I see code that’s kinda ok and working, but really doesn’t look Right – and find out that my feelings about the code are correct.

in the interests of not forgetting what I’ve learnt, and helping other people to learn it with less pain, I’m gonna just kinda braindump here. some of this is random trivia, some of it is really important concepts that affect whether your applet is a buggy mess or a nice happy simple thing.

this information really should get onto techbase or something asap, but I feel like I need to have it written up more nicely before I put it there, and if I wait until I can write it up nicely I’ll have forgotten half the stuff; I need somewhere I can do a few drafts first.

sizing: when you’re an applet doing your own stuff, you want to use functions with Content in them, so that you don’t have the borders added in. when you’re acting on another applet, you use the ones without, so that the border is taken into account. other than that, I’m really quite confused. I feel like we really shouldn’t need two functions for every type of sizing. but I don’t have time to investigate that right now.
If you need to be notified when your plasmoid is resized, then implement constraintsUpdated() and check for a sizeconstraint. your new size is given by contentSize().
avoid reimplementing functions that return a size. only override contentSizeHint if you really need to (eg. controlling the aspect ratio).
if you know you’ll always need a certain amount of space, then setMinimumContentSize

when creating an applet, do as little as possible in the constructor. set a default size, and preferably no more. your ui creation, config reading, etc. should all go in init() instead. don’t read your size from the config, it’s done for you.
if your applet is useless without some configuration, then there’s a special thing you should set, but I forget what it is; I should do this for twitter at some point.
however… layouts may not like not being in the constructor. can someone explain how these two bits of information fit together?

don’t sync the config; that’s plasma’s responsibility. you can, however, emit configNeedsSaving().

there’s stuff to be done when accepting mouseclicks so that the event doesn’t get completely swallowed all the time – but I’ve forgotten the details and just can’t find the information any more.

setVisibility() does the same thing as show() and hide() – so don’t use both at once :P

if your function name starts with “got”, or you’re having trouble commenting the function in the header, chances are your design is suboptimal.

if your applet gets data regularly, don’t use a qtimer to poll the data engine. have the data engine alert you when it’s got stuff, and set an update interval when you connect to the source. reducing the number of timers makes laptops happy. :)
there’s no need to worry about connecting to the same source twice, it won’t hurt.
if you use Plasma::AlignToMinute, you won’t get any updates until the minute rolls around – if your first update was a fake empty one and the actual data was downloaded 5 seconds later, you’ll have to wait another 55 seconds for it.

if you were trying to work on a dataengine in the past, and wondering why the update interval wasn’t always updating – it was probably the timing bug that I just fixed, so you can go revisit that code now :)

remember that if the dataengine creates a source, it hangs around until it’s explicitly killed off, but if an applet requests a source it only stays as long as something’s connected to it.

if you really really want to send data from an applet to an engine, the current awful hack is to abuse Q_PROPERTY… but please don’t. hopefully we’ll have guidelines on a non-awful way to deal with this for 4.1.

I think I know enough about dataengines that i could write something on how to use them, but figuring out what to say is hard.

The general idea of the dataengine is that it provides data in a nice, easy-to-use way, so that writing applets is easier. if you have 5 different clocks on your desktop, they’re not all polling for the time individually; they just connect to the time dataengine, tell it how often they want updates, and it’ll send them a regular signal with the data they need (in this case, the time). this means the clock applets don’t need to each have their own QTimer. reducing the number of timers is always a good thing.

if you just want to make use of an existing dataengine, it should be fairly straightforward. the documentation of DataEngine is good; read it. First off you’ll want to create a dataengine with Applet::dataEngine() – this goes through the engine manager, which makes sure there’s only one of each engine running, and kills it off when nobody’s using it any more.
you should probably check that you actually got a valid engine back before doing anything with it, just in case something funny happened.

once you’ve got your engine pointer, you can query the list of sources, connect to sources, etc – all the stuff mentioned in the DataEngine API. what the API doesn’t tell you, of course, is what kinds of sources are offered by the particular engine you want to use. the sources may not be created until you request them, the engine may expect source names to follow a certain format, etc… these things depend on the individual engine, and I don’t know whether there’s a standard way to document them yet. twitter has a big comment in the .h file right before the engine class; otoh, the time engine only has this mysterious comment: “This class evaluates the basic expressions given in the interface.” I had to read the source code to see that it has sources named by timezone.

so, what if you want to actually *write* an engine?
well, you’re going to have to provide data to the applets requesting those sources. there’s two general concepts here: data that can be returned immediately, and asynchronous data that has to be downloaded or something. the first is, of course, easier.
here, again, DataEngine docs come in handy.
updateSource is called whenever it’s time to update a source. pretty straightforward, right? so, this function is generally where you figure out what your data is, and setData() on the source. if you’re doing something asynchronous, you’ll want to set that in motion and return false (to show that you don’t really have anything useful yet).
sourceRequested generally wants to call updateSource(), to get the ball rolling on the new source. asynchronous engines don’t want to bilndly return the return value from updateSource(), because here returning false means that no source was created at all (something you might want if you’re expecting, say, a timezone, and the applet requests “mars”).
also, for asyncronous engines, when the data finally does come in you should just need to setData() (you remembered to keep track of what source it’s for, right?) and… well, that’s it. see? sending data to applets is easy :)

oh, and you might also want to give some thought to how long information is held in the engine. do you want to clear out all the old data from a source when you update it? or would you be better off just appending to it – and then making sure that it doesn’t grow too big, of course. :) sources created for an applet request are removed when nobody’s using them any more – but if your engine creates new sources on its own, should it remove them on its own eventually, or let them hang around until it’s deleted?

if you want data to go the other direction… well, best to wait until that’s been figured out properly.

strange, I was quite opposed to the thought of writing tutorials a week ago, but now that I have some paragraphs together it’s starting to look like a tutorial anyways.

detour! let’s talk about *using* plasma.
keyboard shortcuts: ctrl+f12 brings up the dashboard. this brings all the desktop widgets to the front. whether or not panels should also be raised hasn’t been decided afaik.
new thing in today’s plasma: thanks to Alex, holding ctrl while resizing allows you to change the aspect ratio. dunno how this actually works in practice since I haven’t svn upped yet.

I think that’s it for keyboard shortcuts. someone needs to add more someday. preferably in a sane manner, not just at random. :) apparently such information belongs in kdebase/workspace/doc – but I’m just dumping raw information right now, I’m not gonna write actual real docs unless someone tells me how :)

oh, and if you right-click on the little gap between two tasks in the taskbar, you get the standard applet contextmenu. this was supposed to also be added to the menu you get when you right-click any task, but I think that turned out to be harder than expected and isn’t done yet.

wow, it’s amazing how much I get done when I’m avoiding studying. :P

randomguy3 says:

I’m thinking that having separate buttons for “resize” and “rotate” might be a better way. And have CTRL make it keep the aspect ratio, rather than the other way around.

maniacmusician says:

+1 to randomguy3’s suggestion.

john says:

I just wanted to say how cool it is that you did this brain dump. On new projects I’m regularly frustrated out of proportion by little details like what you describe.

The cool thing is that I’m totally not into Plasma coding, but these glimpses make me want to try it out (even though that’ll never happen due to lack of time)

Comments are closed.

et cetera
%d bloggers like this: