electronjs入门-编辑器应用程序

news2024/7/4 6:19:11

我们将在Electron中创建一个新项目,如我们在第1章中所示,名为“编辑器”,我们将在下一章中使用它来创建编辑器;在index.js中,这是我们的主要过程;请记住为Electron软件包放置必要的依赖项:

npm init -y
cnpm install --save-dev electron

package.json

"start":"DEBUG=true electron ."

新建index.js

const { app, BrowserWindow } = require("electron");
function createWindow() {
  let win = new BrowserWindow({
    width: 800,
    height: 600,
  });
  win.loadFile("index.html");
}
app.whenReady().then(createWindow);

新建index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initialscale=1.0">
    <title>My Editor</title>
</head>

<body>
    <textarea id="MyID"></textarea>
</body>

</html>

npm start启动项目之后我们将看到如下的情形
在这里插入图片描述
配置文本编辑器让我们配置以下文本编辑器:
https://www.npmjs.com/package/simplemde
我们在项目中安装它时使用:

npm i simplemde -registry=https://registry.npm.taobao.org
# 或者yarn安装
yarn add simplemde

修改index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initialscale=1.0">
    <title>My Editor</title>
    <link rel="stylesheet" href="./node_modules/simplemde/dist/simplemde.min.css">
    <script src="./node_modules/simplemde/dist/simplemde.min.js">
    </script>
</head>

<body>
    <textarea id="MyID"></textarea>
    <script>
        var simplemde = new SimpleMDE({
            element:
                document.getElementById("MyID")
        });
    </script>
</body>

</html>

启动项目

npm run start

在这里插入图片描述
在上面的代码中,我们加载编辑器插件的JS和CSS,并使用以下方法进行配置:

var simplemde = new SimpleMDE({
            element:
                document.getElementById("MyID")
        });

其中,我们将先前创建的TEXTAREA的选择器作为引用传递。为了使编辑器填充屏幕的宽度,我们将使用CSS:

html,
 body {
     display: flex;
     flex-direction: column;
     height: 100%;
 }

 .CodeMirror {
     flex: 1
 }

在这里插入图片描述

编辑器应用程序和进程之间的通信

双方事件的使用对于Electron中的任何应用程序都是至关重要的,也是Electron应用程序的核心,对于我们使用编辑器的notes应用程序,能够为以下功能执行这项工作是至关重要的:

  • 打开文件
  • 保存文件
  • 对文本执行一些操作,例如下划线、加粗等

这一点很重要,因为这取决于我们在哪里实施这些选项,我们可能必须沟通进程,例如,如果我们定义了一个带有粗体选项的菜单,该菜单是从渲染过程,因为它是由具有Electron对操作系统的相应调用;让我们请记住,我们与操作人员进行的所有沟通系统是通过渲染过程完成的,但我们想要的操作执行(将粗体应用于文本)将在编辑器中完成,其是安装在网页或呈现过程上的插件,因此,当单击菜单中的此选项时,我们必须发送从主进程到呈现进程的消息。

菜单结构

在本节中,我们将为应用程序创建选项菜单,该菜单由一组选项组成,既可以编辑内容,也可以执行文件操作,特别是打开和保存文件。

创建和打开文件

对于使用文件的部分,即保存或打开文件,我们将在项目中的新文件中执行此操作:editor-options.js

创建文件

为了保存文件,我们将创建一个新函数,它将接收窗口和我们要插入的数据作为参数,特别是与用户在编辑器中编写的HTML内容相对应的数据:

module.exports.save_file = function(win,data){}

要使用弹窗创建文件,请执行以下操作:
保存文件需要哪个模块,我们使用该模块:

const { dialog } = require('electron')

其模块包含打开文件保存对话框的功能:

const path = dialog.showSaveDialogSync(win,option)

其中:

  • win,对应于将打开对话框的窗口。
  • option,选项对应于我们要处理的文件的标题和类型,在本例中为txt文件
const option = {
  title: "Guardar archivo",
  filters: [
    {
      name: "archivo",
      extensions: ["txt"],
    },
  ],
};

“showSaveDialogSync()”函数返回用户选择的文件;如果对话框被取消,则返回undefined。有了文件路径,我们可以用它来写,在Node中处理文件,我们有了模块:

const fs = require('fs')

内容如下:

fs.writeFileSync(path,data)

最后,函数变为:

