اپلیکیشنهای Node.js را بسته به نیازها میتوان در مکانهای مختلفی میزبانی کرد. در ادامه فهرستی (غیر جامع) از گزینههای ممکن برای میزبانی و توزیع اپلیکیشنهای Node.js و ایجاد دسترسی عمومی معرفی شدهاند. این فهرست از سادهترین گزینههای پیکربندی محیط توسعه آغاز شده و در ادامه به سمت گزینههای پیچیده و قویتر حرکت میکند. برای مطالعه بخش قبلی این مجموعه مطلب آموزشی میتوانید به لینک زیر مراجعه کنید:
حتی در صورتی که یک IP دینامیک داشته باشید یا شبکهتان تحت NAT پیکربندی شده باشد، میتوانید اپلیکیشن خود را روی سیستم لوکال توزیع کنید و درخواستها را با استفاده از تونل لوکال مستقیماً از رایانه خودتان پاسخ دهید. این گزینه برای برخی مقاصد تست کردن، نمایش دمویی از یک محصول یا اشتراک یک اپلیکیشن با گروه معدودی از افراد مناسب است.
یک ابزار بسیار خوب برای این منظور وجود دارد که ngrok (+) نام دارد. با استفاده از ngrok میتوان صرفاً با وارد کردن دستور زیر پورت مورد نظر را برای دسترسی اینترنت باز کرد:
ngrok PORT
بدین ترتیب یک دامنه از ngrok.io به دست میآورید، اما با عضویت در طرحهای پولی این سرویس میتوانید یک URL سفارشی و گزینههای امنیتی بیشتری به دست آورید. به خاطر داشته باشید که در این روش سیستم خود را برای دسترسی اینترنت باز میکنید. سرویس دیگری که بدین منظور میتوان استفاده کرد localtunnel (+) است.
در این بخش برخی از روشهای توزیع اپلیکیشنهای Node.js را معرفی میکنیم که نیاز به هیچ پیکربندی ندارند.
Glitch: (+) یک محیط کاری و روشی برای ساخت اپلیکیشنها به روشی سریع است. به طوری که این اپلیکیشنها بیدرنگ در زیردامنه مربوطه از دامنه glitch.com در دسترس قرار میگیرند. در حال حاضر امکان داشتن یک دامنه سفارشی وجود ندارد و همچنین چند محدودیت دیگر وجود دارند؛ اما این سرویس برای ساخت پروتوتایپ بسیار عالی است. ظاهر آن جالب است و یک محیط ساده ملالآور محسوب نمیشود. شما در این محیط میتوانید همه قدرت Node.js، یک CDN، ذخیرهسازی امن برای کلیدهای احراز هویت، دستورهای ایمپورت/اکسپورت گیتهاب و موارد دیگر را در اختیار داشته باشید.
این سرویس از سوی شرکت FogBugz و Trello (و همچنین همبنیانگذار Stack Overflow) عرضه شده است. استفاده از آن برای مقاصد دمو توصیه میشود.
Codepen: کدپن (+) پلتفرم و جامعهای جذاب است. شما میتوانید پروژهای با چندین فایل ایجاد کرده و آن را روی یک دامنه سفارشی توزیع کنید.
Serverless: روشی برای انتشار اپلیکیشن است و در آن کلاً هیچ سروری برای مدیریت کردن وجود ندارد. Serverless پارادایمی است که در آن اپلیکیشنها به صورت تابع انتشار مییابند و به یک endpoint شبکه پاسخ میدهند. این پارادایم به نام FASS نیز شناخته میشود که اختصاری برای «Functions As A Service» (تابع به عنوان سرویس) است. لازم به اشاره است که Serverless Framework و Standard Library دو مورد از راهحلهای بسیار محبوب در این زمینه به حساب میآیند؛ هر دوی این موارد یک لایه تجرید برای انتشار روی AWS Lambda و دیگر راهحلهای FAAS مبتنی بر Azure یا Google Cloud ارائه میکنند.
PAAS: اختصاری برای عبارت «پلتفرم به عنوان سرویس» (Platform As A Service) است. این پلتفرمها بسیاری از مواردی را که شما در زمان توزیع اپلیکیشن باید در نظر داشته باشید، بر عهده میگیرند و بدین ترتیب دغدغههای شما کاهش مییابد.
Zeit Now: (+) یک گزینه جذاب است. در این پلتفرم کافی است عبارت now را در ترمینال وارد کنید تا عمل توزیع اپلیکیشن اجرا شود. نسخه رایگانی از آن وجود دارد که با برخی محدودیتها مواجه است؛ ولی نسخه پولی امکانات قویتری دارد. با استفاده از این گزینه کلاً فراموش میکنید که سروری هم وجود دارد و صرفاً به توزیع اپلیکیشن میپردازید.
Nanobox: (+) نیز یکی از گزینههای میزبانی و توزیع اپلیکیشنهای Node.js است.
Heroku: (+) یکی از گزینههای جالب دیگر برای توزیع اپلیکیشنهای Node.js محسوب میشود.
Microsoft Azure: سرویس ابری مایکروسافت است که گزینههای خوبی برای توزیع اپلیکیشنهای Node.js ارائه میکند.
Google Cloud Platform: پلتفرم ابری گوگل نیز ساختار جالبی برای اپلیکیشنهای شما ارائه میکند. بخش مستندات (+) Node.js آن کاملاً مفید است.
سروریهای مجازی دیگر: در این بخش برخی از شرکتهایی که سرورهای مجازی ارائه میکنند و مناسب میزبانی اپلیکیشنهای Node.js هستند را معرفی کردهایم. از جمله این شرکتها میتوان به «دیجیتال اوشن» (Digital Ocean)، «وب سرویسهای آمازون» و Linode اشاره کرد. در مورد سرویسهای وب آمازون به طور خاص باید به Amazon Elastic Beanstalk اشاره کرد، چون تا حدودی پیچیدگی AWS را کاهش داده است. از آنجا که این گزینهها یک ماشین خالی لینوکس ارائه میکنند تا روی آن کار کنید، راهنمایی خاصی در مورد آنها نمیتوان ارائه کرد. در دسته سرورهای مجازی (VPS) گزینههای زیادی وجود دارند که میتوان معرفی کرد و این موارد تنها شرکتهای شاخص محسوب میشوند.
سرورهای فیزیکی: گزینه دیگری که میتوان برای میزبانی اپلیکیشن Node.js استفاده کرد، این است که یک سرور فیزیکی را با نصب توزیعی از لینوکس پیکربندی کرده و به اینترنت وصل نمود.
REPL اختصاری برای عبارت «Read-Evaluate-Print-Loop» (خواندن-ارزیابی-پرینت-حلقه) است و روشی عالی برای کاوش قابلیتهای Node.js به طور سریع محسوب میشود. دستور node دستوری است که برای اجرای اسکریپتهای Node.js استفاده میشود.
node script.js
اگر نام فایل را فراموش کنیم، میتوانیم از روش REPL استفاده کنیم:
Node
اگر دستور فوق را در ترمینال وارد کنید، خروجی زیر را مشاهده میکنید:
❯ node >
این دستور در حالت انتظار (idle) میماند و منتظر وارد کردن مقدار دیگری است.
نکته: اگر از چگونگی باز کردن ترمینال اطلاع ندارید، برای آشنایی با محیط ترمینال یا خط فرمان در سیستمهای عامل مختلف میتوانید از مقالات زیر بهره بگیرید:
در حالت فوق REPL منتظر میماند تا نوعی کد جاوا اسکریپت را وارد کنید. برای شروع مقدار زیر را وارد کنید:
> console.log('test')
test
undefined
>مقدار نخست یعنی test، آن خروجی است که از کنسول میخواهیم پرینت کند و سپس مقدار «تعریف نشده» (undefined) دریافت میکنیم که مقدار بازگشتی ()console.log است. اینک میتوانیم خط جدیدی از کد جاوا اسکریپت وارد کنیم.
نکته جالب در مورد REPL این است که تعاملی است. همچنان که کد را مینویسید؛ اگر کلید tab را بزنید، REPL تلاش خواهد کرد آن چه را که مینویسید تکمیل کند و با متغیری که قبلاً تعریف شده یا متغیر از قبل تعریف شده است تطبیق دهد.
تلاش کنید نام کلاس جاوا اسکریپت مانند Number را وارد کنید و یک نقطه در انتهای آن وارد کرده و کلید tab را بزنید. در این زمان REPL همه مشخصهها و متدهایی که روی کلاس وجود دارند را نمایش میدهد:

امکان بازبینی اشیای سراسری با وارد کردن عبارت global. و فشردن کلید tab نیز وجود دارد:

