從零開始的electron開發-窗口通訊與拓展屏

窗口通訊與拓展屏

本期內容主要介紹拓展屏的開啓以及兩個窗口之間的消息通訊。html

拓展屏

在窗口啓動這一節裏咱們封裝了一個createWindow,用這個來建立一個窗口,拓展屏也是窗口,只不過咱們根據有幾個顯示屏把其放在對應的外接屏上罷了。vue

渲染進程

咱們在渲染進程添加一個按鈕,讓其點擊以後,向主進程發送對拓展屏的操做(打開或隱藏)node

<a-button type="primary" @click="openScreen">打開拓展屏</a-button>

const state = reactive({
  open: true,
  message: ''
})
async function openScreen() {
  await window.ipcRenderer.invoke('win-subScreen', {
    open: state.open,
    path: '#/subScreen'
  })
  state.open = !state.open
}

主進程

主進程接受到以後,經過screen.getAllDisplays()獲取如今窗口的數組(有幾個顯示屏),而後根據externalDisplay來獲取咱們外接屏幕的信息。
一樣的,咱們建立新的窗口也是用createWindow,以前咱們渲染進程傳遞了一個path,這個path呢就是咱們拓展屏要展現的頁面。
這裏須要注意的是createWindow返回的值須要用一個全局變量保存起來,以前托盤的時候說過,若是是局部變量的話,在函數執行完畢以後會被銷燬,那麼窗口也就被銷燬了。
咱們這裏用global賦值是後面窗口通訊會用到,你這裏用全局變量賦值時同樣的。react

global.js

global.tray = null
global.willQuitApp = false
global.envConfig = {}
global.sharedObject = {
  win: '',
  subScreen: ''
}

export default global


import { ipcMain, app, screen } from 'electron'
import global from '../config/global'
import createWindow from './createWindow'
import path from 'path'

const win = global.sharedObject.win
ipcMain.handle('win-subScreen', (_, data) => {
  if (data.open) {
    const displays = screen.getAllDisplays()
    const mainBounds = win.getNormalBounds()
    const externalDisplay = displays.find((display) => {
      return display.bounds.x !== 0 || display.bounds.y !== 0
    })
    if (externalDisplay) {
      if (global.sharedObject.subScreen) {
        global.sharedObject.subScreen.show()
      } else {
        global.sharedObject.subScreen = createWindow({
          frame: false,
          show: false,
          parent: win, // win是主窗口
          fullscreen: true,
          webPreferences: {
            webSecurity: false,
            contextIsolation: false,
            enableRemoteModule: true,
            nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
            plugins: true,
            preload: path.join(__dirname, 'preload.js'),
            devTools: false
          },
          x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x,
          y: externalDisplay.bounds.y
        }, data.path, `index.html${data.path}`)
        global.sharedObject.subScreen.once('ready-to-show', () => {
          global.sharedObject.subScreen.show()
        })
        global.sharedObject.subScreen.on('closed', () => {
          global.sharedObject.subScreen = null
        })
      }
    } else {
      console.log('未檢測到拓展屏')
    }
  } else {
    global.sharedObject.subScreen && global.sharedObject.subScreen.destroy()
  }
})

這裏呢有個小處理,咱們先判斷一下咱們的軟件是位於那個屏幕,好比咱們有兩個屏,1和2,那麼咱們的主窗口和拓展屏應該處於不一樣位置,好比:咱們的主窗口在1的話,那麼咱們的拓展屏應該在2打開,若是位置相同的話,因爲咱們拓展屏設置的是全屏,此時會把主窗口給遮擋住,若是沒有設置快捷鍵關閉的話,那麼將沒法關閉拓展屏,故有如下處理。git

const mainBounds = win.getNormalBounds()
mainBounds爲主窗口信息,若是主窗口有一半以上都處於副屏幕的話,那麼咱們認爲主窗口在副屏,那麼拓展屏打開位置應該在主屏,不然的話應該在副屏。
x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x

窗口通訊