const { app, dialog } = require("electron");
const fs = require("fs");
const path = require("path");
module.exports.save_file = function (win, data) {
  const option = {
    title: "Guardar archivo",
    filters: [
      {
        name: "archivo",
        extensions: ["txt"],
      },
    ],
  };
  const path = dialog.showSaveDialogSync(win, option);
  // console.log(path)
  fs.writeFileSync(path, data);
  /* dialog.showSaveDialog(win,option).then(result => {
 console.log(result.canceled)
 console.log(result.filePaths)
 })*/
};

读取文件

要读取上述函数生成的文件,我们将使用一个新函数:
editor-options.js

module.exports.open_file = function(win){}

要使用alter窗口打开文件:
打开我们使用与上一个功能相同的模块的项目文件是必要的:

const { dialog } = require('electron')

我们有:

paths = dialog.showOpenDialogSync(win,option)

返回用户选择的文件的路径; 如果对话框被取消,则返回undefined。
有了这个路径,我们就可以通过fs模块读取文件的内容,并将数据返回给渲染进程或者网页; 最后的实现如下:

module.exports.open_file = function (win) {
  const option = {
    title: "Abrir archivo",
    filters: [
      {
        name: "archivo",
        extensions: ["txt"],
      },
    ],
  };
  paths = dialog.showOpenDialogSync(win, option);
  if (paths && paths.length > 0) {
    const content = fs.readFileSync(paths[0]).toString();
    win.webContents.send("file-open", content);
    var filename = path.basename(paths[0]);
    win.webContents.send("title-change", app.name + " " + filename);
  }
};

菜单选项

在本节中,我们将为编辑器应用程序创建一个菜单,如第二章所述,我们将使用自定义选项,例如预定义的角色和分隔符,以及快捷键或键盘快捷键; 为了保持应用程序的组织性和模块化,我们将在单独的文件中定义菜单。

由于菜单是在渲染进程中定义的,并且将具有能够操作在网页中定义的 simplemde 插件中定义的文本的选项,因此我们必须在进程之间发送消息才能实现此操作; 例如,如果从菜单中,我们有一个将文本设置为粗体的选项,那么我们必须从菜单(主进程)向网页发送一条消息,并从这里实现将所选文本设置为粗体的功能 ; 考虑到这一点,我们有以下结构:
menu.js

const {
  app,
  ipcMain,
  Menu,
  shell,
  BrowserWindow,
  globalShortcut,
} = require("electron");

const { open_file, save_file } = require("./editor-options");

const template = [
  {
    label: "About us",

    submenu: [
      {
        label: "About us",

        click() {
          shell.openExternal("https://www.electronjs.org");
        },
      },
    ],
  },

  {
    label: "File",

    submenu: [
      {
        label: "Save",

        accelerator: "CommandOrControl+S",

        click() {
          const win = BrowserWindow.getFocusedWindow();

          win.webContents.send("editorchannel", "file-save");
        },
      },

      {
        label: "Open",

        accelerator: "CommandOrControl+O",

        click() {
          const win = BrowserWindow.getFocusedWindow();

          open_file(win);
        },
      },
    ],
  },

  {
    label: "Style and format",

    submenu: [
      {
        label: "Bold",

        click() {
          const win = BrowserWindow.getFocusedWindow();
          win.webContents.send("editorchannel", "style-bold");

          console.log("Negritas");
        },
      },

      {
        label: "Italic",

        click() {
          const win = BrowserWindow.getFocusedWindow();

          win.webContents.send("editor-channel", "style-italic");
        },
      },

      {
        type: "separator",
      },

      {
        label: "H1",

        click() {
          const win = BrowserWindow.getFocusedWindow();

          win.webContents.send("editor-channel", "style-h1");
        },
      },

      {
        label: "H2",

        click() {
          const win = BrowserWindow.getFocusedWindow();
          win.webContents.send("editor-channel", "style-h2");
        },
      },
    ],
  },
];

const menu = Menu.buildFromTemplate(template);

module.exports = menu;

在上面的脚本中,我们还定义了其他选项,例如打开外部链接:

shell.openExternal

通过定义到菜单项的加速器选项,我们可以指示激活这些选项的快捷键。

如果你想为每个操作系统设置自定义选项,你所要做的就是询问所使用的平台; 例如,如果我们想在使用 Windows 或 MacOS 时显示特定选项:
·menu.js·

if (process.platform == "win32" || process.platform == "darwin") {
  template.push({
    label: "Default",
    submenu: [
      { role: "reload" },
      { role: "forceReload" },
      { role: "toggleDevTools" },
      { role: "resetZoom" },
      { role: "zoomIn" },
      { role: "zoomOut" },
      { type: "separator" },
      { role: "togglefullscreen" },
    ],
  });
}

