Getting Started with MythTV Plugin Development

I’m a big fan of MythTV, having used it for years as a very comprehensive media centre.

MythTV also provides an excellent extension framework, which is a good excuse as any to have a bash at some development under Linux for a change.

This post starts at the beginning: getting a hello world plugin compiled and running. This is based heavily on the HelloMyth page on the MythTV Wiki, with tweaks needed for a vanilla MythTV 0.23 installation under Ubuntu 10.10.

Prerequisites

Assuming that you have a working MythTV installation, you will also need the following packages:

sudo apt-get install libmyth-dev qt4-qmake build-essential ccache libqt4-dev libfreetype6-dev libmp3lame-dev libasound2-dev libjack-dev libraw1394-dev libiec61883-dev libavc1394-dev libxv-dev libxvmc-dev

Once those are installed, grab a copy of the standard MythTV plugins from the myth git repository; note that you’ll want to work with the branch of MythTV you have installed (unless you’re working with the trunk version).

git clone -b fixes/0.23 git://github.com/MythTV/mythtv.git

Generate a config file by running the configure script in the mythplugins directory (You may or may not need the prefix argument, this is based on an Ubuntu 10.10 install):

./configure –prefix=/usr

Code

In the mythplugins folder, create a mythhello folder and add the following file:

mythhello/mythhello.pro

TEMPLATE = subdirs

# Directories
SUBDIRS = mythhello

Now inside the mythhello folder, create another mythhello folder, and add the following files:

mythhello/mythhello/mythhello.pro

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

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

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

INSTALLS += uifiles

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

macx {
    QMAKE_LFLAGS += -flat_namespace -undefined suppress
}

mythhello/mythhello/main.cpp (Entry point for the plugin)

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

// QT headers
#include <QApplication>

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

// MythHello headers
#include "mythhello.h"


using namespace std;

void runHello(void);
int  RunHello(void);

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


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

void runHello(void)
{
    RunHello();
}

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;
    }
}

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

int mythplugin_config(void)
{
   
}

mythhello/mythhello/mythhello.h (Plugin header)

#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);
        bool keyPressEvent(QKeyEvent *);
   
       
    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/mythhello/mythhello.cpp (Main plugin body)

// 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();
    VERBOSE(VB_IMPORTANT, 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)
    {
        VERBOSE(VB_IMPORTANT, "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"));
}

mythhello/mythhello/hello-ui.xml (Plugin UI definition)

<?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

Change to mythplugins/mythhello and run the following:

qmake-qt4
make
sudo make install

Everything should be compiled and the plugin copied to /usr/lib/mythtv/plugins/.

To get the plugin to show up in the main menu edit /usr/share/mythtv/themes/defaultmenu/mainmenu.xml and add a new button entry:

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

On starting mythfrontend at this point, the plugin should show up on the main menu.

Summary

At this point we have a working MythTV plugin which will serve as a good starting point for building further functionality.

Useful Links

MythTV Wiki HelloMyth Plugin
MythTV SVN

Leave a Reply

Your email address will not be published. Required fields are marked *