Building Plugins:HelloMyth

From MythTV Official Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.


Creating Your First Plugin: Hello Myth

This is a really simple skeleton plugin that doesn't do anything. It'll show you what is absolutely necessary to create a plugin, then you can expand on that by dissecting others.

As a disclaimer, I am also very new to plugin development and so please correct me where I might be wrong.

Before you start you may need to install some development tools if you don't already have them (Qt5 development tools and headers) (For reference you can see a full list of dependencies the Ubuntu packaging needs to build here. For example:

sudo apt-get install qt5-qmake libqt5-dev ccache git g++ libmyth-dev

NOTE: a quick way for those using Ubuntu to install all the required build dependencies is to use this (you will have to find and uncomment the correct deb-src line from /etc/apt/sources.list.d/ for this to work):

sudo apt-get build-dep mythtv

Now lets get a copy of the MythTV source code from the git repository at github:

git clone git://github.com/MythTV/mythtv.git

By default a new checkout from git will use the master branch but you can choose any branch you require for example to use the fixes/31 branch do this:

git checkout fixes/31

At this point it would be wise to make sure you can build, install and run the clean sources before making any changes (change the configure parameters as required - the example assumes you want to install to the same place as some packages like Ubuntu do).

cd mythtv
./configure --prefix=/usr
make -j4
sudo make install
cd ../mythplugins
./configure --prefix=/usr
make -j4
sudo make install

You can now test the install to make sure everything is working correctly.

Now lets add our new plugin - create the following directory tree within the mythplugins directory:

cd mythtv/mythplugins
mkdir -p mythhello/mythhello

mythhello.pro Files

The *.pro files are used by qmake to create the Makefiles.

1) Create a file called mythhello.pro in the first mythhello directory:

TEMPLATE = subdirs

# Directories
SUBDIRS = mythhello

"mythhello" refers to the second mythhello directory.

2) Create another file called mythhello.pro in the second mythhello (mythplugins/mythhello/mythhello/mythhello.pro) directory

mythhello.pro

include ( ../../mythconfig.mak )
include ( ../../settings.pro )
include ( ../../programs-libs.pro )

QT += network sql xml widgets

TEMPLATE = lib
CONFIG += plugin thread
TARGET = mythhello
target.path = $${LIBDIR}/mythtv/plugins
INSTALLS += target

INCLUDEPATH += $${PREFIX}/include/mythtv
INCLUDEPATH += $${PREFIX}/include/mythtv/libmythui
INCLUDEPATH += $${PREFIX}/include/mythtv/libmythbase

installfiles.path = $${PREFIX}/share/mythtv/themes/default
installfiles.files = hello-ui.xml

INSTALLS += installfiles

# Input
HEADERS += mythhello.h
SOURCES += main.cpp mythhello.cpp

DEFINES += MPLUGIN_API

use_hidesyms {
    QMAKE_CXXFLAGS += -fvisibility=hidden
}

android {
    # to discriminate plugins in a flat directory structure
    TARGET = mythplugin$${TARGET}
}

include ( ../../libs-targetfix.pro )

CPP Files

Now we create the cpp/h files which should be put in mythplugins/mythhello/mythhello/.

Here's the main file that takes care of initializing, running and destructing the plugin:


main.cpp

// C++ headers
#include <unistd.h>

// QT headers
#include <QApplication>

// MythTV headers
#include <mythcontext.h>
#include <mythcorecontext.h>
#include <mythplugin.h>
#include <mythpluginapi.h>
#include <mythversion.h>
#include <mythmainwindow.h>

// MythHello headers
#include "mythhello.h"


using namespace std;

static int RunHello(void)
{
    MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
    
    MythHello *mythhello = new MythHello(mainStack, "hello");
    
    if (mythhello->Create())
    {
        mainStack->AddScreen(mythhello);
        return 0;
    }
    else
    {
        delete mythhello;
        return -1;
    }
}

static void runHello(void)
{
    RunHello();
}

static void setupKeys(void)
{
    REG_JUMP("MythHello", QT_TRANSLATE_NOOP("MythHello",
        "Sample plugin"), "", runHello);
}

int mythplugin_init(const char *libversion)
{
    if (!gCoreContext->TestPluginVersion("mythhello",
                                         libversion,
                                         MYTH_BINARY_VERSION))
        return -1;
    
    setupKeys();
    
    return 0;
}

int mythplugin_run(void)
{
    return RunHello();
}

int mythplugin_config(void)
{
    return 0;
}


mythhello.h

#ifndef MYTHHELLO_H
#define MYTHHELLO_H

// MythTV headers

#include <mythscreentype.h>
#include <mythuibutton.h>


/** \class MythHello
*  \brief Example plugin.  Shows how to use Text areas and buttons
*/
class MythHello : public MythScreenType
{
    Q_OBJECT
    
    public:
        MythHello(MythScreenStack *parent, QString name);
        bool Create(void) override; // MythScreenType
        bool keyPressEvent(QKeyEvent *) override; // MythScreenType
    
        
    private:
        MythUIText *m_titleText;
        MythUIText *m_outText;
        MythUIButton   *m_cancelButton;
        MythUIButton   *m_okButton;