在这里插入图片描述
我们还将为应用程序实现一系列键盘快捷键,专门用于打开文件或保存内容,为此,我们将分别使用 ctrl/command+S 和 ctrl/command+O 的组合:
menu.js

ipcMain.on("editor-channel", (event, arg) => {
  console.log("Mensaje recibido del canal 'editor-channel': " + arg);
});

ipcMain.on("file-open", (event, arg) => {
  const win = BrowserWindow.getFocusedWindow();

  open_file(win);
});
ipcMain.on("file-save", (event, arg) => {
  const win = BrowserWindow.getFocusedWindow();

  save_file(win, arg);
});

app.on("ready", () => {
  globalShortcut.register("CommandOrControl+S", () => {
    const win = BrowserWindow.getFocusedWindow();
    win.webContents.send("editor-channel", "file-save");
  });

  globalShortcut.register("CommandOrControl+O", () => {
    const win = BrowserWindow.getFocusedWindow();
    open_file(win);
  });
});

虽然,当在菜单中定义这些选项时:不需要通过 globalShortcut API 定义键盘快捷键,可以通过如下方式:

const template = [
    {
        label: 'About us',
        click() {
            shell.openExternal("https://www.electronjs.org")
        }
    },
    { label: 'File',
        submenu: [
            {
                label: "Save",
                accelerator: 'CommandOrControl+S',
                ***
            },
            {
                label: "Open",
                accelerator: 'CommandOrControl+O',
                ***
            },
        ]
    }
]

在 index.js 中,我们导入菜单,以便我们可以在应用程序中使用它:index.js

const menu = require('./menu')
//***
Menu.setApplicationMenu(menu)

最后,该菜单的所有处理主要基于发送消息,其中一些包括使用特定模块来执行其操作,例如打开或保存文件的对话框。
最后,在网页流程中,我们定义从menu.js调用的事件并执行相应的操作来编辑所选内容:
index.html

<script>
        const { ipcRenderer } = require('electron')
        ipcRenderer.on('message', (event, arg) => {
            alert("Mensaje recibido desde el proceso principal: " + arg);
        });
        ipcRenderer.send('editor-channel', 'Página cargada')
        ipcRenderer.on('file-open', (event, arg) => {
            console.log("Mensaje recibido desde el proceso principal: " + arg);
            simplemde.value(arg)
        });

        ipcRenderer.on('editorchannel', (event, arg) => {
            console.log("Mensaje recibido desde el proceso principal: " + arg);
            editorOptions(event, arg)
        });

        ipcRenderer.on('title-change', (event, arg) => {
            document.title = arg;
        });

        function editorOptions(event, arg) {
            console.log("editor")
            switch (arg) {
                case 'style-bold':
                    simplemde.toggleBold()
                    break;
                case 'style-italic':
                    simplemde.toggleItalic()
                    break;
                case 'style-h1':
                    simplemde.toggleHeading1()
                    break;
                case 'style-h2':
                    simplemde.toggleHeading2()
                    break;
                case 'file-save':
                    console.log(simplemde.value())
                    event.sender.send('file-save', simplemde.value())
                    break;
            }
        }
    </script>

index.js完整代码如下

const { app, BrowserWindow, Menu } = require("electron");
const menu = require("./menu");

function createWindow() {
  let win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    },
  });
  win.loadFile("index.html");
}
app.whenReady().then(createWindow);
Menu.setApplicationMenu(menu);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

来自同一事件:

event.sender

我们可以将消息发送回发送事件的窗口。
simplemde 插件实现了一系列功能,允许您以编程方式编辑所选内容; 例如,toggleBold() 函数允许您将文本转换为粗体。

作为一个网页,我们可以使用 JavaScript 以及 HTML 和 CSS 的任何脚本,例如,我们可以实现其他附加选项,例如在客户端使用纯 JavaScript 的暗模式:
index.html

<script>
    var simplemde = new SimpleMDE({
        element: document.getElementById("MyID")
    });
    function darkMode() {
        document.querySelector(".CodeMirror").style.background = "black"
    }
</script>

按钮代码如下
index.html

<button onclick="darkMode()">Dark Mode</button>

对于打开文件的功能,由于它是一个必须由主进程处理的进程,我们发送一个事件,该函数用于上面显示的editorOptions()函数:
index.html

function openFile() {
    ipcRenderer.send('file-open', 'file-open')
}

我们还可以使用HTML API拖放来实现打开文件的功能:
index.html

