The COM App example shows how to use ActiveQt to develop a Qt application that can be automated via COM. Different QObject based classes are exposed as COM objects that communicate with the GUI of the running Qt application. The APIs of those COM objects has been designed to resemble the APIs of standard COM applications; i.e. those from Microsoft Office.
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; };
第 1 个类
Application
表示应用程序对象。它暴露只读特性
documents
and
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: QList<Document *> m_list; };
DocumentList
类存储文档的列表。它提供读取文档数、按索引访问各文档及创建新文档的 API。
application
特性返回根对象。
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
类表示最终应用程序文档。各文档由应用程序选项卡 Widget 中的页面表示,且拥有的标题透过文档 API 可读写。
application
特性再次返回根对象。
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
类为选项卡 Widget 创建新页面,并将该页面标题用于 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 后于访问各个对象纵观
Application
API.
int main(int argc, char *argv[]) { 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 注册应用程序对象。现在,透过特定客户端 API 可访问 COM 客户端。
应用程序的退出是明确控制的 - 若 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) 下一 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
和 make 工具在
examples\activeqt\comapp
.