Difference between revisions of "Development/Tutorials/Games/Palapeli Slicers"

(No difference)

Revision as of 14:18, 27 September 2009

Creating a Palapeli pattern
Tutorial Series   Programming with the Palapeli API
Previous   Introduction to KDE4 programming
What's Next   n/a
Further Reading   API reference for libpala
I'm currently updating this article to the new API. -- Majewsky 13:25, 27 September 2009 (UTC)


This tutorial shows you how to create a slicer for Palapeli, that is: a plugin for Palapeli that splits an image into pieces.

As an example for a very basic slicer, we will discuss the structure of the rectangle slicer, which splits the image into a configurable number of evenly-sized pieces.

Technical overview

Overview of the Palapeli infrastructure

Slicer writers do not have to bother with the changes that occur in the Palapeli application every now and then. Slicer plugins are not linked against Palapeli, but against libpala, a light-weight library that is designed for the sole purpose of slicing management. To Palapeli, it serves as an interface to talk with arbitrary slicer plugins. To slicer plugins, it provides an API to get and perform slicing jobs.


A slicer plugin needs to define a subclass of Pala::Slicer. Of course, you can also define more classes, but libpala will only talk to the single Pala::Slicer subclass.

Below this line follows everything that has not been edited yet.

A pattern plugin consists of two classes. The first one (derived from Palapeli::PatternConfiguration) tells Palapeli what features this pattern plugin has and which configuration values it needs. The second one (derived from Palapeli::Pattern) does the actual slicing.

The code: mypattern.h

  1. ifndef MYPATTERN_H
  2. define MYPATTERN_H
  1. include <Palapeli/Pattern>
  2. include <Palapeli/PatternConfiguration>

class MyPattern : public Palapeli::Pattern {

       MyPattern(const QString& type);
       virtual ~MyPattern() {}
       virtual int estimatePieceCount() const;
       virtual void doSlice(const QImage& image);
       QString m_type;


class MyPatternConfiguration : public Palapeli::PatternConfiguration {

       MyPatternConfiguration(QObject* parent = 0, const QVariantList& args = QVariantList());
       virtual ~MyPatternConfiguration() {}
       virtual Palapeli::Pattern* createPattern() const;


  1. endif // MYPATTERN_H

As described above, we have declared two classes deriving from the base classes Palapeli::Pattern and Palapeli::PatternConfiguration. For this simple example, we do only reimplement constructors, destructors and some pure virtual functions.

The code: mypattern.cpp

  1. include "mypattern.h"
  1. include <QImage>
  2. include <KLocalizedString>
  3. include <KPluginFactory>
  4. include <KPluginLoader>

K_PLUGIN_FACTORY(MyPatternFactory, registerPlugin<MyPatternConfiguration>();) K_EXPORT_PLUGIN(MyPatternFactory("mypattern"))

MyPattern::MyPattern(const QString& type)