اگر پس از یک کد، کاراکتر _ را وارد کنید، به آن معنی است که قصد دارید نتیجه آخرین عملیات را نمایش دهید.
REPL برخی دستورهای خاص دارد که همه آنها با یک نقطه (Dot) شروع میشوند و فهرستی از آنها به شرح زیر است:
REPL وقتی یک گزاره چند خطی را وارد میکنید، بدون نیاز به تایپ کردن دستور editor. این مسئله را میداند. برای نمونه، اگر شروع به تایپ کردن یک چرخه تکرار مانند زیر بکنید:
[1، 2، 3].forEach(num => {و سپس کلید Enter را بزنید، REPL به یک خط جدید میرود و آن را با 3 نقطه آغاز میکند که نشان میدهد شما مشغول کار روی یک بلوک هستید.
... console.log(num) ... })
اگر عبارت break. را در انتهای یک خط وارد کنید، حالت چند خطی متوقف میشود و گزاره اجرا نخواهد شد.
در یک برنامه Node.js، برای دریافت آرگومانهایی که در خط فرمان وارد میشوند، میتوانید آنها را در زمان فراخوانی اپلیکیشن به صورت زیر ارسال کنید:
node app.js
آرگومانها میتوانند مستقل باشند و یا برای مثال مانند زیر کلید و مقدار داشته باشند:
node app.js Flavio
و یا به صورت زیر باشند:
node app.js name=Flavio
بدین ترتیب روش بازیابی این مقدار از کد Node.js تغییر پیدا میکند. روشی که برای بازیابی آن استفاده میکنیم، شیء process است که از متدهای درونی Node.js محسوب میشود. این متد یک مشخصه argv افشا میکند. argv آرایهای است که شامل همه آرگومانهای ارسالی از خط فرمان است. آرگومان نخست مسیر کامل دستور node است. عنصر دوم مسیر کامل فایلی است که اجرا خواهد شد. همه آرگومانهای دیگر که از مکانی دیگر فوروارد میشوند در ادامه میآیند. میتوان حلقهای روی همه آرگومانها (شامل نام فایل و مسیر node) تعریف کرد:
process.argv.forEach((val، index) => {
console.log(${index}: ${val})
})میتوان تنها آرگومانهای اضافی را به دست آورد و به این منظور باید یک آرایه جدید که دو پارامتر نخست را حذف میکند ایجاد کرد:
const args = process.argv.slice(2)
اگر یک آرگومان بدون نام اندیس به صورت زیر داشته باشید:
node app.js Flavio
با استفاده از دستور زیر میتوانید به آن دسترسی داشته باشید:
const args = process.argv.slice(2) args[0]
در این حالت:
node app.js name=Flavio
[args[0 به صورت name=Flavio است و باید آن را تجزیه کنید. بهترین روش برای انجام این کار استفاده از کتابخانه minimist (+) است که متدهایی برای کار با آرگومانها ارائه کرده است:
const args = require('minimist')(process.argv.slice(2))
args['name'] //Flavioروش پرینت موارد مختلف در کنسول خط فرمان با استفاده از Node.js، از یک متد ابتدایی console.log تا سناریوهای پیچیدهتر متفاوت است.
Node.js یک ماژول console دارد که روشهای مفید زیادی برای تعامل با خط فرمان ارائه کرده است. این ماژول اساساً همان شیء console است که قبلاً در مرورگر دیدهایم. سادهترین و پراستفادهترین متد ()console.log نام دارد که یک رشته ارسالی را در کنسول پرینت میکند. اگر یک شیء ارسال شود، کنسول آن را به صورت رشته رندر میکند. امکان ارسال متغیرهای چندگانه به console.log به صورت زیر وجود دارد:
const x = 'x' const y = 'y' console.log(x، y)
در این حالت Node.js هر دوی آنها را پرینت میکند. امکان قالببندی عبارتها در حالت pretty با ارسال متغیرها و یک قید قالببندی مانند مثال زیر نیز وجود دارد:
console.log('My%s has%d years'، 'cat'، 2)در کد فوق موارد زیر را باید توضیح دهیم:
مثال:
console.log('%O'، Number)دستور ()console.clear، کنسول را پاک میکند (البته رفتار آن وابسته به نوع کنسول مورد استفاده است).
()console.count یک متد کارآمد است. این کد را در نظر بگیرید:
const x = 1 const y = 2 const z = 3 console.count( 'The value of x is ' + x + ' and has been checked.. how many times?' ) console.count( 'The value of x is ' + x + ' and has been checked.. how many times?' ) console.count( 'The value of y is ' + y + ' and has been checked.. how many times?' )
اتفاقی که در کد فوق میافتد این است که count تعداد دفعاتی که یک رشته پرینت میشود را میشمارد و تعداد را نیز پس از آن پرینت میکند. بدین ترتیب در مثال زیر تعداد سیبها و پرتقالها را میشماریم:
const oranges = ['orange'، 'orange']
const apples = ['just one apple']
oranges.forEach(fruit => {
console.count(fruit)
})
apples.forEach(fruit => {
console.count(fruit)
})ممکن است مواردی وجود داشته باشند که پرینت «رد پشته فراخوانی» (call stack trace) یک تابع مفید باشد و به یافتن پاسخ این پرسش کمک کند که: «چطور به این بخش از کد رسیدهایم؟»
پرینت رد پشته با استفاده از دستور ()console.trace به صورت زیر ممکن است:
const function2 = () => console.trace() const function1 = () => function2() function1()
این دستور رد پشته را پرینت میکند. برای نمونه اگر کد فوق را در Node REPL وارد کنیم، نتیجه زیر به دست میآید:
Trace at function2 (repl:1:33) at function1 (repl:1:25) at repl:1:1 at ContextifyScript.Script.runInThisContext (vm.js:44:33) at REPLServer.defaultEval (repl.js:239:29) at bound (domain.js:301:14) at REPLServer.runBound [as eval] (domain.js:314:12) at REPLServer.onLine (repl.js:440:10) at emitOne (events.js:120:20) at REPLServer.emit (events.js:210:7)
میتوان میزان زمانی که یک تابع طول کشیده اجرا شود را با استفاده از ()time و ()timeEnd به راحتی محاسبه کرد:
همانطور که دیدیم console.log برای پرینت کردن پیامها در کنسول کاملاً عالی است. این کار خروجی استاندارد یا stdout نام دارد. console.error در استریم stderr پرینت میکند. این مقدار در کنسول ظاهر میشود؛ اما در لاگ خطا قابل مشاهده است.
میتوان خروجی متن را در کنسول با استفاده از یک دنباله escape رنگی کرد. دنباله escape به مجموعهای از کاراکترها گفته میشود که برای مثال مانند زیر یک رنگ را شناسایی میکنند:
console.log('\x1b[33m%s\x1b[0m'، 'hi!')این کد را میتوان در Node REPL امتحان کرد و خروجی کار یک عبارت !hi با رنگ زرد خواهد بود.
با این وجود، این روشی سطح پایین برای انجام این کار است. سادهترین روش برای رنگی کردن خروجی کنسول استفاده از یک کتابخانه است. Chalk (+) چنین کتابخانهای است و علاوه بر رنگی کردن خروجی، امکان سبکبندیهای مختلف دیگر آن مانند درشت (bold) کردن قلم و یا نوشتن به صورت ایتالیک و زیرخطدار را نیز میدهد.
این کتابخانه با دستور npm install chalk نصب میشود و به صورت زیر میتوانید از آن استفاده کنید:
const chalk = require('chalk')
console.log(chalk.yellow('hi!'))استفاده از chalk.yellow نسبت به تلاش برای به خاطرسپاری کدهای escape بسیار آسانتر است و کد نیز خوانایی بیشتری پیدا میکند.
Progress (+) نام یک پکیج جالب است که برای ایجاد نوار پیشرفت در کنسول استفاده میشود. آن را با استفاده از دستور زیر میتوانید نصب کنید:
npm install progress
قطعه کد زیر یک نوار پیشرفته 10 مرحلهای ایجاد میکند. در هر 100 میلیثانیه یک گام از این نوار پیشرفت کامل میشود. زمانی که نوار به پایان برسد، بازههای زمانی پاک میشوند:
شاید تاکنون از خود پرسیده باشید چگونه میتوان یک برنامه CLI در Node.js را به صورت تعاملی درآورد؟
Node از نسخه 7 خود به بعد ماژول readline (+) را برای اجرای کارهایی مانند دریافت ورودی از استریم قابل خواندن مانند استریم process.stdin عرضه کرده است که در طی اجرای برنامه Node همان ورودی ترمینال است و به صورت هر بار یک خط ورودی را دریافت میکند.
قطعه کد فوق نام کاربری را میپرسد و زمانی که متنی وارد شود و دکمه Enter زده شود، یک خوشامدگویی ارسال میشود.
متد ()question نخستین پارامتر (یک سؤال) را نمایش میدهد و منتظر ورودی کاربر میماند. این متد در زمان فشرده شدن کلید enter یک تابع callback فراخوانی میکند. در این تابع callback اینترفیس readline را میبندیم.
readline چند متد دیگر نیز دارد که توصیه میکنیم مستندات آنها را در صفحهای که پیشتر معرفی کردیم بررسی کنید. اگر نیاز دارید که یک رمز عبور را الزام کنید، بهتر است آن را اینک مطرح کنید، اما آن را با استفاده از کاراکترهای * نمایش دهید. سادهترین روش برای این کار، استفاده از پکیج readline-sync است که بر اساس API شباهت زیادی به readline دارد و این کار را به صورت خودکار انجام میدهد. این پکیج را میتوان با استفاده از دستور زیر نصب کرد:
npm install inquirer
سپس کد فوق را به صورت زیر بازنویسی میکنیم:
فایل Inquirer.js امکان انجام کارهای زیادی مانند پرسیدن سؤالات چندگانه، ارائه دکمههای رادیویی، تأییدیهها و مواردی از این دست را میدهد.
البته شناخت همه گزینههای جایگزین به خصوص آنهایی که به صورت توکار از سوی Node.js ارائه شدهاند، مفید است؛ اما اگر میخواهید ورودی CLI را در سطح بالاتری مورد استفاده قرار دهید، Inquirer.js یک گزینه بهینه محسوب میشود.
Node.js یک سیستم ماژول داخلی با نام module.exports دارد که امکان استفاده از API داخلی و افشای دادهها به فایل دیگر در همان اپلیکیشن و یا حتی اپلیکیشنهای دیگر را میدهد.
هر فایل Node.js میتواند کارکردهای افشا شده از سوی فایلهای Node.js دیگر را ایمپورت کند. زمانی که میخواهید چیزی را برای استفاده خود ایمپورت کنید، باید از دستور زیر استفاده کنید:
const library = require('./library')از دستور فوق میتوان برای ایمپورت کردن کارکردهای افشا شده در فایل library.js که در پوشه کنونی قرار دارند استفاده کرد. در این فایل، هر کارکرد پیش از آن که بتواند از سوی فایلهای دیگر ایمپورت شود، باید افشا شود. هر شیء یا متغیر تعریف شده در این فایل به صورت پیشفرض خصوصی است و در معرض دسترسی دنیای خارجی قرار ندارد. برای افشای یک کارکرد باید از API با نام module.exports استفاده کنیم که از سوی سیستم ماژول Node.js ارائه شده است.
هنگامی که یک شیء یا تابع را به صورت یک مشخصه exports انتساب میدهید، این همان چیزی است که افشا میشود. بدین ترتیب میتوان آن را در بخشهای دیگر اپلیکیشن یا حتی اپلیکیشنهای دیگر نیز ایمپورت کرد. این کار به دو روش قابل اجرا است. روش نخست این است که یک شیء را به module.exports انتساب میدهیم. module.exports به صورت آماده مصرف از سوی سیستم ماژول ارائه شده است و کار اکسپورت فایل را به آن شیء تبدیل میکند.
روش دوم برای افزودن شیء اکسپورت شده به صورت یک مشخصه exports است. بدین ترتیب امکان اکسپورت شیءها، تابعها یا دادههای چندگانه وجود دارد:
روش مستقیم نیز به صورت زیر است:
در فایل دیگر نیز باید با ارجاع دادن به مشخصه ایمپورت خودتان از آن استفاده کنید:
روش دیگر نیز چنین است:
شاید بپرسید فرق بین module.exports و exports چیست؟ حالت نخست آن «شیء» را که به آن اشاره میکند افشا میکند. حالت دوم مشخصههای شیء مورد اشاره را افشا میکند. بدین ترتیب به پایان بخش دوم سری مقالات آموزش Node.js میرسیم. در بخش بعدی در مورد npm و روش نصب پکیجها صحبت خواهیم کرد.
https://blog.faradars.org/node-js-tutorial-part-2/
در این بخش از سری مقالات راهنمای عملی ساخت اپلیکیشنهای آیفون قصد داریم به بررسی مشخصههای آیتمهای ناوبری اپلیکیشن خود بپردازیم. در بخش قبلی ما صحنه News را وارد کنترلر ناوبری خودش کردیم. در این زمان میتوان نوار ناوبری را مشاهده کرد، اما از آنجا که آیتم ناوبری صحنه News هیچ عنوانی ندارد، نوار ناوبری خالی است.
برای مطالعه قسمت قبلی این مجموعه مطلب آموزشی میتوانید روی این لینک کلیک کنید.

به Xcode بروید و گزینه Main.storyboard را اگر انتخاب نشده است، انتخاب کنید. طوری اسکرول کنید که صحنه News پدیدار باشد. روی آیتم ناوبری در صحنه News کلیک کنید تا انتخاب شود. اگر پنل Document Outline نیز در حالت نمایش است در این زمان خواهید دید که Navigation Item در آنجا نیز انتخاب شده است. در روی بوم نیز چنین وضعیتی وجود دارد.
اگر پنل Inspector در سمت راست فعال نباشد، روی دکمه انتهای سمت راست نوار ابزار کلیک کنید تا پدیدار شود. اینک میتوانید Attributes Inspector را ببینید. اگر چنین نیست روی آیکون چهارم بالای پنل Inspector کلیک کنید تا به آن سوییچ شود.

پنل Attributes Inspector همه خصوصیتهای موجود (گاهی مشخصه نیز نامیده میشود) را برای شیء انتخاب شده کنونی نمایش میدهد. شما میتوانید اغلب این موارد را تغییر دهید. در این مورد باید یک عنوان برای آیتم ناوبری انتخاب شده وارد کنیم.
در فیلد title در پنل Attributes Inspector عبارت News را وارد کرده و روی Return کلیک کنید. آیتم ناوبری روی بوم و همچنین بخش «دورنمای سند» اینک باید عنوانی به صورت News داشته باشد.

برای این که به اهداف طراحی خود که در بخش اول این سری مقالات راهنما مطرح کردهایم، برسیم باید رنگ نوار ناوبری را به صورت قرمز تعیین کنیم. شاید متوجه شده باشید که خصوصیتهای آیتم ناوبری شامل تنظیمات رنگ نیست. دلیل این امر چنان که در بخش سومتوضیح دادیم، آن است که آیتم ناوبری که از سوی کنترلر ناوبری برای هر صحنه طراحی میشود، منحصر به فرد است؛ اما نوار ناوبری خودش ثابت است و تحت مالکیت کنترلر ناوبری قرار دارد. این همان جایی است که باید نوار ناوبری را تغییر دهیم.
در کنترلر ناوبری برای صحنه News یک بار روی نوار ناوبری کلیک کنید تا انتخاب شود.

همان طور که میبینید خصوصیتهای زیادی برای نوار ناوبری وجود دارد. در حال حاضر ما تنها چند مورد از آنها را بررسی میکنیم. روی منوی بازشدنی کنار Bar Text کلیک و گزینه custom را انتخاب کنید.
Xcode گزینه «انتخاب رنگ» (Color Picker) را نمایش میدهد.
در نوار ابزار این بخش انتخاب رنگ، برگه Color Sliders که دارای آیکون اسلایدر و گزینه دوم از سمت چپ است را انتخاب کنید. در منوی بازشونده گزینه RGB Sliders را انتخاب کنید. مقدار رنگ را به صورت مقادیر زیر تعیین کنید:
میزان «مات بودن» (Opacity) را نیز برابر با 100% تنظیم کنید. اینک میبینید که رنگ پسزمینه نوار ناوبری روی بوم تغییر یافته است.

در این مرحله بخش انتخاب کننده رنگ را ببندید. اکنون که نوار ناوبری تیره داریم، متن تیرهرنگ عنوان آیتم ناوبری و متن «نوار وضعیت» (Status Bar) کنتراست مناسبی ندارند. ما باید از Xcode بخواهیم که با نوار ناوبری به صورت یک شیء تیره رفتار کند تا رنگ این عناوین به صورت سفید درآید.
به این منظور منوی بازشدنی Style را برای نوار ناوبری به صورت Black تنظیم کنید. همزمان سبک عناوین بزرگ را انتخاب کنید تا با ظاهرتصویر ابتدای مقاله اول این سری که میخواهیم ایجاد کنیم مطابقت داشته باشد. در ادامه کادر انتخاب Prefers Large Titles را فعال کنید.

اگر اپلیکیشن خود را به مثابه یک لِگو تصور کنیم، تنها کاری که انجام دادهایم این است که رنگ و ارتفاع نوار ناوبری را تغییر دادهایم. ساختار بلوکها و لایهها همچنان همان است. نوار ناوبری Large Titles باعث میشود که عناوین آیتمهای ناوبری به جای مرکز در سمت چپ قرار بگیرند.
در صحنه News میتوان یک سلول خالی منفرد را زیر هدر بخش Prototype Cells مشاهده کرد. در ادامه میخواهیم محتوایی برای این بخش تعیین کنیم.
یک بار روی سلول کلیک کنید تا انتخاب شود. دقت کنید که نباید دو بار کلیک صورت بگیرد، چون در این صورت دیگر سلول انتخاب نمیشود و به نمای محتوای داخل آن منتقل میشویم. اگر شیء نادرستی را انتخاب کردهاید، کافی است با کلیک روی بخش خالی بوم (یعنی غیر از فضای صحنه)، همه چیز را از حالت انتخاب خارج کنید و سپس دوباره اقدام به انتخاب بخش مورد نظر خود بکنید.
زمانی که سلول را به طرز صحیحی انتخاب کردید، همانند تصویر زیر باید عبارت Table View Cell را در بخش فوقانی Attributes Inspector ببینید.

ما میخواهیم کاربر بتواند روی این سلول در صحنه News کلیک کند تا مقاله خبری مرتبط را در صحنهای دیگر با جزییات بیشتر مشاهده کند. کاربر مورد نظر برای انجام این کار به نوعی راهنمایی نیاز دارد که به وی اشاره کنیم امکان تپ کردن روی سلول را دارد. استاندارد این کار «disclosure indicator» است که یک پیکان خاکستری با نوک اشاره کننده به سمت راست در سمت راست سلول است.
بدین ترتیب به سادگی میتوان به سلول اعلام کرد که یک نشانگر disclosure را نمایش دهد و سلول میداند که باید یک «نمای فرعی» (subview) به نام accessory view در سمت راست سلول اضافه کند.
در بخش Attributes Inspector منوی بازشدنی Accessory را از مقدار None به Disclosure Indicator تغییر دهید.

این سلول هم اینک یک نشانگر Disclosure دارد که همان پیکان خاکستری است که به سمت راست اشاره میکند. به طور مشابه سلول نمای جدولی، چهار گزینه style دارد که به صورت خودکار نماهای فرعی برچسب را در سلول اضافه میکنند. سبک subtitle دو برچسب را به عنوان نمای فرعی در سمت چپ اضافه میکند.
در این زمان باید منوی بازشدنی Style را به صورت Subtitle تنظیم کنید. بدین ترتیب خواهید دید که برچسبهای متنی خالی در سلول ظاهر میشوند. در صورتی که Content View را باز کنید، میبینید که در بخش دورنمای سند نیز این موارد قابل مشاهده هستند.

روی برچسب Title در سلول دو بار کلیک کنید. اینک میبینید که برچسب با نوشته Label به صورت عنوان در پنل نمایش مییابد.

اینک باید متن برچسب را تغییر دهیم. به این منظور میتوانیم هم در بوم روی آن کلیک کنیم و هم در پنل Attributes inspector این کار را انجام دهیم. متن برچسب را از Title به Lego Block Chain تغییر دهید. این تیتر در واقع عنوان مقاله خبری خیالی ما است.

به طور مشابه، متن برچسب Subtitle را انتخاب کرده و برخی جزییات را برای مقاله خبری اضافه کنید.

اگر اپلیکیشن خودمان را در این مرحله اجرا کنیم، سلولها علیرغم این که عناوین و تیترهای مختلفی را در آنها وارد کردهایم، همچنان خالی مشاهده میشوند. دلیل این امر آن است که نمای جدولی هم اینک روی حالت «پروتوتایپهای دینامیک» (dynamic prototypes) تنظیم شده است. این بدان معنی است که نمای جدولی انتظار دارد مقادیر هر سلول به صورت دینامیک و احتمالاً از طریق ارتباط با یک سرور خبری پر شوند. ما در حال حاضر هیچ کد یا حتی سروری در این زمینه نداریم و از این رو قصد داریم جدول را به صورت محتوای استاتیک تنظیم کنیم.
به این منظور در صحنه News یک بار روی ناحیه خاکستری Table View —Prototype Content پایین سلول کلیک کنید. اینک پنل Attributes Inspector باید عنوان Table View را نمایش دهد. منوی بازشدنی Content را از Dynamic Prototypes به Static Cells تغییر دهید.

هنگامی که سلول را از حالت دینامیک به استاتیک تغییر میدهید، توجه داشته باشید که Xcode سه سلول ایجاد میکند که در واقع یک کپی از همه سلولهای اولیه ما هستند. متن سلول دوم باید یک مقاله خبری خیالی دیگر باشد.

یک بار روی سلول سوم کلیک کنید. با بررسی این که دستگیره انتخاب در لبه انتهایی سلول سوم مشاهده میشود و این که عنوان پنل Attributes Inspector به صورت Table View Cell درآمده است، مطمئن شوید که این سلول انتخاب شده است.

این سلول را با فشردن کلید Delete کیبورد، حذف کنید.
اپلیکیشن را اجرا کنید تا آنچه را تا به اینجا طراحی کردهایم، مشاهده کنید.
همان طور که میبینید، نوار ناوبری و سلولها همگی خصوصیتها و محتواهایی که طراحی کردهایم را نمایش میدهند. سلولها هنوز هیچ تصویر یا قالببندی متن ندارند و از این رو بخشی از متن را نمایش میدهند. این وضعیت را در بخش بعدی این سری مقالات اصلاح خواهیم کرد.
برای کامیت کردن تغییرات همانند دفعات قبل مراحل زیر را اجرا میکنیم:
News: customised attributes in navigation bar، navigation item، table view and cells.
3. روی دکمه Commit کلیک کنید.
در این بخش از سری مقالات راهنمای ساخت اپلیکیشن آیفون که قسمت چهارم آن است از پنل Attributes Inspector برای سفارشیسازی نوار ناوبری، آیتم ناوبری، نمای جدولی و سلولهای آن استفاده کردیم. هر گونه سؤال یا دیدگاه مرتبط با این نوشته را میتوانید در بخش نظرات با ما در میان بگذارید. در بخش بعدی بحث ناوبری اپلیکیشن خود را تکمیل میکنیم. برای مطالعه بخش بعدی روی لینک زیر کلیک کنید:
واقعیت این است که دیباگ کردن Node.js همواره کار دشواری است. اگر تاکنون فرصت کدنویسی یک پروژه در Node.js را داشتهاید، میدانید که یافتن منشأ خطا در این نوع کدها و در نتیجه دیباگ Node.js پیچیدگی و دردسر فراوانی به همراه دارد.
برخلاف کدهای جاوا اسکریپت که در مرورگر دیباگ میشوند و یا کدهای جاوا که با استفاده از یک IDE قوی مانند IntelliJ بررسی میشوند، در Node.js امکان تعیین «نقاط توقف» (breakpoint) در نقاط دلخواه، رفرش کردن صفحه یا راهاندازی مجدد کامپایلر و بررسی گام به گام کد، ارزیابی توابع و یافتن متغیر عوض شده یا مفقود وجود ندارد. همین ناتوانی است که موجب دشواری دیباگ کدهای Node.js شده است.
با این حال، امکان دیباگ وجود دارد و صرفاً به زحمت بیشتری نیاز دارد. در ادامه گزینههای موجود را بررسی کرده و سادهترین روش را معرفی خواهیم کرد.
چند روش برای دیباگ برنامههای مشکلدار Node.js وجود دارد که در ادامه آنها را فهرستبندی کردهایم و لینکهای مربوطه را نیز در صورت نیاز به مطالعه بیشتر ارائه کردهایم.
این روش بارها امتحان خود را پس داده است؛ اما در صورتی که تاکنون کدهای جاوا اسکریپت نوشته باشید باید مقدار بیشتری توضیح بدهیم. این ابزار درون Node.js قرار دارد و همانند ابزار داخلی جاوا اسکریپت که لاگ را در کنسول مرور نشان میداد، موارد مرتبط را در ترمینال نمایش میدهد.
معادل این ابزار در جاوا ()System.out.println و در پایتون ()print نام دارد. ابزار یاد شده سادهترین روش برای پیادهسازی دیباگ است و همچنین سریعترین روش برای آلوده کردن کدهای تمیز به خطوطی از اطلاعات اضافی محسوب میشود. به هر حال این روش در برخی موارد به یافتن و اصلاح خطا کمک میکند.
مستندات Node.js نیز درک کرده است که دیباگ کردن کدها کار آسانی نیست و از این رو یک مرجع کارآمد (+) برای کمک به شروع دیباگ ارائه کرده است.
این ابزار مفید است؛ اما اگر صادقانه سخن بگوییم رمزگشایی از آن به جز برای کسانی که تجربه زیادی در کدنویسی دارند کار دشواری محسوب میشود. بدین ترتیب ممکن است به سرعت در دام مباحثی چون UUID-ها، وبسوکتها و «عوارض امنیتی» بیافتید و دچار سرگیجه شوید. در واقع بهتر بود که در این زمینه از روش غیر پیچیدهتری استفاده میشد.
بخش ابزارهای توسعهدهنده در مرورگر کروم امکان دیباگ کردن کدهای Node.js را فراهم میسازد که به ظاهر روش سادهای محسوب میشود، اما اگر میخواهید این روش را بیازمایید، ممکن است متوجه شوید که نیم ساعت سپری شده است و هنوز نتوانستهاید پنجره DevTools را به برنامه Node وصل کنید. به هر حال به نظر میرسد استفاده از Chrome DevTools برای دیباگ کدهای Node.js پیچیدهتر از آن است که باید باشد.
JetBrains یکی از شرکتهای محبوب توسعه نرمافزار است که IDE-هایی مانند IntelliJ و WebStorm را عرضه کرده است. این شرکت اکوسیستم جالبی از افزونهها برای ابزارهای خود ارائه کرده است.
به لطف جامعه گسترده کاربران JetBrains، مقالات زیادی در حوزههای متفاوت و از جمله دیباگ کردن کدهای Node.js نوشته شده است. اما این مورد نیز همچون مستندات Node و Chrome DevTools آسان نیست. کاربر در این روش باید پیکربندیهای زیادی انجام دهد، پردازشهای اجرایی را الحاق کند و همچنین «ترجیحات» (Preferences) را به مقدار زیادی پیکربندی کند تا WebStorm آماده دیباگ کردن شود.
این گزینه یکی از سادهترین روشهای دیباگ کردن کدهای Node.js را ارائه کرده است. هر کسی که VS Code را یک بار تجربه کرده، عاشق آن شده است.
VS Code کاری را که هیچ کدام از گزینههای دیگر در خصوص دیباگ کردن کدهای Node.js موفق به انجام نشدند، اجرایی کرده و آن را به فرایندی کاملاً ساده تبدیل کرده است. دیباگ کردن با استفاده از VS Code چنان ساده است که هر کسی میتواند آن را به سرعت راهاندازی و اجرا کند و مهم نیست که چه میزان از آشنایی با IDE-ها، Node و یا برنامهنویسی دارد.
در این بخش اقدام به تنظیم VS Code و دیباگ کردن Node.js میکنیم. به این منظور، میبایست قبلاً VS Code را دانلود (+) و نصب کرده باشید. بنابراین در ادامه شروع به تنظیم آن میکنیم.
در نرمافزار VS Code به بخش Preferences > Settings بروید و در کادر جستجو عبارت node debug را وارد کنید. زیر برگه Extensions یک اکستنشن با عنوان «Node debug» ظاهر میشود. در اینجا روی کادر نخست Debug > Node: Auto Attach کلیک کرده و منوی بازشدنی را روی on تنظیم کنید. اینک آماده شروع به کار هستیم.

اکنون به فایل پروژه Node.js خود بروید و با کلیک کردن روی سمت چپ فایل در هر جایی که دوست دارید برخی نقاط توقف را تنظیم کنید. سپس در ترمینال عبارت node –inspect <FILE NAME> را وارد کنید تا عملکرد آن را مشاهده کنید.

اگر به یک پروژه Node.js نیاز دارید که این موارد را تست کنید میتوانید این پروژه (+) را دانلود کنید. این پروژه جهت تست کردن شکلهای مختلف استریم کردن مقادیر بالایی از داده در Node ساخته شده است؛ اما در این دموی ما نیز به خوبی کار میکند. زمانی که کلید Enter را بزنید. بخش انتهایی ترمینال VS Code به رنگ نارنجی در میآید تا نشان دهد که در حالت دیباگ هستید. کنسول نیز پیامی را مبنی بر الحاق دیباگر (‘Debugger Attached’) نمایش میدهد.

زمانی که این وضعیت را مشاهده کردید، بدانید که موفق به دیباگ کردن کدهای Node.js در VS Code شدهاید.
اینک میتوانید نقاط توقف را در گوشه پایین-چپ صفحه مشاهده کنید. امکان فعال و غیرفعال کردن آنها نیز با انتخاب کادر مربوطه وجود دارد. بدین ترتیب امکان اجرای خط به خط کد و گزینههای step over ،step in ،restart و غیره، همانند زمانی که با استفاده از دکمه Play کوچک در مرورگر اجرا میکردید وجود خواهد داشت. به این منظور باید از دکمههای بخش میانی و بالای IDE استفاده کنید. VS Code نقاط توقف و خطی را که کد در آن متوقف شده است هایلایت میکند تا پیگیری فرایند کار، آسان باشد.

زمانی که با استفاده از دکمههای فوق از یک نقطه توقف به نقطه توقف بعدی حرکت میکنید میتوانید ببینید که برنامه موارد console.log را در کنسول دیباگ در بخش تحتانی ویژوال استودیو کد پرینت میکند و با حرکت به سمت جلو با رنگ زرد هایلایت میشود.

همان طور که میبینید هر چه در برنامه پیش میرویم مواردی که در کنسول دیباگ به نمایش در میآیند افزایش مییابند و در طی این مسیر میتوان اشیا و تابعها را در دامنه محلی با استفاده از ابزارهای گوشه چپ-بالای VS Code دقیقاً همانند روش بررسی موارد مشابه در مرورگر بررسی کرد.
دیباگ کردن Node.js به لطف VS Code دیگر مانند گذشته دشوار نیست و لازم نیست 500 بار ()console.log را در کد خود تکرار کنیم تا بفهمیم که باگ کجا هست. گزینه Debug > Node ویژوال استودیو کد به کمک روش الحاق خودکار پردازش این وضعیت را دگرگون ساخته است.
https://blog.faradars.org/easiest-way-to-debug-node-js-with-vscode/
«بوکه» (Bokeh)، کتابخانهای برای بصریسازی تعاملی دادهها است. برخلاف دیگر کتابخانههای موجود برای بصریسازی دادهها در «زبان برنامهنویسی پایتون»، مانند «مَتپلاتلیب» (Matplotlib) و «سیبورن» (Seaborn)، بوکه گرافیکهای خود را با استفاده از «اچتیامال» (HTML) و «جاوا اسکریپت» (JavaScript) رندر میکند. این موضوع موجب شده تا بوکه به گزینهای مناسب برای ساخت دشبوردها و اپلیکیشنهای مبتنی بر وب مبدل شود. به طور کلی باید گفت، کتابخانه بوکه ابزاری قدرتمند برای اکتشاف و درک دادهها یا ساخت نمودارهای زیبا برای پروژهها و گزارشها است. هدف از این راهنما آن است که روش کار با کتابخانه بوکه با بهرهگیری از مجموعه دادههای جهان واقعی، آموزش داده شود. سرفصلهای زیر در این مطلب ارائه شدهاند.
ساخت خروجیهای بصری با استفاده از کتابخانه بوکه (Bokeh) شامل گامهای زیر میشود:
اکنون، هر یک از مراحل بیان شده در بالا، همراه با جزئیات بیشتر مورد بررسی قرار میگیرد.
بصریسازی دادهها همیشه با دادههای ورودی آغاز میشود. در این گام معمولا از کتابخانههای «پیشپردازش» (Preprocessing)دادهها مانند «پانداس» (Pandas) و «نامپای» (NumPy) استفاده میشود و طی آن، گامهای مورد نیاز برای تبدیل دادهها به فرم مناسب برای انجام پردازشها، برداشته میشود.
در این گام، چگونگی تولید و نمایش بصریسازیها، به وسیله کاربر تعیین میشود. در این راهنما، دو گزینه متداولی که بوکه در این راستا فراهم میکند ارائه شدهاند که عبارتند از: تولید یک فایل HTML استاتیک و رندر کردن بصریسازیها به صورت خطی در «ژوپیتر نوتبوک» (Jupyter Notebook).
در این مرحله، اشکال (Figure) توسط کاربر تعیین و بوم اولیه برای بصریسازی دادهها آماده میشود. در گام انتخاب اشکال، میتوان همه چیز از عناوین گرفته تا علامتها را سفارشیسازی کرد. همچنین، میتوان با بهرهگیری از مجموعهای از ابزارها کاربران را قادر به تعامل با بصریسازیها کرد.
اکنون، از قابلیت رندر کردن بوکه برای شکل دادن به دادهها استفاده میشود. در این فاز، باید از انعطافپذیری موجود برای ترسیم دادهها از پایه با استفاده از اشکال و نشانهگرهای متعدد موجود استفاده کرد؛ همه این موارد به سادگی قابل سفارشیسازی هستند. این عملکرد، آزادی فوقالعادهای را جهت ارائه دادهها در اختیار کاربر قرار میدهد. علاوه بر موارد بیان شده، بوکه دارای توابع توکاری برای ساخت چیزهایی مانند «نمودار میلهای تجمعی» (Stacked Bar Charts) و قابلیتهایی برای ساخت بصریسازیهای پیشرفتهتر مانند گرافهای شبکه و نقشهها است.
بوکه، برای افرادی که نیاز به بیش از یک تصویر برای تشریح دادههای خود دارند نیز ابزاری مناسب محسوب میشود. این کتابخانه، نه فقط گزینههای «قالب» (layout) شبکه مانند استاندارد را فراهم میکند، بلکه این امکان را برای کاربر فراهم میکند تا به سادگی بصریسازیها را با تنها با چند خط کد در یک قالب قرار دهد. علاوه بر آن، نمودارها را میتوان به سرعت به یکدیگر لینک کرد؛ بنابراین، انتخاب یکی از نمودارها، بر کلیه نمودارهای دیگر (هر ترکیبی از دیگر گزینهها) نیز تاثیرگذار است.
در نهایت، زمان آن فرا میرسد که آنچه ساخته شده است را نمایش داد. اگرچه، بصریسازیها در مرورگر یا نوتبوک قابل مشاهده هستند، کاربر قادر به بررسی خروجیها (بصریسازیها)، سفارشیسازی آنها و ویرایش قسمتهای تعاملی موجود در آنها است. اگر کاربر آنچه که دیده را بپسندد، میتواند بصریسازیها را به صورت یک فایل تصویر (Image) ذخیرهسازی کند. در غیر این صورت (یعنی در صورتی که خروجیها نظر کاربر را جلب نکرد)، کاربر میتواند گامهای انجام شده در بالا را برای کسب خروجیهایی که در نظر دارد، مورد بازنگری قرار دهد. شش گام بیان شده در بالا، بلوکهای سازنده یک الگوی سازمانیافته و انعطافپذیر هستند که از مرحله دریافت دادهها از مجموعه داده تا ترسیم آنها به صورت نمودارهای جذاب و قابل فهم را پوشش میدهند.
یکی از قطعه کدهای متداولی که برای انجام کلیه گامهایی که پیشتر بیان شدند مورد استفاده قرار میگیرد در بالا نمایش داده شده است.
راهکارهای متعددی برای ارائه خروجی بصری از دادهها در کتابخانه «بوکه» (Bokeh) وجود دارد. در این راهنما، گزینههای معرفی شده در زیر مورد استفاده قرار میگیرند.
شایان ذکر است که هیچ یک از دو تابع بالا بصریسازیها را به کاربر نمایش نمیدهند. در واقع این اتفاق مادامی که ()show فراخوانی نشده نمیافتد. اگرچه، این توابع اطمینان حاصل میکنند که در صورت فراخوانی ()show، بصریسازیها به گونهای نمایش داده میشود که کاربر تمایل دارد. با فراخوانی همزمان ()output_file و ()output_notebook در یک اجرای واحد، بصریسازی هم روی فایل HTML استاتیک و هم روی نوتبوک به صورت توکار انجام میشود. اگر به هر دلیلی کاربر چند دستور ()output_file را به طور همزمان اجرا کند، تنها آخرین دستور برای رندر کردن مورد استفاده قرار میگیرد. اکنون، فرصت خوبی فراهم شده تا کد اولیه در ()figure پیشفرض بوکه با استفاده از ()output_file نوشته شود.

همانطور که مشهود است، یک پنجره جدید مرورگر باز شده که به آن Empty Bokeh Figure و empty figure گفته میشود. آنچه در تصویر مشهود نیست و در پشت صحنه به وقوع میپیوندد، ساخته شدن فایلی با نام output_file_test.html در پوشه کاری جاری کاربر است. اگر کاربر قصد داشته باشد که قطعه کدی مشابه با ()output_notebook را به جای ()output_file اجرا کند، با این فرض که Jupyter Notebook در حال اجرا است میتواند از قطعه کد زیر استفاده کند.

همانطور که مشهود است، نتایج مشابه هستند و صرفا رندر کردن در محلهای متفاوتی به وقوع پیوسته است. اطلاعات بیشتر پیرامون ()output_file و ()output_notebook در مستندات رسمی بوکه (+) موجود است. نکته شایان توجه آن است که گاهی هنگام رندر کردن چندین بصریسازی به ترتیب، مشاهده میشود که رندرهای قبلی مربوط به اجراهای پیشین پاک نشدهاند. در صورت مواجه شدن با چنین شرایطی، میتوان کد زیر را «وارد» (Import) و در میان اجراها اجرا کرد.
پیش از ادامه دادن این مطلب، لازم به ذکر است که بوکه به طور پیشفرض با یک جعبه ابزار از پیش بارگذاری شده میآید. اطلاعات بیشتر پیرامون این جعبه ابزار و چگونگی پیکربندی آن در بخش افزودن تعامل، در پایان این راهنما ارائه شده است.
اکنون که چگونگی ساخت و نمایش یک تصویر کلی بوکه هم در مرورگر و هم در ژوپیتر نوتبوک آموزش داده شد، زمان آن رسیده تا بیشتر پیرامون چگونگی پیکربنی شی ()figure توضیح داده شود. شی ()figure فقط پایه بصریسازی دادهها نیست، بلکه شیئی است که ابزارهای بوکه برای بصریسازی دادهها را قفلگشایی میکند. figure در بوکه یک زیرکلاس از «Bokeh Plot Object» است که پارامترهای زیادی را برای پیکربندی عناصر زیباییشناختی شکل فراهم میکند. برای درک بهتر این مطلب، در ادامه زشتترین تصویر ممکن با قطعه کد زیر ساخته میشود.

هنگامی که شی ()figure معرفی شد، میتوان آن را پیکربندی کرد. اکنون، فرض میشود که کاربر میخواهد خطوط شبکهای را حذف کند. برای این کار میتوان از قطعه کد زیر استفاده کرد.
مشخصات خطوط شبکهای از طریق خصیصه grid شکل در دسترس هستند. در این مثال، تنظیم grid_line_color روی None، به شکل موثری همه خطوط شبکهای را به صورت یکباره حذف میکند.

نکته: اگر کاربر در نوتبوک یا «محیط توسعه یکپارچه» (Integrated Development Environment | IDE) با قابلیت «تکمیل خودکار» (auto-complete) کار میکند، میتواند از مزایای قابل توجه آن استفاده کند. ()figure دارای عناصر قابل سفارشیسازی متعددی است و قابلیت تکمیل خودکار برای کشف گزینههای موجود برای سفارشیسازی بسیار مفید محسوب میشود.

در صورتی که محیط توسعه مورد استفاده کاربر دارای قابلیت تکمیل خودکار نیست، انجام یک جستوجوی سریع، با کلیدواژه Bokeh و آنچه که کاربر قصد دارد انجام دهد، معمولا او را به مسیر صحیحی هدایت میکند. مطالب بسیار زیادی برای آموزش دادن در همین رابطه وجود دارند، اما ورود بیش از حد به جزئیات، این مطلب را از هدف اصلی آن دور میکند. برای مطالعه بیشتر در این رابطه، میتوان از موارد زیر استفاده کرد.
در ادامه، برخی از گزینههای سفارشیسازی خاصی که فراگیری آنها ارزشمند است بیان شدهاند.
گاهی، تا هنگامی که بخشی از دادهها بصریسازی نشوند، مشخص نیست که تصویر چقدر نیاز به ویرایش و سفارشیسازی دارد. بنابراین، در ادامه روش انجام این کار آموزش داده خواهد شد.
یک شکل خالی، مساله جذاب و قابل توجهی نیست؛ بنابراین در ادمه به بررسی glyphs که بلوک سازنده بصریسازیهای Bokeh است پرداخته میشود. glyph یک شکل گرافیکی برداریسازی شده یا «نشانگری» (Marker) است که برای ارائه دادهها در اشکالی مانند یک دایره یا مربع مورد استفاده قرار میگیرد. مثالهای بیشتر در این رابطه در گالری بوکه (+) موجود هستند. پس از ساخت شکل، باید به دستهای از متدهای قابل پیکربندی glyph دسترسی داده شود. اکنون، کار با یک مثال بسیار پایهای آغاز میشود که طی آن چندین نقطه روی یک دستگاه مختصات x-y ترسیم میشوند.

پس از معرفی ()figure، میتوان دید که چگونه میتوان از آن برای ترسیم دادهها در دستگاه مختصات x-y با استفاده از glyphهای circle سفارشیسازی شده استفاده کرد. در ادامه برخی از دستههای glyph معرفی شدهاند.
glyphها را میتوان ترکیب کرد تا قابلیتهای لازم برای بصریسازی تامین شوند. اکنون فرض میشود که هدف ساخت تصویری است که تعداد کلمات نوشته شده در هر روز برای ساخت این راهنما را با یک خط نشانگر روند رشد تعداد کلمات (روزانه و تجمعی) نشان دهد.

برای ترکیب ستونها و خطها در شکل، این موارد با استفاده از شی ()figure ساخته شدهاند. علاوه بر آن، میتوان مشاهده کرد که با تنظیمات legend برای هر glyph چگونه میتوان راهنمای نمودار را به صورت یکپارچه ترسیم کرد. راهنمای نقشه نیز با تخصیص «top_left» به «fig.legend.location» به گوشه بالا و سمت راست نمودار انتقال داده میشود.
کتابخانه بصریسازی بوکه فرصت خوبی را برای کار با دادههای گوناگون فراهم میکند. جذابیت Bokeh در آن است که تقریبا پیادهسازی هر ایده بصریسازی با آن امکان دارد و این موضوع فقط به چگونگی استفاده از ابزارهایی که در اختیار کاربر قرار داده بستگی دارد. در مثالی که در ادامه به آن پرداخته میشود از مجموعه دادههای عمومی Kaggle که مربوط به ۷۲ فصل از مسابقات بسکتبال اتحادیه ملی بسکتبال آمریکا ( the National Basketball Association’s (NBA) 2017-18 season) هستند، استفاده شده است.
پس از دانلود مجموعه داده، با استفاده از قطعه کدی که در ادامه آمده، دادهها در دیتافریم کتابخانه «پانداس» (Pandas) خوانده میشوند.
این قطعه کد دادهها را از سه فایل CSV خوانده و به صورت خودکار ستونهای داده را به صورت اشیا datetime تفسیر میکند. اکنون زمان آن رسیده که روی دادههای واقعی کار شود.
در مثال بالا از لیستهای پایتون و آرایههای «نامپای» (Numpy Arrays) برای نمایش دادهها استفاده شده است. کتابخانه «بوکه» (Bokeh) برای مدیریت این نوع دادهها به خوبی تجهیز شده است. اگرچه، هنگامی که بحث دادهها در پایتون میشود، احتمال استفاده از دیکشنریها و دیتافریمهای پانداس نیز وجود دارد، به ویژه اگر دادهها از یک فایل یا منبع داده خارجی خوانده میشوند. Bokeh برای کار با این ساختارهای داده پیچیدهتر توانمند و حتی دارای توابع توکاری برای مدیریت آنها است؛ به عنوان مثالی از این توابع میتوان به ColumnDataSource اشاره کرد.
اکنون، امکان دارد این پرسش مطرح شود که «اگر Bokeh دارای رابطی برای کار با دادهها به صورت مستقیم است چرا از ColumnDataSource استفاده شود؟». اولا، هنگامی که به یک لیست، آرایه یا دیتافریم به طور مستقیم ارجاع داده میشود، بوکه در پشت صحنه به هر حال آن را به یک ColumnDataSource تبدیل میکند. دوم و مهمتر آنکه، ColumnDataSource پیادهسازی قابلیتهای تعاملی بوکه را سادهتر میسازد.
ColumnDataSource در انتقال دادن دادهها به glyphهایی که برای بصریسازی مورد استفاده قرار میگیرند نقش بنیادین دارد. کارکرد اصلی آن، برای نگاشت اسامی به ستونهای دادهها است. این کار موجب میشود ارجاع به عناصر دادهها هنگام ساخت بصریسازیها آسانتر شود. این تابع همچنین انجام کار مشابهی را هنگام ساخت بصریسازیها آسانتر میکند. ColumnDataSource میتواند سه نوع شی داده بیان شده در زیر را تفسیر کند.
اکنون، کار با بصریسازی مسابقات برای جایگاه اول در NBA’s Western Conference in 2017-18 برای مدافع قهرمانی یعنی تیم Golden State Warriors و رقیب آن Houston Rockets آغاز میشود. رکوردهای روزانه پیروزی-شکست برای این دو تیم در یک دیتافریم با نام west_top_2 ذخیره شده است.
از اینجا میتوان این DataFrame را در دو شی ColumnDataSource بارگذاری و مسابقه را بصریسازی کرد.

چگونگی ارجاع داده شدن اشیای ColumnDataSource هنگام ساخت دو خط قابل توجه است. اسامی ستون اصلی به سادگی به عنوان پارامترهای ورودی پاس داده میشوند و مشخص میشود که کدام ColumnDataSource با خصیصه source مورد استفاده قرار بگیرد. بصریسازیها نشانگر وجود مسابقات تنگاتنگی در این فصل و همچنین، حاکی از تاثیر قابل توجه Warriors در میانه فصل هستند. اگرچه، یک بیت لغزش در فصل پیش امکان بالا رفتن را برای Rockets فراهم کرده و در نهایت از مدافعان قهرمانی پایان فصل، به عنوان تیم اول پیشی گرفته است.
نکته: در بوکه، میتوان رنگها را بر اساس اسم، مقدار پایه ۱۶ (hex value)، یا کد رنگ RGB تعیین کرد. برای بصریسازی بالا، دو رنگ برای خطوط مربوطه تعیین شدهاند که نشانگر دو تیم هستند. به جای استفاده از اسامی رنگهای CSS، کاربر ممکن است بخواهد که با استفاده از رنگهای رسمی تیمها به صورت کدهای هگزادسیمال جلوه زیبایی به خروجیها ببخشد. در عین حال، میتوان از تاپلهایی که کدهای رنگ RGB را نشان میدهند (۲۰۶، ۱۷، ۶۵) برای Rockets و کدهای (۰، ۱۰۷ و ۱۸۲) برای Warriors استفاده کرد.
بوکه لیست مفیدی از اسامی رنگهای CSS که بر اساس فام اصلی آنها دستهبندی شدهاند را ارائه میکند. همچنین، میتوان از سایتهای متعددی که برای یافتن کدهای رنگی به صورت CSS ،RGB و hex وجود دارند، استفاده کرد. اشیای ColumnDataSource میتوانند کاری بیش از ارجاعدهی به ستونهای DataFrame انجام دهند. شی ColumnDataSource دارای سه فیلتر توکار است که میتوانند برای ساخت نمایشهایی از دادهها با استفاده از شی CDSView مورد استفاده قرار بگیرند؛ این سه فیلتر در ادامه معرفی شدهاند.
در مثال پیشین، دو شی ColumnDataSource ساخته شدند که هر یک از آنها زیرمجموعهای از دیتافریم west_top_2 هستند. مثال بعدی، خروجی مشابهی را از یک ColumnDataSource بر پایه west_top_2 با استفاده از GroupFilter میسازد که دیدی از دادهها فراهم میکند.

چگونگی پاس دادن GroupFilter به CDSView در یک لیست قابل توجه است. این کار، امکان ترکیب چندین فیلتر با یکدیگر را برای ایزوله کردن دادههایی که از ColumnDataSource مورد نیاز هستند فراهم میکند. Western Conference به عنوان مسابقاتی مهیج پایان یافت که رقابتهای آن حقیقتا تنگاتنگ برگزار شده بودند. این رقابتها در ادامه به صورت بصری ارائه خواهند شد. بنابراین، در ادامه به موضوع بعدی یعنی layoutها پرداخته خواهد شد.
جدول ردهبندی Eastern Conference به دو دسته از رقابتها در بخش آتلانتیک تقسیم میشود که عبارتند از: Boston Celtics و Toronto Raptors. پیش از تکرار گامهایی که برای ساخت west_top_2 مورد استفاده قرار گرفتند، بهتر است یکبار دیگر ColumnDataSource با استفاده از آنچه پیشتر توضیح داده شد مورد آزمون قرار بگیرد. در این مثال، چگونگی خوراک دادن کل DataFrame به یک ColumnDataSource و ساخت نمایشهایی برای ایزوله کردن دادههای مرتبط، بیان خواهد شد.

ColumnDataSource قادر به ایزوله کردن دادههای مرتبط در یک دیتافریم ۵، ۰۴۰ در ۳۹ بدون سختی قابل توجه و تنها با چند خط کد «پانداس» (Pandas) است. با نگاه به بصریسازیها، میتوان مشاهده کرد که رقابتهای Eastern Conference دارای هیچ مشکلی نیستند. پس از آنکه Celtics نتوانستند میادین را به خوبی حفظ کنند، Raptorsها همه راه را بازگشتند تا بر رقیب خود غلبه کرده و فصل را با پنج برد بیشتر به اتمام برسانند. با دو بصریسازی آماده، زمان آن رسیده تا این نمودارها در کنار هم قرار داده شوند و تحلیلهایی روی آنها صورت پذیرد. مشابه با عملکرد subplot در «مَتپِلاتلیب» ( Matplotlib)، بوکه نیز دارای توابع column، row و gridplot در ماژول bokeh.layouts است. این توابع میتوانند به صورت عمومیتر با عنوان layout دستهبندی شوند. استفاده از این layoutها کار سختی نیست. اگر هدف، قرار دادن دو بصریسازی در پیکربندی عمودی باشد میتوان به صورت زیر عمل کرد.

تنها با تغییر column با row در قطعه کد بالا، میتوان به طور مشابه دو نمودار را به صورت افقی در کنار هم قرار داد.
نکته: برای افرادی که همراه با مطالعه این مطلب در تلاش برای نوشتن کدهای خودشان هستند، در ادامه نکاتی پیرامون خطایی که امکان دارد با آن مواجه شوند ارائه میشود. این خطا ممکن است ضمن دسترسی به west_fig و east_fig در مثالی که در ادامه ارائه شده رخ دهد. خطای مذکور، چیزی شبیه آنچه در زیر آمده، خواهد بود.
این تنها یکی از خطاهای متعددی است که به عنوان ماژول اعتبارسنجی بوکه به وقوع میپیوندند که در آن W-1004 هشداری پیرامون استفاده مجدد از west_fig و east_fig در یک layout جدید است. برای اجتناب از این خطاها، همانطور که مثالها تست میشوند، قطعه کدی که هر لایه را نمایش میدهد به صورت زیر آغاز میشود:
انجام این کار موجب نوسازی مولفههای مرتبط برای رندر کردن بصریسازیها و حصول اطمینان از این موضوع میشود که هیچ هشداری مورد نیاز نیست. به جای استفاده از column یا row، کاربر ممکن است بخواهد که از gridplot استفاده کند. یک تفاوت کلیدی gridplot با دیگر موارد آن است که به طور خودکار نوار ابزار را در سراسر شکلهای (Figures) فرزند تحکیم میکند. همانطور که مشهود است، در gridplot به جای آنکه یک تاپل به عنوان ورودی به آن داده شود، نیازمند لیستی از لیستها است که در آن هر زیر لیست یک سطر از شبکه را نشان میدهد:

در نهایت، gridplot امکان انتقال مقادیر None را فراهم میکند که به صورت زیرنمودارهای خالی تفسیر میشوند. از این رو، اگر کاربر قصد قرار دادن یک placeholder برای دو نمودار اضافهتر را داشته باشد، میتواند به صورت زیر عمل کند.

اگر کاربر قصد جابهجایی میان بصریسازیها در سایز کامل آنها را بدون پایین کشیدن آنها برای متناسب شدن در کنار یا بر فراز هم داشته باشد، یک گزینه خوب tabbed layout است. یک tabbed layout شامل دو تابع ویجت Bokeh است: ()Tab و ()Panel از زیرماژول bokeh.models.widgets. همچون استفاده از ()gridplot، ساخت یک tabbed layout نیز کار سادهای به حساب میآید.

اولین گام، ساخت یک ()Panel برای هر tab است. این موضوع ممکن است کمی گیجکننده به نظر برسد، در واقع باید به تابع ()Tabs به عنوان مکانیزمی که Tabهای جداگانه ساخته شده با ()Panel را سازماندهی میکند نگریست. هر ()Panel یک فرزند را به عنوان ورودی دریافت میکند که میتواند یک ()figure یا layout تنها باشد. (باید به خاطر داشت که layout یک نام عمومی برای column ،row یا gridplot است.) پس از آنکه پنلها سرهم شدند، میتوان آنها را به ()Tabs در یک لیست انتقال داد. اکنون که چگونگی دسترسی، ترسیم و سازماندهی دادهها مشخص شد، زمان آن رسیده که معجزه بوکه را روی دادههای واقعی دید: تعامل!
ویژگی که بوکه را از دیگر کتابخانههای موجود متمایز میکند، توانایی آن برای پیادهسازی آسان تعامل در بصریسازیها است. بوکه حتی تا توصیف خودش به عنوان یک کتابخانه بصریسازی تعاملی پیش میرود.
Bokeh یک کتابخانه بصریسازی تعاملی است که مرورگرهای مدرن را برای ارائه، هدف قرار میدهد.
در این بخش، چهار راهکاریی که میتوان با استفاده از آنها تعامل را به بصریسازیها افزود بیان شدهاند.
پیادهسازی این عناصر تعاملی امکان اکتشاف دادهها را به شیوهای فراهم میکند که بصریسازیهای استاتیک نمیتوانند انجام دهند.
چنانکه پیشتر در بخش «ساخت شکل اولیه» بیان شد، ()figure پیشفرض بوکه با یک نوار ابزار عرضه میشود. نوار ابزار پیشفرض با ابزارهایی که در ادامه بیان شدهاند ارائه میشود (از چپ به راست).
نوار ابزار را میتوان با پاس دادن toolbar_location=None هنگام معرفی یک شی ()figure حذف کرد و یا با پاس دادن هر یک از گزینههای above، below، left یا right تغییر مکان داد. علاوه بر آن، نوار ابزار را میتوان برای در بر گرفتن هر ترکیبی از ابزارهایی که کاربر تمایل دارد پیکربندی کرد. بوکه ۱۰ ابزار اختصاصی در ۱۸ دسته ارائه می کند که در زیر بیان شدهاند.
پیادهسازی رفتار انتخاب به سادگی افزودن چند کلیدواژه خاص هنگام اعلام glyphها است. دومین مثال، یک نمودار پراکندگی میسازد که تعداد کل ضربات سه امتیازی را نسبت به درصد ضربات ساخته شده (برای بازیکنانی با دستکم ۱۰۰ ضربه سه امتیازی) نمایش میدهد. دادهها را میتوان از دیتافریم player_stats تجمیع کرد.
در ادامه نمونهای از دیتافریمهای حاصل شده را میتوان مشاهده کرد.
name play3PA play3PM pct3PM 229 Corey Brewer 110 31 0.281818 78 Marc Gasol 320 109 0.340625 126 Raymond Felton 230 81 0.352174 127 Kristaps Porziņģis 229 90 0.393013 66 Josh Richardson 336 127 0.377976
اکنون، فرض میشود که کاربر قصد انتخاب گروهی از بازیکنان را در توزیع دارد و در این راستا رنگ glyphها که نشانگر بازیکنان انتخاب نشده هستند را خاموش میکند.
کاربر ابتدا باید ابزارهای انتخابی که میخواهد در دسترس باشند را تعیین کند. در مثال بالا، box_select، lasso_select، poly_select و tap (به علاووه دکمه reset) در لیستی که select_tools نامیده میشود تعریف شدهاند. هنگامی که شکل تعریف شد، نوار ابزار در موقعیت below در نمودار قرار میگیرد و لیست به tools برای در دسترس قرار دادن ابزارهای انتخاب شده در بالا انتقال داده میشود. هر بازیکن در ابتدا با یک مربع به رنگ آبی تیره نمایش داده میشود، اما پیکربندیهای بیان شده در زیر هنگامی که یک بازیکن یا بازیکنان انتخاب میشوند تنظیم شده است.
تنها با چند تغییر سریع، بصریسازی اکنون به چیزی مانند تصویر زیر مبدل شده است.

ابزار ()HoverTool اندکی با ابزارهای انتخاب شده در بالا متفاوت و دارای مشخصههایی و به ویژه tooltips است. ابتدا، میتوان یک tooltip را با ساخت لیستی از تاپلها که حاوی توصیفها و ارجاعات به ColumnDataSource هستند ساخت. این لیست به عنوان ورودی به ()HoverTool انتقال داده شده و سپس به سادگی با استفاده از ()add_tools به شکل افزوده شده است. در ادامه، آنچه به وقوع پیوسته قابل مشاهده است.

اضافه شدن دکمه Hover به جعبه ابزار قابلیت روشن و خاموش شدن را میافزاید که در تصویر بالا میتوان آن را مشاهده کرد. در صورتی که کاربر قصد تاکید بیشتر بر بازیکنان در Hover را داشته باشد، بوکه انجام این کار را با وارسی Hover امکانپذیر ساخته است. در ادامه، کد اندکی ویرایش شده از قطعه کد بالا که tooltip به آن افزوده شده، ارائه شده است.
این کار با ساخت یک glyph کاملا جدید انجام میشود، در این مثال، از دایرهها به جای مربع استفاده شده که به hover_glyph تخصیص داده شدهاند. توجه به این نکته لازم است که شفافیت اولیه برابر با صفر تعیین شده، بنابراین تا هنگامی که نشانگر موس آن را لمس نکند، به صورت شفاف است. مشخصههایی که برای hover ظاهر میشوند با تنظیم hover_alpha برابر با ۰.۵ در امتداد hover_fill_color تنظیم میشوند. اکنون، هنگام Hover کردن روی مارکرهای گوناگون، دایره کوچک مشکی روی مربع اصلی ظاهر میشود.

پیوند دادن، فرآیند همگامسازی عناصر از بصریسازیهای گوناگون درون یک قالب است. برای مثال، ممکن است کاربر بخواهد که محورهای چند نمودار را برای اطمینان از اینکه اگر روی آنها بزرگنمایی شود روی دیگری منعکس میشوند به یکدیگر پیوند دهد. برای این مثال، بصریسازیها قادر به Pan کردن (اسکرول افقی) به بخشهای مختلف از برنامه تیم و آزمودن وضعیت آمارهای بازیهای گوناگون هستند. آمارها به وسیله نمودار در یک ()gridplot نمایش داده میشوند. دادهها از دیتافریم team_stats قابل گردآوری هستند. در ادامه، تیم Philadelphia 76ers به عنوان تیم مورد علاقه انتخاب میشود.
در ادامه، نتایج پنج بازی اول 76ers آورده شده است.
gmDate teamPTS teamTRB teamAST teamTO opptPTS game_num winLoss 10 2017-10-18 115 48 25 17 120 1 L 39 2017-10-20 92 47 20 17 102 2 L 52 2017-10-21 94 41 18 20 128 3 L 80 2017-10-23 97 49 25 21 86 4 W 113 2017-10-25 104 43 29 16 105 5 L
کار با وارد کردن (Import) کتابخانههای لازم بوکه، مشخص کردن پارامترهای خروجی و خواندن دادهها در ColumnDataSource آغاز میشود.
هر بازی به وسیله یک ستون ارائه میشود و اگر نتیجه بُرد بود سبز و اگر باخت بود قرمز رنگ میشود. برای انجام این کار، CategoricalColorMapper بوکه، میتواند برای نگاشت مقادیر داده به رنگهای تعیین شده مورد استفاده قرار بگیرد.
برای این بررسی موردی، لیستی که مقادیر داده دستهای را نگاشت میکند به factors و لیستی با رنگهای مورد نظر به palette پاس داده میشود. چهار آمار برای بصریسازی در یک gridplot دو در دو وجود دارد که عبارتند از امتیازها (point)، پاسهای منجر به گل (assist)، ریباندها (rebounds) و گردشها (turnovers). در ساخت چهار شکل و پیکربندی نمودارهای مربوط به آنها، افزونگی زیادی در ویژگیها وجود دارد. بنابراین، برای ساده کردن کد از یک حلقه for استفاده میشود.
همانطور که مشهود است، تنها پارامترهایی که نیاز به تنظیم داشتند y-axis-label شکل و دادههایی که top را در vbar دیکته میکند هستند. این مقادیر به راحتی در یک dict ذخیره میشوند که در آن تکرار به منظور ساخت شکل برای هر آمار انجام میشود. همچنین، میتوان پیادهسازی CategoricalColorMapper در پیکربندی glyph مربوط به vbar را مشاهده کرد. خصوصیت رنگ یک ٰdict را با فیلد درون ColumnDataSource پاس میدهد تا نگاشت شود؛ بدین شکل، نام CategoricalColorMapper در بالا ساخته شده است. دید اولیه فقط ۱۰ بازی اول از فصل 76ers را نشان میدهد، بنابراین، نیاز به راهی برای اسکرول افقی (pan horizontally) برای کاوش در کل بازیهای این فصل است. بدین ترتیب، پیکربندی نوار ابزار امکان لازم برای داشتن یک ابزار xpan که قابلیت Pan کردن از طریق نمودار را بدون داشتن هرگونه نگرانی پیرامون چولگی تصادفی در نمایش در طول محور عمودی دارد، فراهم میکند. اکنون که شکل ساخته شد، gridplot را میتوان با ارجاع به اشکالی از dict ساخته شده در بالا راهاندازی کرد.
پیوند دادن محورهای چهار نمودار به سادگی تنظیم کردن x_range برای کلیه اشکال به صورت مساوی با یکدیگر است.
برای افزودن نوار عنوان به بصریسازی، میتوان این کار را روی شکل امتیازها انجام داد، اما در صورت انجام چنین کاری، فضای شکل محدود میشود. بنابراین، یک ترفند خوب استفاده از قابلیتهای Bokeh برای تفسیر HTML جهت درج عنصر Div محسوب میشود که شامل اطلاعات عنوان است. پس از آنکه ساخته شد، به سادگی میتوان آن را با ()gridplot در قالب column ترکیب کرد.
کنار هم قرار دادن همه قسمتها منجر به ساخت شکل زیر میشود.

به طور مشابه، میتوان به سادگی انتخابهای لینک شده را در جایی که انتخاب در یک نمودار روی دیگری نیز منعکس میشود، پیادهسازی کرد. برای اینکه مشخص شود این کار به چه صورت انجام میشود، بصریسازی بعدی حاوی دو نمودار پراکندگی خواهد بود، یکی درصد ضربات دو امتیازی 76ers را در مقایسه با فیلد درصد ضربات سه امتیازی نمایش میدهد و دیگری امتیاز تیمهای 76ers را در مقایسه با امتیازهای حریف به صورت بازی به بازی نشان میدهد. هدف از این کار آن است که کاربر قادر به انتخاب نقاط داده در نمودار پراکندگی سمت چپ و همچنین، تشخیص اینکه آیا نقاط داده متناظر در نمودار سمت راست مربوط به بردها هستند یا باختها، باشد. دیتافریم برای این بصریسازی شباهت زیادی به دیتافریم مربوط به مثال اول دارد.
شکلی که دادهها به نظر میرسند:
کد لازم برای ساخت بصریسازیها به صورت زیر است:
این نمایش خوبی از قدرت موجود در استفاده از ColumnDataSource است. تا هنگامی که رندر کننده glyph (در این مثال، glyphs «دایره» (circle) برای درصدها و «مربع» (square) برای شکستها و پیروزیها) ColumnDataSource مشابهی را به اشتراک میگذارند، انتخابها به صورت پیشفرض به یکدیگر لینک میشوند. در شکل زیر نمایش آنچه بیان شد به صورت بصری ارائه شده و همانطور که مشهود است آنچه در نمودار یک سمت تصویر انتخاب میشود در سمت دیگر نیز منعکس میشود.

با انتخاب یک نمونه تصادفی از نقاط داده در ربع بالا سمت راست از نمودار پراکندگی سمت چپ، نقاط متناظر آنها در نمودار مقایسه ضربات دو امتیازی و سه امتیازی برجسته میشود. به طور مشابه، هنگامی که نقاط داده در نمودار سمت راست که مربوط به امتیاز تیمها و رقبایشان است انتخاب میشوند، نقاط داده متناظر آنها در نمودار سمت چپ نمایش داده میشود.
این گام ما را به آخرین مثال تعاملی در این راهنما هدایت میکند: legendهای تعاملی. در بخش «ترسیم دادهها با Glyphها» مشهود بود که پیادهسازی یک legend هنگام ساخت نمودار چقدر آسان است. با استفاده از legend افزودن تعامل صرفا با تخصیص یک click_policy انجامپذیر خواهد بود. با استفاده از یک خط کد، میتوان توانایی hide یا mute کردن دادهها را با بهرهگیری از legend ایجاد کرد. در این مثال، میتوان دو نمودار پراکندگی یکسان را دید که امتیازها و ریباندهای بازی به بازی برای James و Kevin Durant را مقایسه میکنند. تنها تفاوت آنها در این است که یکی از hide به عنوان click_policy خود و دیگری از mute برای این منظور استفاده میکند. اولین گام برای پیکربندی خروجی و راهاندازی دادهها ساخت چشماندازی برای هر بازیکن از دیتافریم player_stats است.
پیش از ساخت شکل، پارامترهای متداول در شکل، مارکرها و دادهها را میتوان در دیکشنریها تلفیق و استفاده مجدد کرد. این کار نه تنها از «افزونگی» (redundancy) در گامهای بعدی جلوگیری میکند، بلکه راه سادهای برای پیچش این پارامترها در صورت نیاز فراهم میکند.
اکنون که مشخصههای گوناگون تنظیم شدند، دو نمودار پراکندگی را میتوان به شیوه خلاصهتری ساخت که کد آن در زیر آمده است.
توجه به این نکته لازم است که mute_fig دارای یک پارامتر افزوده با عنوان muted_alpha است. این پارامتر شفافیت مارکرها را هنگامی که mute به عنوان click_policy مورد استفاده قرار میگیرد تعیین میکند. در نهایت، click_policy برای هر تصویر تنظیم و در پیکربندی افقی نمایش داده میشود.

هنگامی که legend در موقعیت خود قرار گرفت، تمام آنچه نیاز است تخصیص دادن hide یا mute به مشخصه click_policy شکل است. این کار موجب میشود legend پایهای به یک legend تعاملی مبدل شود. شایان توجه است که به ویژه برای mute، مشخصههای اضافی برای muted_alpha در glyphهای circle مربوطه برای LeBron James و Kevin Durant تنظیم شدهاند. این کار، جلوههای بصری مشتق شده از تعاملهای legend را دیکته میکند.
در این راهنما چگونگی پیکربندی اسکریپتهای پایتون با بهرهگیری از کتابخانه بوکه، به منظور رندر کردن یک فایل HTML استاتیک یا نوتبوک ژوپیتر مورد بررسی قرار گرفت. همچنین، چگونگی نمونهسازی و سفارشیسازی شی ()figure، ساخت بصریسازیها با استفاده از glyphها، دسترسی و فیلتر دادهها با ColumnDataSource، سازماندهی نمودارها در لایوتهای شبکهای و tab شده و همچنین، افزودن اشکال گوناگونی از تعامل شامل انتخاب، hover کردن، پیوند دادن و افزودن Legendهای تعاملی آموزش داده شد.
https://blog.faradars.org/bokeh-from-zero-to-hero/
فریمورک مالتیمدیا در اندروید از ضبط و پخش صدا نیز پشتیبانی میکند. در این مطلب مراحل ساخت یک اپلیکیشن ضبط صدا را بررسی میکنیم که قابلیت ضبط کردن صدا و ذخیرهسازی آن در حافظه داخلی دستگاه اندرویدی را دارد. این کار با استفاده از MediaRecorder که از سوی SDK اندروید ارائه شده است صورت میگیرد. همچنین با روش تقاضای آنی «دسترسیهای کاربر» (User Permissions) و شیوه کار با حافظه داخلی یک دستگاه اندرویدی آشنا خواهیم شد.
ابتدا باید رابط کاربری اپلیکیشن ضبط صدای خودمان را بسازیم. این رابط از یک طرحبندی ساده با 3 دکمه تشکیل یافته است که برای آغاز، مکث/ ازسرگیری و توقف ضبط صدا استفاده میشوند.
پس از ایجاد رابط کاربری میتوانیم با استفاده از MediaRecorder شروع به ساخت اپلیکیشن خود بکنیم. اما قبل از آن باید مجوز دسترسیهای مورد نیاز خود برای ضبط صدا و استفاده از حافظه داخلی را از کاربر بخواهیم. این کار با چند خط کد ساده در فایل AndroidManifest.xml ممکن است:
همچنین پیش از استفاده عملی از MediaRecorder لازم است که بررسی کنیم آیا کاربر واقعاً مجوز دسترسیها را ارائه کرده است یا نه؛ این کار را در فایل MainActivity.kt انجام میدهیم:
نکته: این خطوط کد در ادامه به فراخوانی OnClickListener در دکمه start_recording انتقال خواهند یافت تا بتوانیم مطمئن باشیم که MediaRecorder بدون در اختیار داشتن دسترسیهای لازم شروع به کار نمیکند.

پس از تهیه مقدمات کار اینک نوبت آن رسیده است که به صورت عملی اقدام به ضبط و ذخیرهسازی صدا بکنیم.
ابتدا باید یک OnClickListeners به دکمههای خود اضافه کنیم تا مطمئن شویم که به رویدادهای اجرا شده از سوی کاربر پاسخ میدهند. همان طور که پیشتر گفتیم وجود مجوز دسترسیهای صحیح را پیش از افزودن فراخوانی OnClickListener به دکمه start_recording بررسی میکنیم.
سپس باید یک مسیر برای خروجی تعریف کرده و شروع به پیکربندی MediaRecorder خودمان کنیم.
در کد فوق مسیر به سوی ریشه حافظه دستگاه را یافته و نام فایل ضبط شده و نوع فایل را به آن اضافه میکنیم. پس از آن شیء MediaRecorder را ایجاد کرده و منبع صدا، انکودر صدا، قالب خروجی و فایل خروجی را تعریف میکنیم.
کد مورد استفاده برای آغاز به کار MediaRecorder در رویداد OnClickListener برای دکمه start_recording قرار میگیرد:
همان طور که مشاهده میکنید ما باید تابع prepare را پیش از آن که اقدام به ضبط صدا بکنیم، فراخوانی کرده باشیم. همچنین آن را درون بلوک try-catch جاسازی کردهایم تا مطمئن شویم که اپلیکیشن هنگامی که تابع prepares ناموفق باشد، از کار نمیافتد.
متد OnClickListeners دکمه Stop کاملاً شبیه به آن متدی است که در بخش قبل تعریف کردیم.
در کد فوق بررسی میکنیم که آیا MediaRecorder در حال حاضر و پیش از توقف عملی recording در حال اجرا است یا نه، چون اپلیکیشن در زمانی که متد stop هنگام عدم ضبط صدا فراخوانی شود، ممکن است موجب از کار افتادن برنامه شود. پس از آن متغیر state را به حالت false تغییر میدهیم تا مانع این بشویم که کاربر مجدداً روی دکمه stop بزند.
سپس باید OnClickListener را برای دکمه pause/resume تعریف کنیم.
ما در این دو متد بررسی میکنیم که آیا MediaRecorder در حال اجرا است یا نه. اگر چنین باشد فرایند ضبط صدا را متوقف میکنیم و متن دکمه را به resume تغییر میدهیم. اگر دکمه دوباره کلیک شود فرایند ضبط صدا را از نقطهای که باقی مانده بود از سر میگیریم.
در نهایت ما میتوانیم ضبط صدا را آغاز کرده و با باز کردن فایل recording.mp3 که در حافظه داخلی دستگاه ذخیره شده است به آن گوش دهیم.
در ادامه کد منبع کامل اپلیکیشن ابتدایی ضبط صدا را مشاهده میکنید:
پس از ضبط کردن صدا باید به حافظه داخلی دستگاه اندرویدی خود بروید تا بتوانید به فایل ضبط شده گوش بدهید. اگر در مورد مسیر این کار مطمئن نیستید، مراحل کار را در ادامه توضیح دادهایم:

بدین ترتیب به پایان این راهنما با موضوع اپلیکیشن مقدماتی ضبط صدا در کاتلین رسیدیم. اینک شما با طرز کار MediaRecorder آشنا شدهاید و میدانید که چگونه میتوانید به صورت همزمان تقاضای دسترسی بکنید و چرا این کار مهم است. همچنین نکاتی را در مورد حافظه داخلی دستگاه اندرویدی و شیوه ذخیرهسازی دادهها در آن آموختید. نسخه پیشرفتهتری از این اپلیکیشن که مجهز به امکانات بیشتری مانند پخش صدای ضبط شده با استفاده از MediaPlayer است را میتوانید در این ریپوی گیتهاب (+) مشاهده کنید.