Developing My First Plasmoid: The QML Code
By EricMesa
- 6 minutes read - 1198 wordsBack in February I posted the code to the data engine I developed for my plastmoid. At the time I’d wanted to clean up my plasmoid before posting it on here, however, I’ve become stuck on a key feature so I was hoping that maybe by posting the code I could get some help. (As well as provide an example to others)
First of all, plasmoids need a certain structure to the folders. Here’s what mine looks like:
. ??? contents ? ??? ui ? ??? Button.qml ? ??? Button.qml~ ? ??? content ? ? ??? ItemDelegate.qml ? ? ??? ItemDelegate.qml~ ? ??? flickrhelpers ? ? ??? addtogroup.py ? ? ??? addtogroup.py~ ? ? ??? removefromgroup.py ? ? ??? removefromgroup.py~ ? ??? flickrviews.py~ ? ??? flickrviews.qml ? ??? flickrviews.qml~ ? ??? test.qml~ ??? metadata.desktop ??? metadata.desktop~
As you’ll see, I need to have a lot more in the ui folder, but I haven’t taken that optimization step yet. First off is the metadata.desktop file. A lot of this is self-explanatory, but one important part is the X-Plasma-RequiredExtensions which allows me to launch the flickrhelpers files.
` [Desktop Entry] Name=flickr views Comment=A widget for flickr views Icon=chronometer
X-Plasma-API=declarativeappletscript X-Plasma-MainScript=ui/flickrviews.qml X-Plasma-DefaultSize=750,700 X-Plasma-RequiredExtensions=LaunchApp
X-KDE-PluginInfo-Author=Eric Mesa X-KDE-PluginInfo-Email= X-KDE-PluginInfo-Website=http:/server.ericsbinaryworld.com/ X-KDE-PluginInfo-Category=Internet X-KDE-PluginInfo-Name=org.kde.flickrviews X-KDE-PluginInfo-Version=0.2
X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-ServiceTypes=Plasma/Applet Type=Service `
Moving on, we go to Button.qml which allows us to definte a button once and then use it throughout the main file without cluttering things up too much. ` import QtQuick 1.0 Rectangle{
id: button width: 50; height: 43
Text{ id: buttonLabel anchors.centerIn: parent text: bLabel }
property color buttonColor: “lightblue” property color onHoverColor: “gold” property color borderColor: “white” property string bLabel: “button label”
signal buttonClick() onButtonClick:{ console.log(buttonLabel.text + " clicked") }
MouseArea{ id: buttonMouseArea anchors.fill: parent onClicked: buttonClick() hoverEnabled: true onEntered: parent.border.color = onHoverColor onExited: parent.border.color = borderColor }
//detmermines color of button by using the conditional operator color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor `
Next up is the ItemDelegate code. This code takes the XML from the data engine and parses it out to make the list on the left-hand side of all the photos.
import QtQuick 1.0 Item { id: delegate height: column.height + 40 width: delegate.ListView.view.width Column { id: column x: 20; y: 20 width: parent.width - 40 Text { id: titleText text: title; width: parent.width; wrapMode: Text.WordWrap font { bold: true; family: "Helvetica"; pointSize: 14 } } Text { id: viewsText text: "Views: " + views; width: parent.width; wrapMode: Text.WordWrap font { bold: false; family: "Helvetica"; pointSize: 12 } } } MouseArea { anchors.fill: delegate onClicked: { delegate.ListView.view.currentIndex = index window.photoURL = url window.jpegURL = jpeg window.photoID = photo } } }
So, with the helper files out of the way, let’s take a look at the main code. First come the imports:
import Qt 4.7 import "content" import QtWebKit 1.0 import org.kde.plasma.core 0.1 as PlasmaCore
Then comes the code that starts my plasmoid. All of the plasmoid is within a Rectangle. Next up is how I access my engine. This pulls all the data from the engine into my plasmoid. ` Rectangle { id: window width: window.width height: window.height
PlasmaCore.DataSource {
id: viewsSource
engine: "flickrviewsengine2"
interval: 86400000
Component.onCompleted: connectedSources = sources
onSourceAdded: connectSource(source)
}
`
Then I setup the variables I use throughout the plasmoid.
property string currentGroup: "" //this is used to hold the current views group to display property string photoURL: "" //this is used to hold the flickr webpage of the current image property string jpegURL: "" //this is to hold the photo that appaers in the webview property string photoID: "" //this is to hold the photo ID that appaers in the webview property string currentphotogroup: "" //this is to hold the photo's current group that appaers in the webview property string nextphotogroup: "" //this is to hold the group it should go into property bool loading: feedModel.status == XmlListModel.Loading
Then comes the XML liste model. This is what pulls all the data out of the XML. Isn’t it so nice and easy to read? It’s easier than any other XML parser I’ve used in other programming languages.
XmlListModel{ id:feedModel xml: window.currentGroup query: "/photos/photo" XmlRole { name: "title"; query: "title/string()"} XmlRole { name: "views"; query: "views/string()"} XmlRole { name: "url"; query: "URL/string()"} XmlRole { name: "jpeg"; query: "JPEG/string()"} XmlRole { name: "photo"; query: "id/string()"} }
Now we move back into visual stuff rather than program logic. We use the buttons I defined before and use them to populate some of the variables I declared above. There are a lot of buttons (one for each group) so I will just include the first two in this post:
Row { width: window.width height: 43 spacing: 2 //spacing: parent.width/14 Button{ id: v25 bLabel: "25" onButtonClick:{ currentGroup = viewsSource.data["25"]["Group 25"]; window.currentphotogroup = "25"; window.nextphotogroup="50"} } Button{ id: v50 bLabel: "50" onButtonClick:{ currentGroup = viewsSource.data["50"]["Group 50"]; window.currentphotogroup = "50"; window.nextphotogroup="75"} }
Now we get to the column along the left-hand side that uses the ItemDelegate I shared above:
`
Row {
x: 0
y: 43
width: window.width
height: window.height
Rectangle {
width: window.width/3+10; height: window.height
color: “#efefef”
//This uses the ItemDelegate in the “contents” folder to create the list of images
ListView {
id: list
width: window.width/3; height: window.height
model: feedModel
delegate: ItemDelegate {}
highlight: Rectangle { color: "lightgray" }
highlightMoveSpeed: 9999999
}
}
This is where I define the box that holds the preview image.
Rectangle {
width: window.width/3
height: window.height
Row{
spacing: 0
WebView {
id: webView
width: 250
html: ""
//url: photoURL
}
Finally, the action buttons - the part that makes my plasmoid more useful than the commandline program I'd written. This is the part where I really need help from readers as I will explain after this section.
Column{
Button{
id: launchwebpage
width: 200
bLabel: "View on Flickr"
onButtonClick: plasmoid.openUrl(photoURL) //plasmoid.runCommand("firefox", ["http://www.google.com"]) //plasmoid.openUrl([photoURL]) //requires: X-Plasma-RequiredExtensions=LaunchApp
}
Button{
id: launchdiscuss
width: 200
bLabel: "Discuss"
onButtonClick: plasmoid.openUrl("http://www.flickr.com/groups/views"+window.currentphotogroup+"/discuss/")
}
Button{
id: addtogroup
width: 200
bLabel: "Add to Next Group"
onButtonClick: plasmoid.runCommand("/home/ermesa/bin/qml/plasmoids/flickrviews2/contents/ui/flickrhelpers/addtogroup.py", [photoID,nextphotogroup])
}
Button{
id: removefromgroup
width: 200
bLabel: "Remove from Current Group"
onButtonClick: plasmoid.runCommand("/home/ermesa/bin/qml/plasmoids/flickrviews2/contents/ui/flickrhelpers/removefromgroup.py", [photoID,currentphotogroup])
}
}
}
}
}
} ` As you can see, in “Add to Next Group” and “Remove from Current Group” I am launching a python program that either adds or removes the photo from the group. However, sometimes this command fails. The biggest reason is that only 5 photos can be added to a group at a time. And I may have added some photos earlier in the day or via a cron job that I have that adds in the photos to the groups if they’ve never been in any groups. When it fails it raises an exception. I’d like to know how I can communicate that back to my plasmoid. This part is the only part where I haven’t been able to get help from the mailing lists or IRC. Without it, I have to launch the plasmoid from the cli and constantly check to see if there was a failure or success. If any readers can help me out with this, I would be so, so happy. I’d finally be done with functionality and be able to work on cleaning up the code and beautifying the plasmoid.