📜 ⬆️ ⬇️

KDE4 Plasma Desktop. Plasmoid creation

Plasma
Plasmoid ( plasmoid ) is a desktop widget in the KDE4 Desktop. Any visible control on the desktop is a plasmoid, be it a clock, a system tray, a processor load monitor, or a window with a weather forecast.

This lesson describes the creation of a plasmoid that can make requests to a certain server and show the result obtained. Since the server requires user authorization, the process of storing user account data in KWallet will be disassembled. Development language: Python.

As an example, a plasmoid was written, checking the balance of a cell phone of one Irkutsk operator. To obtain data, you need to log in to CCIS , get data from the page and log out.

Package


Each plasmoid is a set of files packed into a zip archive. In this lesson, the plasmoid will be called “bwc-balance-plasmoid”. Create a directory of the same name in which all project files will be stored:
./contents/
./contents/code/
./contents/code/main.py
./metadata.desktop

')
metadata.desktop

The metadata.desktop contains all the meta-data about the plasmoid, for example, its name, author, or programming language in which it is written.
[Desktop Entry]
Encoding=UTF-8
Name=BWC Balance
Name[ru]= BWC
Type=Service
ServiceTypes=Plasma/Applet
Icon=phone
X-Plasma-API=python
X-Plasma-MainScript=code/main.py
X-KDE-PluginInfo-Author=SvartalF
X-KDE-PluginInfo-Email=self@svartalf.info
X-KDE-PluginInfo-Name=bwc-balance
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=http://bitbucket.org/svartalf/bwc-balance-plasmoid/
X-KDE-PluginInfo-Category=Online Services
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true

The assignment of all fields is fairly obvious, so we will not stop here.

main.py



The main code of the plasmoid is in the main.py file.

We import the system libraries, without which the creation of the widget becomes impossible:

Copy Source | Copy HTML
  1. from PyQt4.QtCore import *
  2. from PyQt4.QtGui import *
  3. from PyKDE4.kio import *
  4. from PyKDE4.kdeui import *
  5. from PyKDE4.kdecore import *
  6. from PyKDE4.plasma import Plasma
  7. from PyKDE4 import plasmascript
  8. from PyKDE4.solid import Solid


Create a plasmoid class:
Copy Source | Copy HTML
  1. class BWCBalancePlasmoid (plasmascript.Applet):
  2. def __init__ (self, parent, args = None):
  3. plasmascript.Applet. __init__ (self, parent)
  4. def init (self):
  5. "" "Initialization of settings" ""
  6. # Plasmoid has user settings
  7. self .setHasConfigurationInterface (True)
  8. self .setAspectRatioMode (Plasma.IgnoreAspectRatio)
  9. self .theme = Plasma.Svg (self)
  10. self .theme.setImagePath ( "widgets / background" )
  11. self .setBackgroundHints (Plasma.Applet.DefaultBackground)
  12. # Location of the elements of the plasmoid
  13. self .layout = QGraphicsLinearLayout (Qt.Horizontal, self .applet)
  14. # Label for output
  15. self .label = Plasma.Label ( self .applet)
  16. self .label.setText ( "0.0" ) # Initially, let it be 0
  17. # Add Label to Layout
  18. self .layout.addItem ( self .label)
  19. self .applet.setLayout ( self .layout)
  20. # And we change the size of the plasmoid
  21. self .resize ( 50 , 50 )


After describing the class, add a small function called Plasma to create a plasmoid:
Copy Source | Copy HTML
  1. def CreateApplet (parent):
  2. return BWCBalancePlasmoid (parent)


To test the work there is a program plasmoidviewer:
svartalf ~ $ plasmoidviewer bwc-balance-plasmoid
The result is such a beautiful, but absolutely non-functional window.

Plasmoid basic view

When the plasmoid is ready, pack it into a zip archive.

The resulting file is installed in the system with the following command:
plasmapkg -i bwc-balance-plasmoid.zip
and deleted:
plasmapkg -r bwc-balance-plasmoid.zip

Settings



Let's deal with the implementation of custom settings.

In Qt Designer, we make a dialog based on QDialog with two input fields, and save it in settings_ui.ui

QT Designer

The resulting .ui file is converted into a .py file. This will be the basic settings dialog.

pyuic4 settings_ui.ui -o settings_ui.py

The created dialog will be inherited by our form settings. During the initialization of the form, we will try to fill in the fields with already available data from KWallet.

Copy Source | Copy HTML
  1. class SettingsDialog (QWidget, Ui_SettingsDialog):
  2. def __init__ (self, parent = None):
  3. QWidget. __init__ (self)
  4. self .parent = parent
  5. self .setupUi (self)
  6. # Open local “wallet”
  7. self .wallet = KWallet.Wallet.openWallet (KWallet.Wallet.LocalWallet (), 0 )
  8. if self .wallet:
  9. # Choose our “folder” for storing passwords
  10. self .wallet.setFolder ( "bwc-balance-plasmoid" )
  11. if not self .wallet.entryList (). isEmpty ():
  12. phone = str ( self .wallet.entryList (). first ())
  13. password = QString ()
  14. # Read the password for the phone number
  15. self .wallet.readPassword (phone, password)
  16. # And fill this field with dialogue data
  17. self .textPhone.setText (phone)
  18. self .textPassword.setText ( str (password))
  19. def get_settings (self):
  20. return { "phone" : str ( self .textPhone.text ()), "password" : str ( self .textPassword.text ())}


The KWallet.Wallet.openWallet () function accepts the third, optional OpenType parameter: synchronous / asynchronous wallet opening mode