electorn的窗口間的通訊呢,通常來講有兩種方式。github

  1. 窗口A渲染進程發送信息到主進程,主進程收到後發送信息給窗口B的渲染進程,也就是用主進程作箇中轉。
  2. 窗口A渲染進程中經過窗口B的WebContents.id直接發送信息給窗口B。

通訊方式

這裏介紹幾種常見的通訊方式web

ipcRenderer.invoke:渲染進程發送消息給主進程,這是一個promise,能夠在其resolve中獲取ipcMain.handle的返回值
ipcMain.handle:接收invoke發送的信息,能夠return值給ipcRenderer.invoke

ipcRenderer.send:渲染進程發送消息給主進程
ipcMain.on:接收send的信息

ipcRenderer.on:接收主進程的消息
webContents.send:主進程發送消息給渲染進程

ipcRenderer.sendTo:能夠經過webContentsId直接發送信息到對應的渲染進程窗口

咱們這裏把1,2都實現一下vue-cli

渲染進程

渲染進程中能夠經過remote來獲取主進程中global的值,remote.getGlobal('sharedObject').subScreen就是咱們以前拓展屏的窗口。
transferSub是咱們的方案1,directSub是方案2。數組

div class="subMessage">
  <a-textarea
    v-model:value="state.message"
    :auto-size="{ minRows: 2, maxRows: 5 }"
  />
  <a-button type="primary" :disabled="state.message.length === 0" @click="transferSub">中轉發送</a-button>
  <a-button type="primary" :disabled="state.message.length === 0" @click="directSub">直接發送</a-button>
</div>

import { defineComponent, reactive, onMounted, onUnmounted, getCurrentInstance } from 'vue'
const { remote } = require('electron')

const state = reactive({
  open: true,
  message: ''
})
const { proxy } = getCurrentInstance()
const { $message } = proxy
function transferSub() {
  window.ipcRenderer.invoke('win-subScreen-message', state.message)
}
function directSub() {
  const subScreen = remote.getGlobal('sharedObject').subScreen
  if (subScreen) {
    window.ipcRenderer.sendTo(subScreen.webContents.id, 'main-subScree', state.message)
  }
}
onMounted(() => {
  window.ipcRenderer.on('subScree-main', (_event, data) => {
    $message.success(data)
  })
})
onUnmounted(() => {
  window.ipcRenderer.removeListener('subScree-main')
})

主進程

中轉一下,用webContents.send把信息發送給拓展屏窗口promise

ipcMain.handle('win-subScreen-message', (_, data) => {
  if (global.sharedObject.subScreen) {
    global.sharedObject.subScreen.webContents.send('renderer-subScreen-message', data)
  }
})

拓展屏信息接收及發送

咱們這裏直接監聽中轉的消息以及直接發送的消息,再直接通知主窗口消息。

<template>
  <div class="subScreen">{{ state.message }}</div>
</template>

<script>
import { defineComponent, reactive, onMounted, onUnmounted } from 'vue'
const { remote } = require('electron')

export default defineComponent({
  setup() {
    const state = reactive({
      message: ''
    })
    onMounted(() => {
      const win = remote.getGlobal('sharedObject').win
      window.ipcRenderer.on('renderer-subScreen-message', (_event, data) => {
        state.message = data
        window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了中轉發送信息')
      })
      window.ipcRenderer.on('main-subScree', (_event, data) => {
        state.message = data
        window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了直接發送信息')
      })
    })
    onUnmounted(() => {
      window.ipcRenderer.removeListener('renderer-subScreen-message')
      window.ipcRenderer.removeListener('main-subScree')
    })
    return {
      state
    }
  }
})
</script>

驗證

咱們打開拓展屏,在主窗口輸入信息,點擊直接發送或者中轉發送,看看拓展窗口的顯示是否爲咱們輸入的信息,主窗口展現拓展屏的通知信息。

本系列更新只有利用週末和下班時間整理,比較多的內容的話更新會比較慢,但願能對你有所幫助,請多多star或點贊收藏支持一下

本文地址:連接
本文github地址:連接

相關文章
相關標籤/搜索