博客首页 注册 建议与交流 排行榜 加入友情链接
推荐 投诉 搜索: 帮助

寻觅

笨笨的小窝
benbenxiaobai.cublog.cn


Creating Custom Widgets with Plugins--待补充和说明
Creating Custom Widgets with Plugins
This section will show you how to write a custom widget and how to embed the custom widget into a plugin. There are no restrictions or special considerations that must be taken into account when creating a widget that is destined to become a plugin. If you are an experienced Qt programmer you can safely skip the section on creating a custom widget and go directly to Creating a Plugin.

Creating a Custom Widget
A custom widget is often a specialization (subclass) of another widget or a combination of widgets working together or a blend of both these approaches. If you simply want a collection of widgets in a particular configuration it is easiest to create them, select them as a group, and copy and paste them as required within Qt Designer. Custom widgets are generally created when you need to add new functionality to existing widgets or groups of widgets.

We have two recommendations that you should consider when creating a custom widget for a plugin:
   Using Qt's property system will provide Qt Designer users with a direct means of configuring the widget through the property editor. (See the Qt Properties documentation.)

    Consider making your widget's public 'set' functions into public slots so that you can perform signal-slot connections with the widget in Qt Designer.
In the course of this chapter we will create a simple but useful widget, 'FileChooser', which we'll later make available in Qt Designer as a plugin. In practice most custom widgets are created to add functionality rather than to compose widgets, so we will create our widget in code rather than using Qt Designer to reflect this approach. FileChooser consists of a QLineEdit and a QPushButton. The QLineEdit is used to hold a file or directory name, the QPushButton is used to launch a file dialog through which the user can choose a file or directory.

The FileChooser Custom Widget
   If you've followed the manual up to this point you may well be able to create this custom widget yourself. If you're confident that you can make your own version of the widget, or have another widget that you want to turn into a plugin, skip ahead to Creating a Plugin. If you prefer to read how we created the widget then read on.
Coding the Widget's Interface
We will work step-by-step through the widget's header file, qt/tools/designer/examples/filechooser/widget/filechooser.h.
    #include <qwidget.h>
    #include <qwidgetplugin.h>

    class QLineEdit;
    class QPushButton;
Our widget will be derived from QWidget so we include the qwidget.h header file. We also forward declare the two classes that our widget will be built from.
We include the Q_OBJECT macro since this is required for classes that declare signals or slots. The Q_ENUMS declaration is used to register the Mode enumeration. Our widget has two properties, mode, to store whether the user should select a File or a Directory and fileName which stores the file or directory they chose.
    class QT_WIDGET_PLUGIN_EXPORT FileChooser : public QWidget
    {
        Q_OBJECT

        Q_ENUMS( Mode )
        Q_PROPERTY( Mode mode READ mode WRITE setMode )
        Q_PROPERTY( QString fileName READ fileName WRITE setFileName )

    public:
        FileChooser( QWidget *parent = 0, const char *name = 0);

        enum Mode { File, Directory };

        QString fileName() const;
        Mode mode() const;
The constructor is declared in the standard way for widgets. We declare two public functions, fileName() to return the filename, and mode() to return the mode.
    public slots:
        void setFileName( const QString &fn );
        void setMode( Mode m );

    signals:
        void fileNameChanged( const QString & );

    private slots:
        void chooseFile();
The two 'set' functions are declared as public slots. setFileName() and setMode() set the filename and mode respectively. We declare a single signal, fileNameChanged(). The private slot, chooseFile() is called by the widget itself when its button is clicked.
    private:
        QLineEdit *lineEdit;
        QPushButton *button;
        Mode md;

    };
