در مقاله «
آشنایی با الکترون» با نحوه نصب و راه اندازی آن آشنا شدیم. در این مقاله با تعدادی اصطلاح،
آشنا شده و یک برنامه ساده را برای نوشتن و خواندن فایلها، مینویسیم.
فرآیندها (Processes) در الکترون به دو بخش تقسیم میشوند:
یک. فرآیند اصلی (Main Process) که همان فایل جاوااسکریپتی
است و توسط main، در فایل package.json مشخص شدهاست .فرآیند اصلی تنها
فرآیندی است که قابلیت دسترسی به امکانات گرافیکی سیستم عامل را از قبیل
نوتیفیکشن ها، دیالوگها ،Tray و ... دارد. فرآیند اصلی میتواند با
استفاده از شیء BrowserWindow که در قسمت قبلی کاربرد آن را مشاهده کردیم،
render process را ایجاد کند. با هر بار ایجاد یک نمونه از این شیء، یک
Render Process ایجاد میشود.
دو. فرآیند رندر (Render Process): از آنجا که الکترون از
کرومیوم استفاده میکند و کرومیوم شامل معماری چند پردازشی است، هر صفحهی وب
میتواند پردازش خود را داشته باشد که به آن Render Process میگویند. به
طور معمول در مرورگرها، صفحات وب در محیطی به نام SandBox اجرا میشوندکه
اجازه دسترسی به منابع بومی را ندارند. ولی از آنجا که الکترون میتواند از
Node.js استفاده کند، قابلیت دسترسی به تعاملات سطح پایین سیستم عامل را
نیز داراست.
در فرآیند اصلی، پنجرهها توسط BrowserWindow ایجاد میشوند و هر پنجرهای
که صفحه وبی را برای خودش باز میکند، شامل Render Process خودش است و هر پنجرهای که کارش خاتمه یابد، فرآیند مربوط به خودش به اتمام میرسد. فرآیند
اصلی، همه صفحات وب به همراه Render Process مربوط به خودشان را مدیریت
میکند و هر فرآیند رندر، از دیگری مجزا و محافظت شده است و تنها تمرکزش بر
روی صفحه وبی است که متعلق به خودش است.
در ابتدا قصد داریم یک منو برای برنامهی خود درست کنیم. برای ساخت منو،
راههای متفاوتی وجود دارند که فعلا ما راه استفاده از template را بر
میگزینیم که به صورت یک آرایه نوشته میشود. کدهای زیر را در فایل index.js یا
هر اسمی که برای آن انتخاب کردهاید بنویسید:
const electron = require('electron');
const {app,dialog,BrowserWindow,Menu,shell} = electron;
let win;
app.on('ready', function () {
win = new BrowserWindow({width: 800, height: 600});
win.loadURL(`file://${__dirname}/index.html`);
var app_menu=[
{
label:'پرونده',
submenu:[
{
label:'باز کردن',
accelerator:'CmdOrCtrl+O',
click:()=>{
}
},
{
label:'ذخیره',
accelerator:'CmdOrCtrl+S',
click:()=>{
}
}
]
},
{
label:'سیستم',
submenu:[
{
label:'درباره ما',
click:()=>
{
shell.openExternal('https://www.dntips.ir');
}
},
{
label:'خروج',
accelerator:'CmdOrCtrl+X',
click:()=>
{
win=null;
app.quit();
}
}
]
}
];
تا به اینجای کار، بیشتر کدها برای شما آشناست و فقط تغییرات اندکی در آنها
ایجاد شدهاست. مثلا شیء app و سایر اشیاء به طور خلاصهتری نوشته شدهاند.
در اینجا دو شیء menu و dialogو shell برای شما جدید هستند. بعد از آن ما یک
آرایه را برای منو تدارک دیدهایم که نحوه ساخت آن و تعاریفی مثل عنوان،
کلید میانبر یا ترکیبی و نحوه انتساب رویدادها را میبینید.
در خطوط بعدی، یک کار اضافهتر را جهت آشنایی بیشتر انجام میدهیم. قصد داریم اگر
سیستم عامل مکینتاش بود، نام برنامه هم در ابتدای نوار منو نمایش داده شود.
به همین جهت در ادامه خطوط زیر را اضافه میکنیم:
if(process.platform=="darwin")
{
const app_name=app.getName();
app_menu.unshift({
label:app_name
})
}
با استفاده از process.platform در node.js میتوانیم نوع پلتفرم جاری را
دریافت کنیم. مقادیر زیر، مقادیری هستند که بازگردانده میشوند:
ویندوز | win32 حتی اگر 64 بیتی باشد. |
لینوکس | linux |
مک | darwin |
فری بی اس دی | freebsd |
سولاریس | sunos |
سپس نام برنامه را از شیء app دریافت میکنیم و با استفاده از متد unshift، مقادیر داده شده را به ابتدای آرایه اضافه میکنیم.
دستو shell در بالا به شما اجازه میدهد با محیط دسکتاپ، یکپارچگی خود
را حفظ کنید و دستوراتی از قبیل باز کردن url، باز کردن یک مسیر دایرکتوری،
باز کردن یک فایل، انتقال فایل به سطل آشغال یا بازیافت و صدای بوق سیستم
(بیپ) را به شما میدهد. مستندات این شیء را در اینجا مطالعه فرمایید.
دستور app.quit همانطور که از نامش پیداست، باعث خاتمه برنامه میشود.
ولی یک نکته در اینجا وجود دارد که الزامی به نوشتن کدی برای اینکار نیست.
میتوانید زیرمنوی بالا را به شکل زیر هم بنویسید:
{
label:'خروج',
accelerator:'CmdOrCtrl+X',
role:'close'
}
خصوصیت role شامل چندین نوع اکشن مانند minimize,close,undo,redo و... میباشد که لیست کاملتر آن در
اینجا قرار دارد. اگر خصوصیت کلیک و role را همزمان استفاده کنید، خصوصیت role نادیده گرفته خواهد شد.
در انتها با اجرای دو دستور زیر، منو ساخته میشود:
var menu=Menu.buildFromTemplate(app_menu);
Menu.setApplicationMenu(menu);
در خط اول، منو توسط قالبی که با آرایهها ایجاد کردیم ساخته میشود و در خط دوم، منو به برنامه ست میشود.
حال قصد داریم برای زیرمنوی «باز کردن فایل» یک دیالوگ open درخواست کنیم.
برای این کار از شیء dialog استفاده میکنیم. پس خطوط زیر را به رویداد کلیک
این زیرمنو اضافه میکنیم:
dialog.showOpenDialog({
title:'باز کردن فایل متنی',
properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ]
,filters:[
{name:'فایلهای نوشتاری' , extensions:['txt','text']},
{name:'جهت تست' , extensions:['doc','docx']}
]
},
(filename)=>{
if(filename===undefined)
return;
dialog.showMessageBox({title:'پیام اطلاعاتی',type:"info",buttons:['تایید'],message:`the name of file is [${filename}]`});
});
این متد سه پارامتر دارد که اولین و آخرین پارامتر آن اختیاری میباشد.
اولین پارامتر آن شیء پنجره است. دومین پارامتر آن، تنظیم یک سری خصوصیات که
شامل (پسوندهای قابل قبول، عنوان، مسیر پیش فرض، قابلیت انتخاب چندگانه،
قابلیت باز کردن دایرکتوری و...) میشود که لیست کامل آن را میتوانید در
این
صفحه ببینید.
سومین پارامتر هم که در کد بالا ذکر شده است، callback میباشد که خروجی آن،
مسیر فایل مورد نظر است و اگر انتخاب چندگانه باشد، آرایهای با نام فایلهاست، که همگی آنها به همراه مسیرشان میباشند. در صورتیکه کاربر از دیالوگ
انصراف بدهد، پارامتر دریافتی با خروجی undefined همراه است. آخرین دیالوگ
هم نمایش یک پیام ساده است که نام فایل جاری را بر میگرداند. اگر خصوصیت
buttons را با آرایه خالی مقداردهی کنید، دکمه Ok نمایش داده میشود و اگر
هم مقداردهی نکنید با خطا روبرو خواهید شد.
برای قسمت ذخیره هم کد زیر را مینویسیم:
dialog.showSaveDialog({
title:'باز کردن فایل متنی',
properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ]
,filters:[
{name:'فایلهای نوشتاری' , extensions:['txt','text']}
]
},
(filename)=>{
if(filename===undefined)
return;
});
حال بهتر است این دیالوگهای جاری را هدفمند کنیم و بتوانیم فایلهای متنی
را به کاربر نمایش دهیم، یا آنها را ذخیره کنیم. به همین علت فایل html
زیر را نوشته و طبق دستوری که در مقاله «
آشنایی با الکترون» فرا گرفتیم، آن را نمایش میدهیم:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
Fie Content:<br/>
<textarea id="TextFile" cols="100" rows="50"></textarea>
</body>
</html>
برای تشکیل ساختار HTML میتوانید عبارت HTML را تایپ نمایید تا بعد از زدن
Enter، ساختار آن به طور خودکار تشکیل شود. سپس محتوا را مثل بالا به شکل
دلخواه تغییر میدهیم.
کاری که میخواهیم انجام دهیم این است که فایل متنی را باز کرده و محتوای
آن را در کادر متنی نشان دهیم و موقع ذخیره نیز محتوای نوشته شده در کادر
متنی را در فایلی ذخیره کنیم. از آنجا که main Process به المانهای DOM یا
Render Process دسترسی ندارد، باید از طریقی، ارتباط آن را برقرار کنیم.
یکی از راههای برقراری این ارتباط، IPC است. IPC در واقع یک فرستنده و یک
شنونده است که هر کدام در یک سمت قرار گرفته اند. فرستنده پیام را تحت یک
عنوان ارسال میکند و شنونده منتظر دریافت پیامی تحت همان عنوان میماند و
پیام دریافتی را پاسخ میدهد. در این مقاله، ما فقط قسمتی از این نوع
ارتباطات را بررسی میکنیم.
در نتیجه محتوای callback کدهای دیالوگ open و save را به شکل زیر تغییر میدهیم:
Open dialog.showOpenDialog({
title:'باز کردن فایل متنی',
properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ]
,filters:[
{name:'فایلهای نوشتاری' , extensions:['txt','text']},
{name:'جهت تست' , extensions:['doc','docx']}
]
},
(filename)=>{
if(filename===undefined)
return;
win.webContents.send('openFile',filename);
// dialog.showMessageBox({title:'پیام اطلاعاتی',type:"info",buttons:['تایید'],message:`the name of file is [${filename}]`});
});
Save dialog.showSaveDialog({
title:'باز کردن فایل متنی',
properties: [ 'openFile']//[ 'openFile', 'openDirectory', 'multiSelections' ]
,filters:[
{name:'فایلهای نوشتاری' , extensions:['txt','text']}
]
},
(filename)=>{
if(filename===undefined)
return;
win.webContents.send('saveFile',filename);
});
دستور win.webContents.send یک پیام را به صورت Async به سمت RenderProcess
مربوطه ارسال میکند. پارامتر اول، عنوان IPC است و پارامتر دوم، پیام IPC
است.
برای ایجاد شنونده هم کد زیر را به فایل index.html اضافه میکنیم:
<script>
const {ipcRenderer} = require('electron');
var fs=require('fs');
ipcRenderer.on('openFile', (event, arg) => {
var content= fs.readFileSync(String(arg),'utf8');
document.getElementById("TextFile").value=content;
});
ipcRenderer.on('saveFile', (event, arg) =>{
var content=document.getElementById("TextFile").value;
fs.writeFileSync(String(arg),content,'utf8');
alert('ذخیره شد');
});
</script>
در اینجا شوندههایی را از نوع ipcRenderer ایجاد میکنیم و با استفاده از متد on، به
پیامهایی تحت عنوانهای مشخص شده گوش فرا میدهیم. پیامهای ارسالی را که
حاوی آدرس فایل میباشند، به شیءای که از نوع fs میباشد، میدهند و آنها
را میخوانند یا مینویسند. خواندن و نوشتن فایل، به صورت همزمان صورت
میگیرد. ولی اگر دوست دارید که به صورت غیر همزمان پیامی را بخوانید یا
بنویسید، باید عبارت Sync را از نام متدها حذف کنید و یک callback را به
عنوان پارامتر دوم قرار دهید و محتوای آن را از طریق نوشتن یک پارامتر در
سازنده دریافت کنید.
فایلهای پروژه