Difference between revisions of "Building Plugins:HelloMyth"

From MythTV Official Wiki
Jump to: navigation, search
(The UI File)
(CPP Files: VERBOSE is apparently deprecated and you should use LOG instead)
(9 intermediate revisions by 7 users not shown)
Line 1: Line 1:
{{outdated}}
 
 
[[Category:HOWTO]]
 
[[Category:HOWTO]]
 
[[Category:Developer_Documentation]]
 
[[Category:Developer_Documentation]]
Line 9: Line 8:
 
As a '''disclaimer''', I am also very new to plugin development and so please correct me where I might be wrong.
 
As a '''disclaimer''', I am also very new to plugin development and so please correct me where I might be wrong.
  
To start, download the mythplugin directory from [http://svn.mythtv.org/trac/ SVN] and create the following directory tree within its:
+
To start, download a copy of the MythTV source code repository from [https://github.com/organizations/MythTV github] and create the following directory tree within its:
  
 
<pre>
 
<pre>
svn co http://svn.mythtv.org/svn/trunk/mythplugins
+
git clone git://github.com/MythTV/mythtv.git
mkdir -p mythplugins/mythhello/mythhello
+
mkdir -p mythtv/mythplugins/mythhello/mythhello
 
</pre>
 
</pre>
  
You will also need to install some development tools if you don't already have them (''qmake-qt4'' for newer mythtv, or ''qmake'' for older versions). For example:
+
You will also need to install some development tools if you don't already have them (Qt4 development tools and headers). For example:
<pre>sudo apt-get install qt4-qmake
+
<pre>
 +
sudo apt-get install qt4-qmake libqt4-dev
 
</pre>
 
</pre>
  
Line 35: Line 35:
  
 
2) Create another file called mythhello.pro in the second mythhello (mythplugins/mythhello/mythhello/mythhello.pro) directory
 
2) Create another file called mythhello.pro in the second mythhello (mythplugins/mythhello/mythhello/mythhello.pro) directory
 +
{{Code box|mythhello.pro|
 
<pre>
 
<pre>
 
include ( ../../mythconfig.mak )
 
include ( ../../mythconfig.mak )
 
include ( ../../settings.pro )
 
include ( ../../settings.pro )
 +
include ( ../../programs-libs.pro )
 +
 
QT += network sql xml
 
QT += network sql xml
 
TEMPLATE = lib
 
TEMPLATE = lib
Line 60: Line 63:
 
}
 
}
 
</pre>
 
</pre>
 +
}}
  
 +
{{Note box|In my case also "''include ( ../../programs-libs.pro )''" was needed to compile it}}
  
The following items are specific to your plugin: 'uifiles.files', 'installfiles.files', 'HEADERS', 'SOURCES'.  Be sure that $${LIBDIR} and $${PREFIX} are set in mythplugins/mythconfig.mak to be the mythtv install prefix and library directory.  Mine are set to:
+
The following items are specific to your plugin: 'uifiles.files', 'installfiles.files', 'HEADERS', 'SOURCES'.  Be sure that $${LIBDIR} and $${PREFIX} are set in mythplugins/mythconfig.mak to be the MythTV install prefix and library directory.  Mine are set to:
 
<pre>
 
<pre>
 
PREFIX=/usr/
 
PREFIX=/usr/
Line 70: Line 75:
 
''(Note: I didn't find a mythplugins/mythconfig.mak but adding the above two lines to the top of my mythhello.mak worked to set the paths correctly - using a 0.21 system.)''
 
''(Note: I didn't find a mythplugins/mythconfig.mak but adding the above two lines to the top of my mythhello.mak worked to set the paths correctly - using a 0.21 system.)''
  
''(Note2: You can execute the configure script in mythplugins to generate the mythconfig.mak file.  Depending on where your mythtv is located it may be necessary to add the argument --prefix=/usr)''
+
''(Note2: You can execute the configure script in mythplugins to generate the mythconfig.mak file.  Depending on where your MythTV is located it may be necessary to add the argument --prefix=/usr)''
  
 
=== CPP Files ===
 
=== CPP Files ===
  
 
Now we create the cpp/h files which should be put in mythplugins/mythhello/mythhello/.
 
Now we create the cpp/h files which should be put in mythplugins/mythhello/mythhello/.
 
==== main.cpp ====
 
  
 
Here's the main file that takes care of initializing, running and destructing the pluigin:
 
Here's the main file that takes care of initializing, running and destructing the pluigin:
  
 +
{{Code box|main.cpp|
 
<pre>
 
<pre>
  
Line 154: Line 158:
 
      
 
      
 
}
 
}
 
 
 
  
 
</pre>
 
</pre>
 +
}}
  
