electronjs入门-编辑器应用程序

我们将在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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

30个Python操作小技巧

1、列表推导 列表的元素可以在一行中进行方便的循环。 numbers [1, 2, 3, 4, 5, 6, 7, 8] even_numbers [number for number in numbers if number % 2 0] print(even_numbers)输出&#xff1a; [1,3,5,7]同时&#xff0c;也可以用在字典上。 dictionary {first_num: 1,…

1x1卷积核

1 1 1\times 1 11卷积核对输入数据的通道做约简。 每个 1 1 1\times 1 11卷积核相当于在输入数据的通道上做了一个降维&#xff08;经过一个神经元个数为1的全连接层&#xff09;&#xff0c;从而相当于大幅度降低了特征图的数量&#xff0c;但不影响特征图的结构。 使用 1 …

VCR库代码示例

1. 首先&#xff0c;我们需要在代码中添加对VCR库的引用&#xff1a; ruby require vcr require rest-client 2. 然后&#xff0c;我们需要创建一个VCR录制器&#xff0c;以便我们可以记录实际的HTTP请求和响应&#xff1a; ruby VCR.use_cassette(download_video, record:…

高通发布骁龙X Elite Oryon CPU /GitHub出现一款开源项目,让用户“拥有”更大的GPU内存|魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 高通发布骁龙X Elite Oryon CPU&#xff0c;性能超越苹果和英特尔&#xff0…

java 中arrayList 中去除重复项

ArrayList 中去除重复对象 Testpublic void removeRepeatItem() {ArrayList<String> arrayList new ArrayList<>();arrayList.add("apple");arrayList.add("banbana");arrayList.add("apple");arrayList.add("apple");S…

手写ThreadPoolExecutor线程池

很多人不推荐造轮子&#xff0c;我偏不。我造轮子又不是为了上生产环境&#xff0c;而是为了加深理解&#xff0c;有何不可&#xff1f;私以为造轮子几乎是最好的学习方式&#xff0c;甚至没有之一。因为造轮子需要至少做足以下两点&#xff1a; 了解设计思想&#xff08;设计…

java反射机制

java反射机制 方法四要素使用反射机制获取方法并调用方法 方法四要素 不使用反射机制调用一个方法需要几个要素的参与&#xff1f; 例&#xff1a; SystemService.java package com.w.spring6.reflection;public class SystemService {public void logout(){System.out.prin…

质量管理工作难做,为什么还有那么多人还继续做?

理解质量管理的挑战 在当今商业环境中&#xff0c;质量管理工作是一项充满挑战的使命。然而&#xff0c;尽管面对种种困难&#xff0c;却有着越来越多的人愿意踏上这条坎坷之路。为何质量管理工作如此艰难&#xff0c;却依旧吸引无数人投身其中呢&#xff1f; 内外动因交融 内…

【23真题】坑挖的不错,题目也有质量!

今天分享的是23年西安石油大学810的信号与系统试题及解析。 本套试卷难度分析&#xff1a;22年西安石油810考研真题&#xff0c;我也发布过&#xff0c;若有需要&#xff0c;戳这里自取!本套试题内容难度中等偏下&#xff0c;题量较少&#xff0c;没有考察选填题&#xff0c;通…

刚刚!奥特曼终于透露了GPT-5的最新消息!

原 创作者 | Tscom、王二狗 大爆料&#xff01;OpenAI被实锤正在研发GPT-5&#xff01; 还是OpenAI的CEO Sam Altman 亲口证实的。 今日&#xff0c;奥特曼接受《金融时报》的采访&#xff0c;透露了很多OpenAI的下一步计划&#xff0c;二狗帮大家整理成以下10个要点&#x…

搬家快递服务预约小程序的作用是什么

无论家庭还是企业办公&#xff0c;不少人都有搬家快递服务需求&#xff0c;尤其是近些年类似服务市场需求规模增长迅速。而在实际经营中&#xff0c;行业商家从业者也面临一些经营难题&#xff1a; 搬家公司的服务一般主要针对同省用户&#xff0c;同城需求较高&#xff0c;然…

实现定时巡检接口,测试不通过时自动发邮件

背景是这样的&#xff1a;最近组织架构调整&#xff0c;我们这个团队部分人员调入到了另外的业务组&#xff0c;因此她之前负责的业务需要交接给我们。 其中一个是接口每日监测&#xff0c;之前这个同事的做法是每天去手动点下按钮来跑接口测试&#xff0c;然后看一眼接口测试…

GZ038 物联网应用开发赛题第7套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 &#xff08;第7套卷&#xff09; 工位号&#xff1a;______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具&#xff0c;操作安全规范&#xff1b; 2、竞赛过程中如有异议&#xff0c;可向现场考评…

YOLOv8任务

介绍 YOLOv8是一个支持多个计算机视觉任务的人工智能框架。该框架可用于执行检测、分割、分类和姿态估计。每个任务都有不同的目标和用例。 检测 检测是YOLOv8支持的主要任务。它包括检测图像或视频帧中的对象&#xff0c;并在它们周围绘制边界框。检测到的对象根据其特征被分类…

全网火爆,Python接口自动化测试Mock服务详细总结(实战场景)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、Mock实现原理与…

SparkSQL声明式

简单案例 import org.apache.spark.sql.SparkSession import org.junit.Testcase class Person(id:Int,name:String,sex:String,age:Int) class DataSetCreate {val spark SparkSession.builder().appName("test").master("local[4]").getOrCreate()impo…

2023年【汽车驾驶员(中级)】免费试题及汽车驾驶员(中级)考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【汽车驾驶员&#xff08;中级&#xff09;】免费试题及汽车驾驶员&#xff08;中级&#xff09;考试试卷&#xff0c;包含汽车驾驶员&#xff08;中级&#xff09;免费试题答案和解析及汽车驾驶员&#xff08;…

人工智能基础_机器学习029_Lasso回归的使用_代码实现_稀疏性提现---人工智能工作笔记0069

然后我们再来看lasso回归,其实也是前面我们说的套索回归,我们说了 套索回归,具有稀松性,就是有一部分w会变成0对吧 我们先看一下套索回归的公式 公式我们可以去官网去看 可以看到这里上面有个写法是L1 = ||w||1这里两个竖,就是矩阵的写法,表示矩阵,然后 后面的部分|wi|绝对…

c++ 经典服务器开源项目Tinywebserver如何运行

第一次直接按作者的指示&#xff0c;运行sh ./build.sh,再运行./server&#xff0c;发现不起作用&#xff0c;localhost:9006也是拒绝访问的状态&#xff0c;后来摸索成功了发现&#xff0c;运行./server之后&#xff0c;应该是启动状态&#xff0c;就是不会退出&#xff0c;而…

【广州华锐互动】消防科普VR实训展馆增强群众学习兴趣和沉浸感

在现代社会&#xff0c;科技的发展已经深入到我们生活的各个角落&#xff0c;其中包括教育和信息传播领域。3D技术的引入为科普教育提供了全新的可能性。特别是在消防安全教育中&#xff0c;消防科普VR实训展馆的应用&#xff0c;不仅可以提高公众的消防安全意识&#xff0c;还…