document.ondrop = function (event) {
    event.preventDefault();
    if (event.dataTransfer.items) {
        item = event.dataTransfer.items[0];
        if (item.kind == "file" && item.type == "text/plain") {
            var reader = new FileReader()
            var file = item.getAsFile();
            reader.onload = e => {
                //console.log(e.target.result);
                simplemde.value(e.target.result);
            }
            reader.readAsText(file);
        }
    }
}

这些选项是多种多样的,但它们意味着主进程和渲染进程之间的通信,甚至可能有必要根据情况再次与渲染进程通信;例如,要打开一个文件,在该文件中,我们传递文件的内容:
editor-options.js

win.webContents.send('file-open',content)

在网页上,我们可以选择打开文件,稍后我们将看到:
index.html

function openFile() {
    ipcRenderer.send('file-open', 'file-open')
}

另一个例子是关于保存文件的选项;按下菜单中的选项(主进程)会通过Electron中的事件向渲染进程发送一条消息(如我们在第2章中看到的):
在这里插入图片描述
从渲染过程中,我们获得编辑器中生成的HTML,将带有HTML内容的消息发送回渲染过程,以便它从操作系统请求对话框保存带有所述HTML内容的文件:
在这里插入图片描述
最后,当使用以下内容运行应用程序时:

npm run start

在这里插入图片描述
在这里插入图片描述
源代码如下https://github.com/libredesarrollo/electron-editor

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1209475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MediaFoundation】相关的概念

MF 概览 Media Foundation 提供了两种不同的编程模型&#xff0c;左边展示的是端到端的媒体数据模型&#xff0c;主要用在&#xff1a;播放URL或者文件&#xff0c;以及控制流。 在图表右侧展示的第二种模型中&#xff0c;应用程序可以从源头拉取数据&#xff0c;也可以将数据…

代号:408 —— 1000道精心打磨的计算机考研题

文章目录 &#x1f4cb;前言&#x1f3af;计算机科学与技术专业介绍&#xff08;14年发布&#xff09;&#x1f9e9;培养目标&#x1f9e9;毕业生应具备的知识和能力&#x1f9e9;主要课程 &#x1f3af;代号&#xff1a;408&#x1f525;文末送书&#x1f9e9;有什么优势&…

量化交易:使用 python 进行股票交易回测

执行环境: Google Colab 1. 下载数据 import yfinance as yfticker ZM df yf.download(ticker) df2. 数据预处理 df df.loc[2020-01-01:].copy()使用了 .loc 方法来选择索引为 ‘2020-01-01’ 以后的所有行数据。通过 .copy() 方法创建了一个这些数据的副本&#xff0c;确…

PCA9698的IIC转接GPIO控制N路灯

PCA9698验证灯的办法和PCA9535验证6路数字继电器&#xff0c;编译成ko直接Insmod&#xff0c;然后查看/dev/节点有了吗&#xff1f;然后用iictool命令往对应iic地址上面写数据&#xff0c;看看灯亮灭或者听继电器开关声响&#xff0c;至于写多少&#xff0c;研究芯片手册上面参…

Spark SQL编程

1. Spark SQL概述 1.1 什么是Spark SQL Spark SQL是用于结构化数据处理的Spark模块。与基本的Spark RDD API不同&#xff0c;Spark SQL提供的接口为Spark提供了有关数据结构和正在执行的计算的更多信息。在内部&#xff0c;Spark SQL使用这些额外的信息来执行额外的优化。与Spa…

Mac M2/M3 芯片环境配置以及常用软件安装-前端

最近换了台新 Mac&#xff0c;所有的配置和软件就重新安装下&#xff0c;顺便写个文章。 一、环境配置 1. 安装 Homebrew 安装 Homebrew【Mac 安装 Homebrew】 通过国内镜像安装会比较快 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Ho…

IDEA接口调试插件不好找?这款免费用!

IDEA插件市场中的API调试插件不是收费&#xff08;Fast Request &#xff09;就是不好用&#xff08;apidoc、apidocx等等&#xff09;今天给大家介绍一款国产的API调试插件&#xff1a;Apipost-Helper&#xff0c;完全免费且好看好用&#xff01; 这款插件由Apipost团队开发的…

【Qt之QWizard】使用2,示例分析

效果图 根据首页的选择不同&#xff0c;进入不同的选项。 以下是代码。 示例 .h #ifndef LICENSEWIZARD_H #define LICENSEWIZARD_H#include <QWizard>QT_BEGIN_NAMESPACE class QCheckBox; class QLabel; class QLineEdit; class QRadioButton; QT_END_NAMESPACEcla…

C/C++轻量级并发TCP服务器框架Zinx-框架开发001: 读取标准输入,回显到标准输出