==== mythhello.h ====
+
{{Code box|mythhello.h|
 
 
 
<pre>
 
<pre>
 
#ifndef MYTHHELLO_H
 
#ifndef MYTHHELLO_H
Line 199: Line 200:
  
 
</pre>
 
</pre>
 +
}}
  
==== mythhello.cpp ====
+
{{Code box|mythhello.cpp|
 
 
 
<pre>
 
<pre>
 
// POSIX headers
 
// POSIX headers
Line 231: Line 232:
 
     //example of how to find the configuration dir currently used.
 
     //example of how to find the configuration dir currently used.
 
     QString confdir = GetConfDir();
 
     QString confdir = GetConfDir();
     VERBOSE(VB_IMPORTANT, LOC + "Conf dir:"  + confdir);
+
     LOG(VB_GENERAL, LOG_DEBUG, LOC + "Conf dir:"  + confdir);
 
}
 
}
  
Line 252: Line 253:
 
     if (err)
 
     if (err)
 
     {
 
     {
         VERBOSE(VB_IMPORTANT, "Cannot load screen 'hello'");
+
         LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'hello'");
 
         return false;
 
         return false;
 
     }
 
     }
 
      
 
      
  
     if (!BuildFocusList())
+
     BuildFocusList();
    {
 
        VERBOSE(VB_IMPORTANT, LOC_ERR +
 
        "Failed to build a focuslist. Something is wrong");
 
    }
 
 
 
 
     SetFocusWidget(m_cancelButton);
 
     SetFocusWidget(m_cancelButton);
  
Line 271: Line 267:
 
     return true;
 
     return true;
 
}
 
}
 +
 
bool MythHello::keyPressEvent(QKeyEvent *event)
 
bool MythHello::keyPressEvent(QKeyEvent *event)
 
{
 
{
Line 306: Line 303:
 
     m_outText->SetText(QString("Cancel clicked"));
 
     m_outText->SetText(QString("Cancel clicked"));
 
}
 
}
 
 
  
 
</pre>
 
</pre>
 +
}}
  
 
=== The UI File ===
 
=== The UI File ===
Line 315: Line 311:
 
What would a plugin be without a UI?  Now create hello-ui.xml and put it in mythplugins/mythhello/mythhello/.
 
What would a plugin be without a UI?  Now create hello-ui.xml and put it in mythplugins/mythhello/mythhello/.
  
 +
{{Code box|hello-ui.xml|
 
<pre>
 
<pre>
 
<?xml version="1.0" encoding="utf-8"?>
 
<?xml version="1.0" encoding="utf-8"?>
Line 348: Line 345:
  
 
</pre>
 
</pre>
 +
}}
  
 
=== Building ===
 
=== Building ===
Line 360: Line 358:
 
</pre>
 
</pre>
  
Make sure everything compiled OK and the files where copied to your mythtv installation directories.
+
Make sure everything compiled OK and the files where copied to your MythTV installation directories.
  
 
{{Note box|For newer versions of MythTV, ''qmake'' should be replaced by ''qmake-qt4''.}}
 
{{Note box|For newer versions of MythTV, ''qmake'' should be replaced by ''qmake-qt4''.}}
Line 387: Line 385:
  
 
[[MythPlugin Architecture]]
 
[[MythPlugin Architecture]]
 +
 +
[[Building_Plugins:MythNotes]] - Another plug-in tutorial
  
 
[[Category:HOWTO]]
 
[[Category:HOWTO]]

Revision as of 19:48, 17 August 2013


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.

To start, download a copy of the MythTV source code repository from github and create the following directory tree within its:

git clone git://github.com/MythTV/mythtv.git
mkdir -p mythtv/mythplugins/mythhello/mythhello

You will also need to install some development tools if you don't already have them (Qt4 development tools and headers). For example:

sudo apt-get install qt4-qmake libqt4-dev

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

Script.png 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
}


Important.png Note: In my case also "include ( ../../programs-libs.pro )" was needed to compile it

The following items are specific to your plugin: 'uifiles.files', 'installfiles.files', 'HEADERS', 'SOURCES'. Be sure that $${LIBDIR} and $${PREFIX} are set in mythplugins/mythconfig.mak to be the MythTV install prefix and library directory. Mine are set to:

PREFIX=/usr/
LIBDIR=/usr/lib/

(Note: I didn't find a mythplugins/mythconfig.mak but adding the above two lines to the top of my mythhello.mak worked to set the paths correctly - using a 0.21 system.)

(Note2: You can execute the configure script in mythplugins to generate the mythconfig.mak file. Depending on where your MythTV is located it may be necessary to add the argument --prefix=/usr)

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 pluigin:


Script.png main.cpp



// 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)
{
    
}


Script.png 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);
        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 */


Script.png 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/.


Script.png 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:

su
qmake-qt4
make
make install

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


Important.png Note: For newer versions of MythTV, qmake should be replaced by qmake-qt4.

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