When opening in asynchronous mode, you need to check the return value of the function, and if it is not None, connect the walletOpened () signal to the slot that will work with the wallet data.

In this case, the wallet is open in synchronous mode, data is read from it and fill in the dialogue fields.

Now you need to show this dialogue at the request of the user and save the entered data.

Let us add the following lines for the plasmoid class in init ():
Copy Source | Copy HTML
  1. # The settings dialog object will be stored here.
  2. self .settings_dialog = None
  3. # And this is the settings themselves
  4. self .settings = { "phone" : None, "password" : None}
  5. # When loading the plasmoid we open the wallet in asynchronous mode
  6. # So the user will immediately see the plasmoid and the offer to allow access
  7. this wallet app
  8. self .wallet = KWallet.Wallet.openWallet (KWallet.Wallet.LocalWallet (), 0 , 1 )
  9. if self .wallet:
  10. self .connect ( self .wallet, SIGNAL ( "walletOpened (bool)" ), self .walletOpened)


In the self.walletOpened () function, open the wallet, read the user data and start the timer, which will update the information after a certain period of time.

Wallet request

Add a function to the BWCBalancePlasmoid class that will be called when the corresponding menu item is selected.

Plasmoid call settings
Copy Source | Copy HTML
  1. def showConfigurationInterface (self):
  2. # Create an object of our dialogue
  3. self .settings_dialog = SettingsDialog (self)
  4. # We embed it in a standard dialogue form
  5. dialog = KPageDialog ()
  6. dialog.setFaceType (KPageDialog.Plain)
  7. dialog.setButtons (KDialog.ButtonCode (KDialog.Ok | KDialog.Cancel))
  8. page = dialog.addPage ( self .settings_dialog, u "CCIS Settings" )
  9. # Connect slots with signals
  10. self .connect (dialog, SIGNAL ( "okClicked ()" ), self .configAccepted)
  11. self .connect (dialog, SIGNAL ( "cancelClicked ()" ), self .configDenied)
  12. dialog.resize ( 350 , 200 )
  13. # Show dialogue
  14. dialog.exec_ ()


The self.configAccepted () and self.ConfigDenied () functions will be called when the “Ok” and “Cancel” buttons are pressed in the dialog. Since when we click the Cancel button, we don’t need to take any action, it only remains to describe the logic of configAccepted ().

Copy Source | Copy HTML
  1. def configAccepted (self):
  2. # Update data in the program
  3. self .settings = self .settings_dialog.get_settings ()
  4. # And save them in wallet
  5. wallet = KWallet.Wallet.openWallet (KWallet.Wallet.LocalWallet (), 0 )
  6. if wallet:
  7. if not wallet.hasFolder ( "bwc-balance-plasmoid" ):
  8. wallet.createFolder ( "bwc-balance-plasmoid" )
  9. wallet.setFolder ( "bwc-balance-plasmoid" )
  10. for e in wallet.entryList ():
  11. wallet.removeEntry (e)
  12. wallet.writePassword ( self .settings [ "phone" ], self .settings [ "password" ])


After changing the settings, we start the update timer, and in order not to wait until it works for the first time, we do the data download.

Data update



We agree that the data will be updated at once per hour. First of all, create a timer for which data will be loaded. Let's add the following to the init () function of the BWCBalancePlasmoid class:
Copy Source | Copy HTML
  1. self .timer = QTimer ()
  2. self .connect ( self .timer, SIGNAL ( "timeout (bool)" ), self .loadBalance


Here we create an object of the QTimer () class and connect its timeout () signal to the loadBalance () function.

It remains only after loading the plasmoid to start the timer:
Copy Source | Copy HTML
  1. self .timer.start ( 1000 * 60 * 60 ) # The time is specified in milliseconds


Data loading



Initially, data was loaded via urllib2.build_opener () and urllib2.Request (), but this method had the following disadvantages:

The problem was solved by the use of KIO's network functions, namely, storedGet () and storedHttpPost (), which operate in asynchronous mode.

I will not dwell on the process of working with these functions, just give examples of GET and POST requests:

Get

Copy Source | Copy HTML
  1. self .job = KIO.storedGet (KUrl ( "http://example.com/account" ), KIO.Reload, KIO.HideProgressInfo)
  2. self .job.addMetaData ( "User-Agent" , "User-Agent: bwc-balance-plasmoid" )
  3. self .connect ( self .job, SIGNAL ( "result (KJob *)" ), self ._get_result)


POST

Copy Source | Copy HTML
  1. self .job = KIO.storedHttpPost (QByteArray (urlencode ({ 'phone' : self .settings.get ( "phone" ), 'password' : self .settings.get ( "password" )})), \
  2. KUrl ( "</ code> <code> http://example.com/ </ code> <code> login" ), KIO.HideProgressInfo)
  3. self .job.addMetaData ( "Content-type" , "Content-Type: application / x-www-form-urlencoded" )
  4. self .job.addMetaData ( "Accept" , "Accept: text / plain" )
  5. self .job.addMetaData ( "User-Agent" , "User-Agent: bwc-balance-plasmoid" )
  6. self .connect ( self .job, SIGNAL ( "result (KJob *)" ), self ._get_result)


It remains only to describe the self._get_result () function:
Copy Source | Copy HTML
  1. def self ._get_result ( self , job):
  2. print job.data () # Just do something with the response received


Total



The process of working with Plasmoid settings, KWallet and KIO network functions was disassembled. I hope this example is enough for the number of useful plasmoids for KDE Desktop to increase noticeably in a short time. Hot plasma!

The full code of the plasmoid can be found here .

Source: https://habr.com/ru/post/76548/


All Articles