天天看點

Qt開發示例:COM應用程式示例(ActiveQt)

點選下載下傳Qt6最新試用版

COM應用程式的例子展示了如何使用ActiveQt來開發一個可以通過COM自動化的Qt應用程式。不同的基于QObject的類被暴露為COM對象,與運作中的Qt應用程式的GUI進行通信。這些COM對象的API被設計成類似于标準COM應用程式的API,即那些來自Microsoft Office的API。

class Application : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("ClassID", "{b50a71db-c4a7-4551-8d14-49983566afee}")
    Q_CLASSINFO("InterfaceID", "{4a427759-16ef-4ed8-be79-59ffe5789042}")
    Q_CLASSINFO("RegisterObject", "yes")

    Q_PROPERTY(DocumentList* documents READ documents)
    Q_PROPERTY(QString id READ id)
    Q_PROPERTY(bool visible READ isVisible WRITE setVisible)

public:
    explicit Application(QObject *parent = nullptr);
    DocumentList *documents() const;

    QString id() const { return objectName(); }

    void setVisible(bool on);
    bool isVisible() const;

    QTabWidget *window() const { return m_ui.data(); }

public slots:
    void quit();

private:
    QScopedPointer <DocumentList> m_docs;
    QScopedPointer <QTabWidget> m_ui;
};
           

第一個類Application代表應用對象。它公開了隻讀屬性documents和id來擷取對文檔清單的通路,以及一個辨別符。一個讀/寫屬性visible控制應用程式基于QTabWidget的使用者界面是否應該可見,一個槽quit()終止應用程式。

RegisterObject屬性被設定為確定該類的執行個體在COM的運作對象表(ROT)中被注冊--這允許COM用戶端連接配接到一個已經執行個體化的COM對象。

class DocumentList : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("ClassID", "{496b761d-924b-4554-a18a-8f3704d2a9a6}")
    Q_CLASSINFO("InterfaceID", "{6c9e30e8-3ff6-4e6a-9edc-d219d074a148}")

    Q_PROPERTY(Application* application READ application)
    Q_PROPERTY(int count READ count)

public:
    explicit DocumentList(Application *application);

    int count() const;
    Application *application() const;

public slots:
    Document *addDocument();
    Document *item(int index) const;

private:
    QVector<Document *> m_list;
};
           

DocumentList類存儲了一個文檔的清單,它提供了一個API來讀取文檔的數量,通過索引通路每個文檔,并建立新的文檔。它提供了一個API來讀取文檔的數量,通過索引通路每個文檔并建立一個新的文檔。應用屬性傳回根對象。

class Document : public QObject
{
    Q_OBJECT

    Q_CLASSINFO("ClassID", "{2b5775cd-72c2-43da-bc3b-b0e8d1e1c4f7}")
    Q_CLASSINFO("InterfaceID", "{2ce1761e-07a3-415c-bd11-0eab2c7283de}")

    Q_PROPERTY(Application *application READ application)
    Q_PROPERTY(QString title READ title WRITE setTitle)

public:
    explicit Document(DocumentList *list);
    virtual ~Document();

    Application *application() const;

    QString title() const;
    void setTitle(const QString &title);

private:
    QScopedPointer <QWidget> m_page;
};
           

Document類最後代表應用程式中的一個文檔。每個文檔由應用程式的标簽部件中的一個頁面來表示,并且有一個标題,這個标題是可以通過文檔的API來讀寫的。應用程式屬性再次傳回根對象。

Document::Document(DocumentList *list)
: QObject(list)
{
    QTabWidget *tabs = list->application()->window();
    m_page.reset(new QWidget(tabs));
    m_page->setWindowTitle(tr("Unnamed"));
    tabs->addTab(m_page.data(), m_page->windowTitle());

    m_page->show();
}

Document::~Document() = default;

Application *Document::application() const
{
    return qobject_cast<DocumentList *>(parent())->application();
}

QString Document::title() const
{
    return m_page->windowTitle();
}

void Document::setTitle(const QString &t)
{
    m_page->setWindowTitle(t);

    QTabWidget *tabs = application()->window();
    int index = tabs->indexOf(m_page.data());
    tabs->setTabText(index, m_page->windowTitle());
}
           

Document該類的實作為頁籤小部件建立一個新頁面,并将該頁面的标題用作title屬性。删除文檔後,頁面即被删除。

DocumentList::DocumentList(Application *application)
: QObject(application)
{
}

Application *DocumentList::application() const
{
    return qobject_cast<Application *>(parent());
}

int DocumentList::count() const
{
    return m_list.count();
}

Document *DocumentList::item(int index) const
{
    return m_list.value(index, nullptr);
}

Document *DocumentList::addDocument()
{
    Document *document = new Document(this);
    m_list.append(document);

    return document;
}
           

該DocumentList實作是直接的。

