qwebchannel-bridge
qwebchannel-bridge copied to clipboard
🌉How to intergrate @qt qwebchannel with @vuejs
QWebChannel bridge
This is an integration approach for QWebChannel with Vue.js v2, consisted of a message broker layer.
Prerequisites
Qt side should provide one or more QObject which include all information shared with JS side.
-
A
Cppfunction namedemitEmbeddedPageLoadclass WebBridge: public QObject { Q_OBJECT public slots: void emitEmbeddedPageLoad() { QMessageBox::information(NULL,"emitEmbeddedPageLoad", "I'm called by client JS!"); } }; WebBridge *webBridge = new WebBridge(); QWebChannel *channel = new QWebChannel(this); channel->registerObject('keyNamedContext', webBridge); view->page()->setWebChannel(channel); -
In
JSside, you should provide a init function whenQWebChannelinitialized.new QWebChannel(window.qt.webChannelTransport, function(channel) { const published = channel.objects.keyNamedContext Vue.prototype.$_bridge = published // This function calling will notify Qt server asynchronously published.emitEmbeddedPageLoad('', function(payload: string) { // This payload has included dispatcher name and its parameters. dispatch(payload) console.info(` Bridge load ! `) }) })Advance: You can also create a process like these implementation for function calling or properties reading with abstract
namespace. -
dispatchfunction should include all navigation logic.
Once QWebChannel initialized, dispatch will be invoked when Cpp function named emitEmbeddedPageLoad return a value async notification. dispatch function would play a navigator role in JS side.
How to navigate
-
In
Qtside, all entry point should be based on root path -https://<YOUR_HOST>/. All navigation will be distributed byJSside (vue-router, a kind of front-end router) rather thanQt.Qtside would has more opportunities to focus on other business logic.-
When
Qtside receives a initial message fromJSside, it should return a value which syntax should be like:interface InitialProps { type: string payload: any }// Actual value { type: [JS_SIDE_DISPATCHER_NAME], payload: [OPTIONAL_PAYLOAD] }
typeproperty will be used to invokedispatcherin the dispatchersMap, thenpayloadproperty including any messages fromQtside will passeddispatcher.dispatcherin the dispatchersMap plays anavigatorrole in front-end, and developer should add navigation logic into here. This is all secrets about front-end navigation withoutQtrouting.Above all process has described how to initialize
Vue.jsapp in theQWebEngine, and how navigation works in theVue.jswithQWebEngine. -
-
Be careful any external link and redirect uri from any external web site like
Alipayonline payment links. If you want to respect any redirect uri and prevent navigation from abovedispatchfunction, you MUST provide non-root redirect uri (eg.https://<YOUR_HOST>/#/NOT_EMPTY_PATH). You can find more details from dispatch function here.
How to push message from JS side
If you want to push messages from JS side to Qt side, you can invoke the mapping of Qt methods in JS side directly:
channel.object[QObjectJSMappingKey].methodNameMappingFromQtSide(
payload,
callback
)
Enhance: the following logic is based on these implementation:
// A QObject named `QObjectJSMappingKey` (as an abstract namespace) in Qt/JS side
// in the vue instance
this.$$pusher.QObjectJSMappingKey({
action: 'QT_QOBJECT_KEY',
payload: 'CALLING_PAYLOAD'
})
How to push messages from Qt side
Qt signal listener mechanism is a kind of good solution for communicate from Qt side to JS side. You may be wondering why we don't use signal mechanism directly to handle first frontend navigation? Because Qt side never known when frontend router is available until JS side push loaded message to Qt side positively.
class WebBridge: public QObject {
Q_OBJECT
public slots:
void emitEmbeddedPageLoad();
// define your own signal
signals:
void signalMessageFromQt(const QString &str);
};
Always define all available signal listeners in config/bridge.ts:
interface SignalCallbacks {
[targetSignal: string]: Function
}
All signal would be handled automatically by these codes.