文章目录 完整代码实现参考-非项目使用项目使用的代码 - 乱-但是思路与上面的相同创建Kernel类添加删除修改epoll&#xff0c;才能写run方法创建stdin_Channel类在Kernel类中实现run方法 完整代码实现参考-非项目使用 #include <errno.h> #include <signal.h> #in…

科学上网导致Adobe软件运行弹出This non-genuine Adobe app will be disabled soon,尝试解决办法

之前介绍用防火墙拦截Adobe软件的出站规则可以解决软件的非正版弹窗&#xff0c;但是有的用户却不行是为什么&#xff0c;原因是使用了代理网络。因为Adobe此时跑的不是本地的流量而是代理的流量。所以防火墙拦截就不起作用了。 首先是之前介绍过的拦截方法&#xff0c;如果你没…

网络安全-学习手册

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c;但是连方向都没搞清楚就开始学习…

Spring-Spring之AOP底层原理解析---实践(动态代理)

动态代理 代理模式的解释&#xff1a;为其他对象提供一种代理以控制对这个对象的访问&#xff0c;增强一个类中的某个方法&#xff0c;对程序进行扩展。 cglib动态代理 方式一&#xff1a; public class UserService {public void test() {System.out.println("test..…

k8s自定义Endpoint实现内部pod访问外部应用

自定义endpoint实现内部pod访问外部应用 endpoint除了可以暴露pod的IP和端口还可以代理到外部的ip和端口 使用场景 公司业务还还没有完成上云&#xff0c; 一部分云原生的&#xff0c;一部分是实体的 业务上云期间逐步实现上云&#xff0c;保证各个模块之间的解耦性 比如使…

IEEE--DSConv: Efficient Convolution Operator 论文翻译

论文地址:https://arxiv.org/pdf/1901.01928v1.pdf 目录 摘要 1 介绍 2 相关工作 3 DSConv层 4 量化过程 5 分布偏移 6 优化推断 7 训练 8 结果 8.1 ImageNet 8.2 内存和计算负载 8.3 转移性 9 结论 摘要 我们引入了一种卷积层的变体&#xff0c;称为DSConv&…

网络编程 —— TCP 和 UDP 编程详解

目录 网络编程主要函数介绍 1. socket 函数 2. bind 函数 3. listen 函数 4. accept 函数 5. connect 函数 6. send 函数 7. recv 函数 8. recvfrom 函数 9. sendto 函数 TCP 和 UDP 原理上的区别 TCP 编程 服务端代码&#xff1a; 客户端代码&#xff1a; UDP 编…

如何进行iOS技术博客的备案?

​ 如何进行iOS技术博客的备案&#xff1f; 标题&#xff1a;iOS技术博客备案流程及要求解析 摘要&#xff1a; 在本篇问答中&#xff0c;我们将为iOS技术博主介绍如何进行备案。如果你的iOS应用只包含简单的页面&#xff0c;并通过蓝牙进行数据采集和传输&#xff0c;那么你…

0基础学习VR全景平台篇第121篇:认识视频剪辑软件Premiere

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 大家好&#xff0c;这节课是带领大家认识认识我们的剪辑软件Premiere&#xff0c;一般简称是PR。 &#xff08;PR界面&#xff09; 我们首先打开PR&#xff0c;第一步就是要创建…

LeetCode【238】除自身意外的数组的乘积

题目&#xff1a; 思路&#xff1a; https://zhuanlan.zhihu.com/p/109306706?utm_id0 代码&#xff1a; int n nums.length;int[] l new int[nums.length];int[] r new int[nums.length];l[0] 1;r[n-1] 1;for (int i1;i<nums.length;i) {l[i] l[i-1] * nums[i-1]…

python数据处理作业4:使用numpy数组对象,随机创建4*4的矩阵,并提取其对角元素

每日小语 真理诚然是一个崇高的字眼&#xff0c;然而更是一桩崇高的业绩。如果人的心灵与情感依然健康&#xff0c;则其心潮必将为之激荡不已。——黑格尔 难点&#xff1a;如何创建&#xff1f;取对角元素的函数是什么&#xff1f; gpt代码学习 import numpy as np# 随机创…

基于springboot实现体育场馆运营平台项目【项目源码】计算机毕业设计

基于springboot实现体育场馆运营平台演示 系统开发平台 在该数码论坛系统中&#xff0c;Eclipse能给用户提供更多的方便&#xff0c;其特点一是方便学习&#xff0c;方便快捷&#xff1b;二是有非常大的信息储存量&#xff0c;主要功能是用在对数据库中查询和编程。其功能有比…