Application::Application(QObject *parent)
: QObject(parent),
  m_ui(new QTabWidget),
  m_docs(new DocumentList(this))
{
    setObjectName(QStringLiteral("From QAxFactory"));
}

DocumentList *Application::documents() const
{
    return m_docs.data();
}

void Application::setVisible(bool on)
{
    m_ui->setVisible(on);
}

bool Application::isVisible() const
{
    return m_ui->isVisible();
}

void Application::quit()
{
    m_docs.reset();
    m_ui.reset();
    QTimer::singleShot(0 /*ms*/, qApp, &QCoreApplication::quit);
}

#include "main.moc"
           

在Application類初始化構造函數中的使用者界面,以及顯示和隐藏它的實作setVisible()。對象名稱(可通過id屬性通路)設定為“ "From QAxFactory ”,以訓示此COM對象已由COM建立。請注意,沒有析構函數可删除QTabWidget-而是quit()在調用quit( )通過單次計時器進行,這是確定對插槽的COM調用完成所必需的。

QAXFACTORY_BEGIN("{edd3e836-f537-4c6f-be7d-6014c155cc7a}", "{b7da3de8-83bb-4bbe-9ab7-99a05819e201}")
   QAXCLASS(Application)
   QAXTYPE(Document)
   QAXTYPE(DocumentList)
QAXFACTORY_END()
           

使用QAxFactory宏從伺服器導出類。隻能Application從外部執行個體化對象-其他API隻能在通路整個ApplicationAPI中的相應對象後才能使用。

int main(int argc, char *argv[])
{
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    app.setQuitOnLastWindowClosed(false);

    // started by COM - don't do anything
    if (QAxFactory::isServer())
        return app.exec();

    // started by user
    Application appobject;
    appobject.setObjectName(QStringLiteral("From Application"));

    QAxFactory::startServer();
    QAxFactory::registerActiveObject(&appobject);

    appobject.window()->setMinimumSize(300, 100);
    appobject.setVisible(true);

    QObject::connect(&app, &QGuiApplication::lastWindowClosed, &appobject, &Application::quit);

    return app.exec();
}
           

main()切入點函數建立一個QApplication,如果應用程式是由COM啟動的,則進入事件循環即可。如果應用程式已被使用者啟動,則建立Application對象,并将對象名設定為 "From Application"。然後啟動COM伺服器,并将應用對象注冊到COM中。現在,COM用戶端可以通過用戶端特定的API來通路它。

應用程式的退出是顯式控制的--如果COM啟動了應用程式,那麼用戶端代碼必須調用quit();如果使用者啟動了應用程式,那麼當最後一個視窗被關閉時,應用程式就會終止。

最後,使使用者界面可見,并啟動事件循環。

現在,一個簡單的Visual Basic應用程式可以通路這個Qt應用程式。在VB中,啟動一個新的 "Standard Exe "項目,并向comappLib類型庫添加一個項目引用。建立一個具有清單框 "DocumentList"、靜态标簽 "DocumentsCount "和指令按鈕 "NewDocument "的窗體。最後,像這樣實作表單的代碼。

Private Application As comappLib.Application
Private MyApp As Boolean

Private Sub UpdateList()
    DocumentList.Clear
    DocumentsCount.Caption = Application.documents.Count
    For Index = 0 To Application.documents.Count - 1
       DocumentList.AddItem (Application.documents.Item(Index).Title)
    Next
End Sub

Private Sub Form_Load()
    On Error GoTo CreateNew
    Set Application = GetObject(, "comapp.Application")
    MyApp = False
    GoTo Initialized
CreateNew:
    On Error GoTo InitializeFailed
    Set Application = New Application
    Application.Visible = True
    MyApp = True
Initialized:
    Caption = Application.id
    UpdateList
InitializeFailed:
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If MyApp Then
        Application.quit
    End If
End Sub

Private Sub NewDocument_Click()
    Application.documents.addDocument
    UpdateList
End Sub
           

要建構示例,您必須首先建構QAxServer庫。然後運作qmake,将您的制作工具放入examples\activeqt\comapp。

Qt相關元件:

  • QtitanRibbon| 下載下傳試用: 遵循Microsoft Ribbon UI Paradigm for Qt技術的Ribbon UI元件,緻力于為Windows、Linux和Mac OS X提供功能完整的Ribbon元件。
  • QtitanChart | 下載下傳試用 :是一個C ++庫,代表一組控件,這些控件使您可以快速地為應用程式提供漂亮而豐富的圖表。并且支援所有主要的桌面作業系統。
  • QtitanDataGrid | 下載下傳試用 :這個Qt資料網格元件使用純C++建立,運作速度極快,處理大資料和超大資料集的效果突出。QtitanDataGrid完全內建了QtDesigner,因而極易适應其他相似的開發環境,保證100%相容Qt GUI。

繼續閱讀