A pointer to QLineEdit and QPushButton, as well as a Mode variable are held as private data.
Coding the Implementation
We will work step-by-step through the implementation which is in qt/tools/designer/examples/filechooser/widget/filechooser.cpp.
    FileChooser::FileChooser( QWidget *parent, const char *name )
        : QWidget( parent, name ), md( File )
    {
The constructor passes the parent and name to its superclass, QWidget, and also initializes the private mode data, md, to File mode.
        QHBoxLayout *layout = new QHBoxLayout( this );
        layout->setMargin( 0 );

        lineEdit = new QLineEdit( this, "filechooser_lineedit" );
        layout->addWidget( lineEdit );
We begin by creating a horizontal box layout (QHBoxLayout) and add a QLineEdit and a QPushButton to it.
        connect( lineEdit, SIGNAL( textChanged( const QString & ) ),
                 this, SIGNAL( fileNameChanged( const QString & ) ) );

        button = new QPushButton( "...", this, "filechooser_button" );
        button->setFixedWidth( button->fontMetrics().width( " ... " ) );
        layout->addWidget( button );

        connect( button, SIGNAL( clicked() ),
                 this, SLOT( chooseFile() ) );

We connect the lineEdit'stextChanged() signal to the custom widget's fileNameChanged() signal. This ensures that if the user changes the text in the QLineEdit this fact will be propagated via the custom widget's own signal. The button'sclicked() signal is connected to the custom widget's chooseFile() slot which invokes the appropriate dialog for the user to choose their file or directory.
        setFocusProxy( lineEdit );
    }
    We set the lineEdit as the focus proxy for our custom widget. This means that when the widget is given focus the focus actually goes to the lineEdit.
    void FileChooser::setFileName( const QString &fn )
    {
        lineEdit->setText( fn );
    }

    QString FileChooser::fileName() const
    {
        return lineEdit->text();
    }

The setFileName() function sets the filename in the QLineEdit, and the fileName() function returns the filename from the QLineEdit. The setMode() and mode() functions (not shown) are similarly set and return the given mode.
    void FileChooser::chooseFile()
    {
        QString fn;
        if ( mode() == File )
            fn = QFileDialog::getOpenFileName( lineEdit->text(), QString::null, this );
        else
            fn = QFileDialog::getExistingDirectory( lineEdit->text(),this );

        if ( !fn.isEmpty() ) {
            lineEdit->setText( fn );
            emit fileNameChanged( fn );
        }
    }

     When chooseFile() is called it presents the user with a file or directory dialog depending on the mode. If the user chooses a file or directory the QLineEdit is updated with the chosen file or directory and the fileNameChanged() signal is emitted.
Although these two files complete the implementation of the FileChooser widget it is good practice to write a test harness to check that the widget behaves as expected before attempting to put it into a plugin.
Testing the Implementation。

   We present a rudimentary test harness which will allow us to run our custom widget. The test harness requires two files, a main.cpp to contain the FileChooser, and a .pro file to create the Makefile from. Here is qt/tools/designer/examples/filechooser/widget/main.cpp:
    #include <qapplication.h>
    #include "filechooser.h"

    int main( int argc, char ** argv )
    {
        QApplication a( argc, argv );
        FileChooser *fc = new FileChooser;
        fc->show();
        return a.exec();
    }
And here is qt/tools/designer/examples/filechooser/widget/filechooser.pro
TEMPLATE = app
LANGUAGE = C++
TARGET   = filechooser

SOURCES += filechooser.cpp main.cpp
HEADERS += filechooser.h
CONFIG  += qt warn_on release
DBFILE  = filechooser.db
DEFINES += FILECHOOSER_IS_WIDGET
 
   We can create the makefile using qmake: qmake -o Makefile filechooser.pro, then we can make and run the harness to test our new widget. Once we're satisfied that the custom widget is robust and has the behaviour we require we can embed it into a plugin.