    private slots:
        void ok_clicked(void);
        void cancel_clicked(void);
};

#endif /* MYTHHELLO_H */


mythhello.cpp

// POSIX headers
#include <unistd.h>

// MythTV headers
#include <mythuibutton.h>
#include <mythuitext.h>
#include <mythmainwindow.h>
#include <mythcontext.h>
#include <mythdirs.h>

// MythHello headers
#include "mythhello.h"

#define LOC      QString("MythHello: ")
#define LOC_WARN QString("MythHello, Warning: ")
#define LOC_ERR  QString("MythHello, Error: ")

/** \brief Creates a new MythHello Screen
*  \param parent Pointer to the screen stack
*  \param name The name of the window
*/
MythHello::MythHello(MythScreenStack *parent, QString name) :
    MythScreenType(parent, name),
    m_cancelButton(NULL)

{
    //example of how to find the configuration dir currently used.
    QString confdir = GetConfDir();
    LOG(VB_GENERAL, LOG_DEBUG, LOC + "Conf dir:"  + confdir);
}

bool MythHello::Create(void)
{
    bool foundtheme = false;

    // Load the theme for this screen
    foundtheme = LoadWindowFromXML("hello-ui.xml", "hello", this);
    
    if (!foundtheme)
        return false;
    
    bool err = false;
    UIUtilE::Assign(this, m_titleText, "title", &err);
    UIUtilE::Assign(this, m_outText, "outtext", &err);
    UIUtilE::Assign(this, m_cancelButton, "cancel", &err);
    UIUtilE::Assign(this, m_okButton, "ok", &err);

    if (err)
    {
        LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'hello'");
        return false;
    }
    

    BuildFocusList();
    SetFocusWidget(m_cancelButton);

    connect(m_okButton, SIGNAL(Clicked()), this, SLOT(ok_clicked()));
    connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(cancel_clicked()));

    
    return true;
}

bool MythHello::keyPressEvent(QKeyEvent *event)
{
    if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
        return true;
    
    bool handled = false;
    QStringList actions;
    handled = GetMythMainWindow()->TranslateKeyPress("Hello", event, actions);

    for (int i = 0; i < actions.size() && !handled; i++)
    {
        QString action = actions[i];
        handled = true;

        if (action == "ESCAPE")
            Close();
        else
            handled = false;
    }
    
    if (!handled && MythScreenType::keyPressEvent(event))
        handled = true;

    return handled;
}

void MythHello::ok_clicked(void)
{
    m_outText->SetText(QString("OK clicked"));
}

void MythHello::cancel_clicked(void)
{
    m_outText->SetText(QString("Cancel clicked"));
}

The UI File

What would a plugin be without a UI? Now create hello-ui.xml and put it in mythplugins/mythhello/mythhello/.


hello-ui.xml

<?xml version="1.0" encoding="utf-8"?>
<mythuitheme>
    <window name="hello">
        <textarea name="title">
            <area>0,10,800,100</area>
            <font>large</font>
            <align>allcenter</align>
            <multiline>yes</multiline>
            <value>Hello World</value>
        </textarea>

        <textarea name="outtext">
            <area>0,300,800,100</area>
            <font>large</font>
            <align>allcenter</align>
            <multiline>yes</multiline>
        </textarea>

        <button name="cancel" from="basebutton">
            <position>500,400</position>
            <value>Cancel</value>
        </button>

        <button name="ok" from="basebutton">
            <position>200,400</position>
            <value>OK</value>
        </button>
    </window>
</mythuitheme>


Building

Go to the top mythhello directory (mythplugins/mythhello) and run these commands:

qmake
make
sudo make install

Make sure everything compiled OK and the files where copied to your MythTV installation directories.

Adding MythHello to your Menus

As a final step you have to add your plugin to the main MythTv menu -- in my case /usr/share/mythtv/mainmenu.xml. Add the following anywhere in the XML file between the <mythmenu> root tag:

<button>
   <type>HELLO_MYTH</type>
   <text>Hello Myth</text>
   <action>PLUGIN mythhello</action>
</button>

Finishing Up

Now restart MythFrontend and you should see a 'Hello Myth' button on the main menu.

Good Luck

- Mozmonkey

More about plugins

MythPlugin Architecture

Building_Plugins:MythNotes - Another plug-in tutorial

If you wish to compile the existing set of plugins, you will need at least the following packages (it is a long list):

sudo apt-get install libxinerama-dev libxv-dev libxrandr-dev libxml2-dev libavahi-compat-libdnssd-dev libssl-dev libass-dev libfftw3-dev libmp3lame-dev libva-dev libasound2-dev libpulse-dev libsdl1.2-dev libfreetype6-dev libpng12-dev libx264-dev libvpx-dev libcrystalhd-dev libudev-dev uuid-dev libbz2-dev libmysqlclient-dev python-oauth libdate-manip-perl libdatetime-format-iso8601-perl libimage-size-perl libsoap-lite-perl libjson-perl dcraw libcdio-paranoia-dev libraw1394-dev libiec61883-dev libavc1394-dev libtag1-dev libflac-dev libvorbis-dev libexif-dev