Introduction of QT

Qt is a cross-platform C++ graphical user interface application development framework developed by the Qt Company in 1991. It can be used to develop both GUI programs and non-GUI programs, such as console tools and servers. Qt is an object-oriented framework that uses special code-generating extensions (called the Meta Object Compiler (MOC)) and some macros. Qt is easy to extend and allows for true component programming.

Web communications

Use Qt Qt WebEngine and WebChannel module to communicate with the Web. The front-end needs to introduce the official qWebchannel. js library to interact with the Qt client (asynchronous interaction) official link

Qt<– > Web interaction principle

Instantiation QWebChannel generated after an interactive channel channel, and then obtain channel object let qtContext = channel. Objects. WebBridge; QtContext: qtContext: qtContext:

qtContext.contentChanged.connect((response) => {
  const { event, data } = response
  responseLog({ event, data })
  EE.emit(event, data)
})
Copy the code

Send a message to Client (format: event name, data, callback event name) :

qtContext.dataChanged(
  JSON.stringify({ event, data, callbackEvent })
)
Copy the code

Encapsulation Promise

  • Check whether the environment is Qt
export const isQtClient = (function () {
  return navigator.userAgent.includes('QtWebEngine')
})()
Copy the code
  • Data asynchronous callback

To implement Ajax-like interaction logic, introduce Eventemitter3 as a bridge:

import EventEmitter from 'eventemitter3'
export const EE = new EventEmitter()
Copy the code

FetchQt receives the event name, data name, and callbackEvent name callbackEvent as parameters:

fetchQt: ({ event, data, callbackEvent }) => {
    requestLog(event, data)
    return qtContext.dataChanged(
      JSON.stringify({ event, data, callbackEvent })
    )
  }
Copy the code

FetchQtCallback is a method that automatically registers callback functions and behaves like Ajax:

fetchQtCallback: ({ event, data,callbackEvent = `${event}_callback` },config = {}) => { const options = { ... conf, ... config } return new Promise((resolve, reject) => { let timer = null const apiHandler = (data) => { clearTimeout(timer) timer = null resolve(data) } EE.once(callbackEvent, apiHandler) if (options.timeout) { timer = setTimeout(() => { EE.off(callbackEvent, apiHandler) reject({ callbackEvent, msg: 'Timeout', time: options.timeout }) }, options.timeout) } qWebChannel.__apis__.fetchQt({ event, data, callbackEvent }) }) }Copy the code
  • use
// Export const qwebApi = async ({event, data, callbackEvent }) => { const channel = await qwebchannel() return channel.fetchQt({ event, data, callbackEvent }, Export const fetchQwebApi = async ({event, data, callbackEvent}, config = {}) => { const channel = await qwebchannel() return channel.fetchQtCallback({ event, data, callbackEvent }, config) }Copy the code

Pay attention to the point

The data objects defined above must have the same parameter meanings as those on the Qt side. For example, callbackEvent will send an event named callbackEvent and return data after receiving this parameter on the Qt side

All the code

utils.js

export const isQtClient = (function () { return navigator.userAgent.includes('QtWebEngine') })() export function assert(condition, msg) { if (! condition) console.error(msg || `[ASSERT]: ${condition} is a falsy value.`) } export const __DEV__ = process.env.NODE_ENV === 'development'Copy the code

qwebchannel.js