Creating a Plugin
  Qt Plugins can be used to provide self-contained software components for Qt applications. Qt currently supports the creation of five kinds of plugins: codecs, image formats, database drivers, styles and custom widgets. In this section we will explain how to convert our filechooser custom widget into a Qt Designer custom widget plugin.

    A Qt Designer custom widget plugin is always derived from QWidgetPlugin. The amout of code that needs to be written is minimal.

   To make your own plugin it is probably easiest to start by copying our exampleplugin.h andplugin.cpp files and changing 'CustomWidgetPlugin' to the name you wish to use for your widget plugin implementation class. Below we provide an introduction to the header file although it needs no changes beyond class renaming. The implementation file requires simple changes, mostly more class renaming; we will review each function in turn and explain what you need to do.

   The CustomWidgetPlugin Implementation
We have called our header fileplugin.h and we've called our plugin class CustomWidgetPlugin since we will be using our plugin class to wrap our custom widgets. We present the entire header file to give you an impression of the scope of the implementation required. Most of the functions require just a few lines of code.
    #include <qwidgetplugin.h>

    class CustomWidgetPlugin : public QWidgetPlugin
    {
    public:
        CustomWidgetPlugin();

        QStringList keys() const;
        QWidget* create( const QString &classname, QWidget* parent = 0, const char* name = 0 );
        QString group( const QString& ) const;
        QIconSet iconSet( const QString& ) const;
        QString includeFile( const QString& ) const;
        QString toolTip( const QString& ) const;
        QString whatsThis( const QString& ) const;
        bool isContainer( const QString& ) const;
    };
From qt/tools/designer/examples/filechooser/plugin/plugin.h
The QWidgetPlugin Functions

     Create your own plugin .cpp file by copying our plugin.cpp file and changing all occurrences of 'CustomWidgetPlugin' to the name you wish to use for your widget plugin implementation. Most of the other changes are simply replacing the name of our custom control, 'FileChooser', with the name of your custom control. You may need to add extra else if clauses if you have more than one custom control in your plugin implementation.
We'll now look at the constructor.
    CustomWidgetPlugin::CustomWidgetPlugin()
    {
    }
The constructor does not have to do anything. Simply copy ours with the class name you wish to use for your widget plugin implementation.
No destructor is necessary.
The keys function.
    QStringList CustomWidgetPlugin::keys() const
    {
        QStringList list;
        list << "FileChooser";
        return list;
    }
   For each widget class that you want to wrap in the plugin implementation you should supply a key by which the class can be identified. This key must be your class's name, so in our example we add a single key, 'FileChooser'.
The create() function.
    QWidget* CustomWidgetPlugin::create( const QString &key, QWidget* parent, const char* name )
    {
        if ( key == "FileChooser" )
            return new FileChooser( parent, name );
        return 0;
    }
    In this function we create an instance of the requested class and return a QWidget pointer to the newly created widget. Copy this function changing the class name and the feature name and create an instance of your widget just as we've done here. (See the Qt Plugin documentation for more information.)
The includeFile() function.
    QString CustomWidgetPlugin::includeFile( const QString& feature ) const
    {
        if ( feature == "FileChooser" )
            return "filechooser.h";
        return QString::null;
    }
This function returns the name of the include file for the custom widget. Copy this function changing the class name, key and include filename to suit your own custom widget.
The group(), iconSet(), toolTip() and whatsThis() functions.
    QString CustomWidgetPlugin::group( const QString& feature ) const
    {
        if ( feature == "FileChooser" )
            return "Input";
        return QString::null;
    }

    QIconSet CustomWidgetPlugin::iconSet( const QString& ) const
    {
        return QIconSet( QPixmap( filechooser_pixmap ) );
    }

    QString CustomWidgetPlugin::includeFile( const QString& feature ) const
    {
        if ( feature == "FileChooser" )
            return "filechooser.h";
        return QString::null;
    }

    QString CustomWidgetPlugin::toolTip( const QString& feature ) const
    {
        if ( feature == "FileChooser" )
            return "File Chooser Widget";
        return QString::null;
    }

    QString CustomWidgetPlugin::whatsThis( const QString& feature ) const
    {
        if ( feature == "FileChooser" )
            return "A widget to choose a file or directory";
        return QString::null;
    }
 
   We use the group() function to identify which Qt Designer toolbar group this custom widget should be part of. If we use a name that is not in use Qt Designer will create a new toolbar group with the given name. Copy this function, changing the class name, key and group name to suit your own widget plugin implementation.

   The iconSet() function returns the pixmap to use in the toolbar to represent the custom widget. The toolTip() function returns the tooltip text and the whatsThis() function returns the Whats This text. Copy each of these functions changing the class name, key and the string you return to suit your own widget plugin implementation.