   : Palapeli::Pattern()
   , m_type(type)

{ }

int MyPattern::estimatePieceCount() const {

   return 2;


void MyPattern::doSlice(const QImage& image) {

   int pieceWidth, pieceHeight;
   QImage leftPiece, rightPiece;
   if (m_type == i18n("Vertical"))
       pieceWidth = image.width() / 2;
       pieceHeight = image.height();
       //create piece images
       leftPiece = image.copy(QRect(0, 0, pieceWidth, pieceHeight));
       rightPiece = image.copy(QRect(pieceWidth, 0, pieceWidth, pieceHeight));
       //add the pieces to the puzzle scene
       addPiece(leftPiece, QRectF(0, 0, pieceWidth, pieceHeight));
       addPiece(rightPiece, QRectF(pieceWidth, 0, pieceWidth, pieceHeight));
   else //m_type == i18n("Horizontal")
       //like in the other branch, only with other metrics
       pieceWidth = image.width();
       pieceHeight = image.height() / 2;
       leftPiece = image.copy(QRect(0, 0, pieceWidth, pieceHeight));
       rightPiece = image.copy(QRect(0, pieceHeight, pieceWidth, pieceHeight));
       addPiece(leftPiece, QRectF(0, 0, pieceWidth, pieceHeight));
       addPiece(rightPiece, QRectF(0, pieceHeight, pieceWidth, pieceHeight));
   //define a neighbor relation between the pieces
   addRelation(0, 1); //0 and 1 are the consecutive indices of the pieces


MyPatternConfiguration::MyPatternConfiguration(QObject* parent, const QVariantList& args)

   : Palapeli::PatternConfiguration(parent, args)


   addProperty("splitting", Palapeli::PatternConfiguration::String, i18n("Splitting direction:"));
   QVariantList options; options << i18n("Horizontal") << i18n("Vertical");
   addPropertyParameters("splitting", options);


Palapeli::Pattern* MyPatternConfiguration::createPattern() const {

   return new MyPattern(property("splitting").toString());


We will start with the MyPatternConfiguration class, because this is the logical entry point. The pattern configuration class is created when Palapeli is started. (The constructor arguments are necessary for the plugin loader, you will not need to bother with them as long as you pass them to the base class constructor.) Its job is to manage the pattern's settings.

In this case, one is able to configure whether the image should be splitted in horizontal or in vertical direction. A configuration option is described by a property. (These have nothing to do with QObject's property system.) Properties are defined with a data type. Note that property data is always passed as QVariant, the data type does only determine which widgets should be used in the configuration interface for the pattern. Legal data types include String (represented by a KLineEdit) or Integer (through KIntSpinBox). The creation of those widgets is done in the Palapeli::PatternConfiguration class, you do only need to define the properties correctly.

When we define our "splitting" property, we assign the data type String to it (from the Palapeli::PatternConfiguration::DataType enumeration). Also, we give some parameters to this property. It depends on the data type how these parameters are used. In this case, the parameters define a list of valid inputs to this property. Therefore, not a line edit is used in the configuration, but a combo box, which limits the user input to the given strings.

When the user has configured its game (and therefore the pattern), the Palapeli game engine will call the createPattern method to create a Palapeli::Pattern object. If you have added properties, read them and pass them to the constructor of your pattern object.

Now we have a Palapeli::Pattern instance. Its main purpose is the doSlice method which takes a method. In this implementation, we copy the left and the right part (or the upper and the lower part) of the image to get two pieces. To add these pieces to Palapeli's puzzle scene, use the base class' addPiece method. It will properly report it to Palapeli.

After we have added all pieces, we need to define neighbor relations between pieces. Neighbor relations are used to snap pieces together when they're near enough. Consider three pieces in a row: If piece 1 is near piece 3, nothing should snap because they do not have a common edge. If piece 1 is moved near piece 2, these should snap together. Therefore, we define a relation between piece 1 and piece 2 (and piece 2 and piece 3, respectively). The relation is added through the addRelation function. In our case (line 29), we define a relation between the first piece (index 0) and the second piece (index 1). The piece indices are based on the order in which you added the pieces to the scene, and start at zero.

Integrate into Palapeli: mypattern.desktop

[Desktop Entry] X-KDE-Library=mypattern X-KDE-PluginInfo-Author=The best KDE hacker [email protected] X-KDE-PluginInfo-Name=mypattern X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://kde-hackers.example.org/palapelipatterns X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-ParentApp=libpalapelipattern X-KDE-ServiceTypes=Palapeli/PatternPlugin Type=Service Icon=palapeli-pattern-mypattern PatternIdentifier=mypattern Name=My pattern Name[de]=Mein Schnittmuster Comment=The best pattern in the world Comment[de]=Das beste Schnittmuster auf der ganzen Welt

We need to tell the Palapeli library that there is a new plugin out there. We need a desktop entry file like shown here. You can mostly copy everything you see here; as long as you keep the fields X-KDE-Library and X-KDE-PluginInfo-Name in sync with the library name (see next section). You should not change the PatternIdentifier field because that will invalidate saved games created with previous versions. Note also that the name is used in the "New puzzle" dialog to identify this pattern. (It can be translated; for example, a German translation has been added in the above example.)

Build everything: CMakeLists.txt


find_package(KDE4 REQUIRED) find_package(Palapeli REQUIRED)

add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) include_directories(${KDE4_INCLUDES} ${PALAPELI_INCLUDE_DIR})

set(mypattern_SRCS mypattern.cpp )

kde4_add_plugin(mypattern ${mypattern_SRCS}) target_link_libraries(mypattern ${KDE4_KDEUI_LIBS} palapelipattern)


Everything of this is pretty straightforward if you have previous experience with CMake (and this is what I assume). Make sure you compile everything as a plugin, and install it into the plugin directory. After having installed the plugin, run kbuildsycoca4. This will locate your plugin, and enable Palapeli to use it. Here is how it should look like then:

Palapeli Pattern Tutorial1.png

Content is available under Creative Commons License SA 4.0 unless otherwise noted.