import EventEmitter from 'eventemitter3' import { QWebChannel } from './qwebchannel' import { assert, isQtClient, __DEV__ } from './utils' let qWebChannel = null const conf = { timeout: Export const PageType = {Current: 0x0000, Home: 0x0001, CloudGames: 0x0002, AndroidGames: Const requestLog = (event, data, ga) => { try { if (ga && Object.keys(ga).length) { console.info( `%cRequest:qwebApi for <${event}>(Ga-Data):`, 'color:green; font-size:18px; ', '\n', ga ) } if (data) { console.info( `%cRequest:qwebApi for <${event}>(Req-Data):`, 'color:pink; font-size:18px; ', '\n', data ) } else { console.info(`%cRequest:qwebApi for <${event}>`, 'color:pink; font-size:18px; ')}} catch (error) {}} // responseLog = (response, isMock) => {try {const {event, data } = response console.info( `%cResponse:qwebApi for <${event}>${isMock ? ' [[MOCK-DATA]]' : ''}:`, 'color:green; font-size:18px; ', '\ n', data)} the catch (error) {console. Error (' % cqtContext. The contentChanged (data parsing error) ', 'color: red; font-size:18px; ', '\n', error ) } } export const EE = new EventEmitter() // console.log(EE) export default function QWC() { return new Promise((resolve, reject) => { if (! __DEV__) { assert( window && window.qt && window.qt.webChannelTransport, "'qt' or 'qt.webchanneltransport' should be initialized(injected) by QtWebEngine")} isQtClient) { window.qt = { webChannelTransport: { send() { console.log(`QWebChannel simulator activated ! `) setTimeout(() => { qWebChannel.__apis__ = { fetchQt: ({ event, data }) => { requestLog(event, data) return Promise.resolve() }, fetchQtCallback: ( { event, data, callbackEvent = `${event}_callback` }, Qwebchannel.__apis__. FetchQt ({event, mock) data }).then(() => { responseLog({ event: callbackEvent, data: initial }, true) EE.emit(callbackEvent, initial) return initial }) }, qtContext: null, on: EE.on.bind(EE), off: EE.off.bind(EE), once: EE.off.bind(EE), emit: EE.emit.bind(EE), } resolve(qWebChannel.__apis__) }, 0) }, }, } } if (! qWebChannel) { qWebChannel = new QWebChannel(window.qt.webChannelTransport, (channel) => { const qtContext = channel.objects.webBridge qtContext.contentChanged.connect((response) => { const { event, data } = response responseLog({ event, data }) EE.emit(event, data) }) qWebChannel.__apis__ = { fetchQt: ({ event, data, page = PageType.Current, callbackEvent, ga = {} }) => { const { path, module, ... extra } = ga const gaTemp = { path: path || window.location.pathname, module, extra: JSON.stringify(extra), } requestLog(event, data, gaTemp) return qtContext.dataChanged( JSON.stringify({ event, data, page, callbackEvent, ga: gaTemp }) ) }, fetchQtCallback: ( { event, data, page = PageType.Current, callbackEvent = `${event}_callback`, ga }, config = {} ) => { const options = { ... conf, ... config } return new Promise((resolve, reject) => { let timer = null const apiHandler = (data) => { clearTimeout(timer) timer = null resolve(data) } EE.once(callbackEvent, apiHandler) if (options.timeout) { timer = setTimeout(() => { EE.off(callbackEvent, apiHandler) reject({ callbackEvent, msg: 'Timeout', time: options.timeout }) }, options.timeout) } qWebChannel.__apis__.fetchQt({ event, data, page, callbackEvent, ga }) }) }, qtContext, on: EE.on.bind(EE), off: EE.off.bind(EE), once: EE.off.bind(EE), emit: EE.emit.bind(EE), } resolve(qWebChannel.__apis__) }) } else { resolve(qWebChannel.__apis__) } }) }Copy the code

qwebApi.js

import qwebchannel, { EE } from '@/tool/qwebchannel' export const on = EE.on.bind(EE) export const off = EE.off.bind(EE) export const once = Ee.once. Bind (EE) export const emit = ee.emit. Bind (EE) export const qwebApi = async ({event, data, page, callbackEvent, ga }) => { const channel = await qwebchannel() return channel.fetchQt({ event, data, page, Export const fetchQwebApi = async ({event, data, page, callbackEvent, ga}); config = {}) => { const channel = await qwebchannel() return channel.fetchQtCallback({ event, data, page, callbackEvent, ga }, config) }Copy the code
  • Vue and React wait for the channel to be established. For example, React:
import React from 'react'
import ReactDOM from 'react-dom'
import qwebchannel from '@/tool/qwebchannel'
qwebchannel().then(async (qwebApi) => {
    ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
})
Copy the code
  • Used in specific components
import { qwebApi, fetchQwebApi } from '@/apis/qwebApi'
qwebApi({
  event: 'openClientLoading',
  data: null,
})z

const vmList = await fetchQwebApi(
  {
    event: 'getVmList',
    data: { packageName: game.package_name},
  },
  { initial: { installed: [], uninstalled: [], defaultEngines: '' } }
)
Copy the code

conclusion

The above is the summary of the use of Qtwebchannel, so that the interaction between the client and web is unified