The isContainer() function.
    bool CustomWidgetPlugin::isContainer( const QString& ) const
    {
        return FALSE;
    }
   Copy this function changing the class name to suit your widget plugin implementation. It should return TRUE if your custom widget can contain other widgets, e.g. like QFrame, or FALSE if it must not contain other widgets, e.g. like QPushButton.

The Q_EXPORT_PLUGIN macro.
    Q_EXPORT_PLUGIN( CustomWidgetPlugin )

   This macro identifies the module as a plugin -- all the other code simply implements the relevant interface, i.e. wraps the classes you wish to make available.
This macro must appear once in your plugin. It should be copied with the class name changed to the name of your plugin's class. (See the Qt Plugin documentation for more information on the plugin entry point.)

   Each widget you wrap in a widget plugin implementation becomes a class that the plugin implementation offers. There is no limit to the number of classes that you may include in an plugin implementation.

  The Project File
The project file for a plugin is somewhat different from an application's project file but in most cases you can use our project file changing only the HEADERS and SOURCES lines.
TEMPLATE = lib
LANGUAGE = C++
TARGET   = filechooser

SOURCES  += plugin.cpp ../widget/filechooser.cpp
HEADERS  += plugin.h ../widget/filechooser.h
DESTDIR   = ../../../../../plugins/designer

target.path=$$plugins.path

INSTALLS    += target
CONFIG      += qt warn_on release plugin
INCLUDEPATH += $$QT_SOURCE_TREE/tools/designer/interfaces
DBFILE       = plugin.db
qt/tools/designer/examples/filechooser/plugin/plugin.pro
Change the HEADERS line to list your plugin's header file plus a header file for each of your widgets. Make the equivalent change for the SOURCES line. If you create a Makefile with qmake and make the project the plugin will be created and placed in a directory where Qt Designer can find it. The next time you run Qt Designer it will detect your new plugin and load it automatically, displaying its icon in the toolbar you specified.

Using the Widget Plugin
    Once the plugin has been compiled it will automatically be found and loaded by Qt Designer the next time Qt Designer is run. Use your custom widget just like any other.
If you want to use the plugin in another of your projects you can link against it by adding an appropriate line to the project, e.g. by adding a line like this to the project's .pro file:

LIBS += filechooser.lib
When you want to distribute your application, include the compiled plugin with the executable. Install the plugin in plugins/widgets subdirectory of the Qt installation directory. If you don't want to use the standard plugin path, have your installation process determine the path you want to use for the plugin, and save the path, e.g. using QSettings, for the application to read when it runs. The application can then call     QApplication::addLibraryPath() with this path and your plugins will be available to the application. Note that the final part of the path, i.e. styles, widgets, etc. cannot be changed.

Plugins and Threaded Applications
   If you want to build a plugin which you want to use with a threaded Qt library (whether or not the plugin itself uses threads) you must use a threaded environment. Specifically, you must use a threaded Qt library, and you must build Qt Designer with that library. Your .pro file for your plugin must include the line:

    CONFIG += thread
Do not mix the normal Qt library and the threaded Qt library in an application. If your application uses the threaded Qt library, you should not link with the normal Qt library. Nor should you dynamically load the normal Qt library or dynamically load another library, e.g. a plugin, that depends on the normal Qt library. On some systems, mixing threaded and non-threaded libraries or plugins will corrupt the static data used in the Qt library.

发表于: 2007-07-10 ,修改于: 2007-07-10 16:33,已浏览239次,有评论0条 推荐 投诉


网友评论

发表评论