در جلسه اول مقاله به تکمیل برنامه سلام دنیا پرداخته و بعد از مشخص کردن نوع قلم و انتخاب نقشک از نقشکهای سیستمی کد را کامل و منبع کد را از طریق گوگلدرایو برای مشاهده و تست، در فضای ابری فوق برای بارگیری غیر مستقیم، بارگزاری کردم. سپس برای معرفی کلاس دکمه در کیوت «QPushButton» از یک مثال ساده استفاده کردهام؛ دراین مثال، بعد از به کار بردن دکمه در یک پنجره که توسط کلاس «QWidget» ایجاد شده بود؛ شروع به معرفی نحوه مدیریت رویدادها کرده و بعد از آن مثالی از یک برنامه ساده با نوشتن دستورات بیشتر به صورت مرحله به مرحله کامل کردم و کدهای نوشته شده در مثال را به صورت اجمالی نیز تشریح کردم تا درک بیشتری از نحوه نوشتن آن داشته باشید.
در مثال مذکور از یک دکمه و یک پنجره کوچک استفاده شده بود؛ به شکلی که در هنگام کلیک بر روی دکمه خروج، شما از برنامه خارج میشوید. برنامه فوق را میتوان برنامهای ساده به حساب آورد؛ اما برای فهم بیشتر مدیریت رویداد در کیوت و اجرای یک تکه کد با کلیک بر روی دکمه، مثال بسیار مفیدی بود. در مثال مذکور، رویداد کلیک موشی را به تابع خروج از برنامه متصل کرده بودیم که یک تابع بسیار ساده در کیوت است و برای خروج کامل از یک برنامه کیوت کاربرد دارد.
در این جلسه نیز ابتدا شروع به معرفی بیشتر شیء والد «Parent» در کیوت خواهیم کرد که به درک بیشتر این مفهوم و آشنایی با نحوه استفاده از آن در کیوت منجر خواهد شد. معمولا هر یک از اشیاء در کیوت دارای یک والد هستند که در هنگامی که شیء فرزند ایجاد شد تا زمانی که شیء والد زنده و در حال اجرا است، شیء فرزند نیز در حال اجرا خواهد بود. در غیر این صورت و اگر شیء والد بسته و یا کشته شود؛ شیء فرزند نیز متوقف شده و از حافظه خارج خواهد شد.
در مثالی که در جلسه اول ذکر کردیم، دکمهای را در داخل شیء ویدجت «QWidget» قرار دادیم که «QPushButton» فرزند شیء والد «QWidget» محسوب میشود. یک والد همانطور که ذکر شد، یک نگاهدارنده یا یک والد برای اشیاء فرزندی است که به عنوان فرزند برایش در نظر گرفته شدهاند. اگر یک شیء فرزند باشد؛ وابسته به شیء والد خود خواهد بود و در زمانی که شیء والد نابود شود؛ آن شیء نیز نابود خواهد شد. به عنوان نمونه اگر کد زیر را در داخل یک فایل سی++ ذخیره کنید و توسط برنامه محیط توسعه کیوت کامپایل و اجرا کنید؛ شاهد خواهید بود که دکمهای در داخل یک ویدجت دیگر و به صورت فرزند اجرا شدهاست. به صورت کلی در مثال زیر پنجرهای را با کلاس «QWidget» را به صورت والد ساخته و «QPushButton» را به شکل فرزند درون آن قرار دادهایم. در این مثال علاوه بر استفاده از یک دکمه به عنوان شیء فرزند از یک کادر متنی و یک ساختار لایهای افقی نیز استفاده کردهایم. ساختارهای افقی و عمودی یا ساختار جدول مانند «Grid» در کیوت برای مشخص کردن نحوه قرارگیری و نمایش عناصر در کیوت به کار میروند.
در اکثر فرمهایی که در برنامههای بصری با رابط گرافیکی کاربرپسند «GUI» وجود دارند؛ عناصر داخل پنجرهها به اشکال جدولبندی شده و یا به اشکال عمودی و افقی مرتب شدهاند. مثلا در پنجره اصلی نرمافزار آماروک «Amarok» که یک پخش کننده موسیقی برای محیط رومیزی کیدیای است از یک لایه افقی استفاده شدهاست که در آن سه سطر در نظر گرفته شدهاست. همچنین این لایه افتی در داخل یک لایه عمودی دیگر است که منوی بالا و نوار وظیفه در آنان به همراه این لایه افقی قرار گرفتهاند. در داخل هر ستون از لایه افقی نیز یک داک «Dock» قرار گرفته است. شما نیز در برنامه خود با بهره گیری از ساختار لایهای و لایههای تو در تو خواهید توانست ظاهر برنامه خود را به دلخواه تنظیم کنید؛ ویژگی مهم این ساختار لایهای این است که در صورت بزرگنمایی پنجره، عناصر خود را با اندازه جدید پنجره، تطبیق خواهند داد.
شروع ساخت پروژه
در قسمت قبلی از یک پروژه خالی استفاده کردیم؛ سپس با نوشتن مقادیر و مشخصههای خاص داخل فایل پروژه، آن پروژه تنظیم و سفارشی کردیم. ولی اینبار به جای آنکه در هنگام ساخت پروژه از پنجره جادویی و مرحلهای «ویزارد» ساخت پروژه، پروژه خالی را انتخاب کنیم. همانند شکل و از قسمت سمت چپ گزینه «Application» را انتخاب کرده و از لیست سمت راست، گزینه برنامه گرافیکی «Qt Widget Application» را انتخاب میکنیم تا یک پروژه با تمامی تنظیمات پیشفرض مورد نیاز برای ساخت یک برنامه گرافیکی ساده در کیوت برایمان ایجاد شود.
بعد از آن، در بخشهای بعدی نام پروژه به همراه تنظیمات دیگر را تنظیم کنید؛ سپس در بخش آخر به جای انتخاب نوع پنجره که از نوع پنجره اصلی «QMainWinow» است؛ گزینه «QWidget» را انتخاب کنید. حتما بعد از آن تیک کنار ساخت فرم «Generate form» را بردارید؛ تا از ایجاد فرم به صورت گرافیکی و با فرمتی تحت قالب اکسامال جلوگیری شود. سپس نام را از ویدجت به «Button» تغییر دهید.
بعد از اجرای مراحل فوق و ساخت یک پروژه گرافیکی که در آن فایل قالب گرافیکی فرم وجود ندارد؛ برای شما و به به صورت خودکار دو فایل برای کلاس دکمه و یک فایل برای فایل سی++ اصلی «main.cpp» ساخته خواهد شد. همانطور که ذکر کردیم؛ در این برنامه یک ساختار و لایه افقی ایجاد میکنیم؛ تا عناصر را در آن افقی بچینیم. سپس دکمهای را داخل آن لایه قرار داده و لایه را به فرم ویدجت نسبت میدهیم. بعد از آن اگر برنامه اجرا شود؛ دکمه در پنجره نمایش داده خواهد شد. همچنین در داخل شیء مذکور یک کادر ورود متن را نیز که شیء دیگری است را نیز به عنوان فرزند خواهیم افزود. اگر کد زیر را در دخل فایل «button.cpp» بریزید و برنامه را کامپایل و اجرا کنید، با یک دکمه و کادر متنی کنار هم به شکل افقی مواجه خواهید شد. این به دلیل ساختاری است که در کد افزوده شده است. اگر از ساختار لایهای استفاده نکنید، عناصر روی هم قرار میگیرند.
#include "button.h" #include <QPushButton> #include <QHBoxLayout> #include <QTextEdit> Button::Button(QWidget *parent) : QWidget(parent) { this->setWindowTitle("برنامه دکمه"); this->setWindowIcon(QIcon::fromTheme("face-cool")); QPushButton *button1 = new QPushButton("Download", this); QTextEdit *txtEdit = new QTextEdit("Name",this); QHBoxLayout *hl = new QHBoxLayout(this); hl->addWidget(button1); hl->addWidget(txtEdit); this->setLayout(hl); } Button::~Button() { }
اگر برنامه فوق را اجرا کنید؛ خروجی برنامه بالا به شکل زیر خواهد بود که میتوانید برخی تغییرات در ظاهر و اندازه دکمه و کادر متنی ایجاد کنید تا ظاهر زیباتر داشته باشند؛ در هر صورت برنامه به همین شکل فعلا برای ما مناسب است. اگر لایه را عمودی در نظر میگرفتید؛ ساختار به شکل عمودی بوده و عناصر به جای ستونی، به شکل سطری و زیر هم مرتب می شدند. مثلا دکمه در بالا به شکل عریض و نازک قرار گرفته و کادر متنی نیز در زیر آن قرار میگرفت.
در برنامه بالا هیچ مدیریت رخدادی انجام نشده است و برنامه قادر نیست کار خاصی را انجام دهد؛ یا اینکه با کلیک و یا تغییر مقادیر موجود در این برنامه هیچ خروجی و یا تغییرات خاصی انجام نمیشود. بیایید برنامه فوق را به شکلی تغییر دهیم که با نوشتن متنی خاص در کادر سمت راست، بتوان عنوان پنجره را متناسب با کادر نوشته شده تغییر داد. همچنین در رویداد تغییر متن توسط فشردن دکمه و یا ویرایش متن کادر متنی، کدهایی را بنویسیم که در هر زمانی که متن تغییر یافت، عبارت موجود در عنوان پنجره نیز تغییر یابد. برای اینکار باید از طریق سیگنال و اسلات که در جلسه قبلی آموختیم، اشیاء را به برخی توابع متصل کنیم. اگر کد بالا را به کد زیر تغییر دهید؛ این اتفاق رخ خواهد داد. عبارت مورد نظر ما از ترکیب عبارت سلام به انگلیسی «, Hi » و نام نوشته شده در کادر تشکیل خواهد شد. در صورت دلخواه می توانید متن را به فارسی یا به اشکال دیگر نیز بهکار برید.
در کد زیر کلاسها را ابتدا در داخل فایل سرآیند تعریف نمودهایم. بعد از تعریف و استفاده از کلاسها به عنوان یک شیء، اشیاء تعریف شده را در داخل فایل منبع سی++ استفاده کرده و مقادیری را به آنان نسبت خواهیم داد. برای تعریف فایل سرآیند کلاس دکمه، ابتدا فایل سرآیند «button.h» را باز کرده و مقادیر زیر را در آن وارد کنید.
#ifndef BUTTON_H #define BUTTON_H #include <QPushButton> #include <QHBoxLayout> #include <QTextEdit> #include <QWidget> class Button : public QWidget { Q_OBJECT public: Button(QWidget *parent = 0); ~Button(); private: QPushButton *button1; QTextEdit *txtEdit; QHBoxLayout *hl; private slots: void updatetitle(); }; #endif // BUTTON_H
همانطور که مشخص است در فایل فوق کلاسها در سه نوع مختلف عمومی «public»، خصوصی «private» و خصوصی برای اسلات «private slots» مشخص شدهاند. آن کلاسها و یا متغیرهایی که به شکل عمومی تعریف شدهاند در تمامی کدها و خارج از کلاس هم قابل دسترسی هستند؛ اما آن دسته از متغیرها و کلاسهایی که به صورت خصوصی تعریف شدهاند را میتوان فقط در داخل کلاس استفاده کرد. برای تعیین و تعریف تابع و توابعی که به اسلات، نسبت داده میشوند؛ باید از نوع خصوصی اسلات «private slots» باشند تا توسط دستور اتصال «connect» شناسایی شوند. حال بعد از نوشتن سرآیند کلاس دکمه، کدهای اصلی را در کد فایل منبع کلاس و در در فایل سی++ «button.cpp» بنویسید.
#include "button.h" Button::Button(QWidget *parent) : QWidget(parent) { this->setWindowTitle("برنامه دکمه"); this->setWindowIcon(QIcon::fromTheme("face-cool")); button1 = new QPushButton("Update Title", this); txtEdit = new QTextEdit("Name",this); hl = new QHBoxLayout(this); hl->addWidget(button1); hl->addWidget(txtEdit); this->setLayout(hl); connect(button1, SIGNAL (clicked()), SLOT (updatetitle())); connect(txtEdit, SIGNAL (textChanged()), SLOT (updatetitle())); } Button::~Button() { } void Button::updatetitle() { this->setWindowTitle( "Hi, " + txtEdit->toPlainText()); }
سپس اگر دستورات بالا را بعد از نوشتن در فایل مذکور، ذخیره کردید و برنامه را با استفاده از نقشک مثلث شکل و سبز رنگ سمت چپ برنامه محیط توسعه کیوت، کامپایل و اجرا کنید؛ با شکل زیر مواجه خواهید شد. در این برنامه اگر بر روی دکمه «Update Title» کلیک کنید؛ عنوان پنجره به «Hi, Name» تغییر مییابد و سپس اگر نام خود را در کادر متنی سمت راست پنجره وارد کنید؛ نیز بلافاصله عنوان پنجره تغییر خواهد یافت. این کار به دلیل اتصال رویداد تغییر متن در کادر متنی به تابع تغییر عنوان انجام میشود.
نکته؛ میتوانید رویداد چندین شیء را با استفاده از اتصال سیگنال و اسلات به یک تابع متصل کنید. یعنی با نوشتن یک تابع، آن تابع را برای چند رویداد در نظر بگیرید.
در خطوط اول کد برنامه، در فایل منبع از فایل سرآیند استفاده کردهایم و همچنین در خطوط بعدی که به طور پیشفرض توسط خود محیط توسعه کیوت ایجاد شده بود، کلاس دکمه «Button» تعریف و تنظیم شدهاست. بعد از آن یک نقشک به شکل دلخواه به پنجره برنامه نسبت داده و مقداری را برای عنوان برنامه ذکر کردهایم تا در هنگام اجرای برنامه، پنجره بی عنوان نباشد. سپس در دستورات بعدی دکمه و کادر متنی را تنظیم کرده در داخل یک لایه افقی قرار دادهایم تا به صورت افقی چیده شوند. اگر این لایه افقی را از «QHBoxLayout» به لایه عمودی «QVBoxLayout» تغییر دهیم؛ عناصر داخل پنجره به صورت عمودی و زیر هم مرتب خواهند شد. همانطور که در کد میبینید؛ این لایه ساختار افقی دارد. برای این کار در ابتدا لایه مورد نظر (افقی یا عمودی) را تعریف میکنید و نام خاصی را به آنان نسبت میدهید. سپس با استفاده از مشخصه «setLayout» آن را به ویدجت نسبت داده تا پنجره ما با این لایه و ساختار طراحی شود. در کد زیر این عمل انجام شده است. هر فرم میتواند یک لایه داشته باشد؛ برای افزودن لایههای دیگر باید یک لایه مادر در نظر بگیرید و دیگر لایهها را در آن بیفزایید.
this->setLayout(hl);
قبل از نسبت دادن لایه به فرم، باید تمامی اجزاء را یک به یک با مشخصه «addWidget» در لایه اضافه کرده باشید؛ تا قبل از نمایش لایه، عناصر فرم به لایه اضافه شوند. همانطور که ذکر شد؛ ساختار پیچیدهتر را نیز میتوان توسط تعریف لایهها به صورت تو در تو در برنامه و فرمها به کار بست. خطوط زیر از کد برنامه چنین کاری را بعهده داشتهاند.
button1 = new QPushButton("Update Title!", this); txtEdit = new QTextEdit("Name",this); hl = new QHBoxLayout(this); hl->addWidget(button1); hl->addWidget(txtEdit);
کد تابعی که برای بهروز کردن نوار عنوان برنامه و متن آن استفاده شده است؛ بسیار راحت است. در این تابع فقط یک خط کد برای نسبت دادن مقداری با استفاده از مشخصه «setWindowTitle» به کلاس جاری «this» نوشتهایم. در کیوت و سی++ برای اشاره به کلاس جاری از عبارت این «this» استفاده میشود. کد تابع به صورت زیر در برنامه استفاده شدهاست.
void Button::updatetitle() { this->setWindowTitle( "Hi, " + txtEdit->toPlainText()); }
استفاده از «QDebug» برای نمایش پیغام در خط فرمان
در مواقعی که از خطوط زیادی از تابع و کلاسهای مختلفی نوشتهاید و میخواهید اطلاع یابید که در هر یک از توابع و کدهای موجود در برنامه شما چه مقداری را نمایش میدهند یا برنامه دچار چه خطایی شدهاست؛ میتوانید آن مقادیر و یا هشدارها را در خط فرمان نمایش دهید. به عنوان نمونه در هنگام اجرای برنامه پخش چندرسانهای ویالسی «VLC» نیز مشاهده میکنید که اگر برنامه از طریق خط فرمان اجرا شود؛ مقداری خط، هشدار یا پیغام در داخل خط فرمان نمایش داده شدهاست. مانند مثال زیر.
ehsan@ETARCH ~ % vlc VLC media player 2.2.1 Terry Pratchett (Weatherwax) (revision 2.2.1-0-ga425c42) [0000000002102118] core libvlc: Running vlc with the default interface. Use 'cvlc' to use vlc without interface.
در کیوت ایجاد و قرار دادن چنین پیغامهایی در خط فرمان بسیار ساده است و با استفاده از یک کلاس قابل انجام است. برای اینکه برنامه شما نیز چنین پیغامهایی را در خط فرمان نمایش دهد؛ باید از کلاس کیو-دیباگ «qDebug» استفاده کنید. برای استفاده از آن ابتدا باید سرآیند مورد نظر را با افزودن خط زیر به اول کد فایل «button.cpp» وارد منبع کنید.
#include <QDebug>
تابع تغییر عنوان «updatetitle» در زمانهایی که متن ویرایش میشد و یا بر دکمه بهروز کردن عنوان کلید میشد؛ اجرا شده و عنوان را تغییر میداد. حال بیایید تابع نوشته شده را به روز کرده و در آن با استفاده از تابع کیو-دیباگ «QDebug» کد جدیدی را اضافه کنیم. در کد جدید پیام دلخواه در زمان اجرای برنامه، در خط فرمان نوشته خواهد شد. به طور کلی کاربرد این کد در مواقع مختلفی است که نیاز به مشاهده برخی پیامها به صورت مخفی دارید. مثلا بانک اطلاعاتی به خوبی متصل شده است؛ برای نمایش آن اگر در خط فرمان عبارت «Connected» نوشته شود؛ متوجه اتصال به بانک اطلاعاتی خواهید شد. در غیر این صورت پیغامی جهت عدم اتصال یا هشداری خاص نمایش داده خواهد شد. برای اینکار خطوط زیر را در تابع فوق بیفزایید تا پیامی دلخواه در هر بار تغییر عنوان نمایش داده شود.
void Button::updatetitle() { this->setWindowTitle( "Hi, " + txtEdit->toPlainText()); qDebug("Title! Updated!"); }
بعد از اجرای دستور فوق هرگاه تابع فوق اجرا شود و متن عنوان پنجره تغییر پیدا کند؛ متن دلخواه یعنی «Title Updated» در خروجی خط فرمان نمایش داده خواهد شد. در برنامه محیط توسعه کیوت در قسمت پایین برنامه قسمت جدید باز خواهد شد و پیغامها به رنگ قرمز نمایش داده خواهند شد. در تصویر زیر پیام نوشته شده به رنگ قرمز در قسمت زیرین برنامه و بخش خروجی برنامه «Application Output» کاملا واضح است.
اگر به جای دستور فوق از دستورات استاندارد سی++ برای نمایش خروجی در خط فرمان استفاده کنیم چه اتفاقی خواهد افتاد؟ برای فهم این موضوع از دستور استاندارد در سی++ یعنی سیاوت «std::cout» در تابع فوق استفاده میکنیم. قبل از آن باید از کلاس «iostream» استفاده کرد. ابتدا خط زیر را به اول فایل بیفزایید تا سرآیند مربوطه وارد برنامه شود.
#include <iostream>
سپس دستورات زییر را در تابع مذکور زیر خط دستور کیو-دیباگ بنویسید.
std::cout << "C++ Standard Output" << std::endl;
اگر کد فوق را نیز اجرا کنید؛ عبارت نوشته شده در جلو عبارت سیاوت «cout» در خروجی برنامه برای شما نمایش داده خواهد شد. اما این بار عبارت نمایش داده شده در خروجی داخل برنامه محیط توسعه کیوت به رنگ قرمز مشخص نیست؛ ویژگی بارز کیو-دیباگ نسبت به دیگر روشهای نوشتن خروجی در دستهبندی و نحوه نمایش بهتر خطاها در نرمافزار محیط توسعه کیوت یا حتی خط فرمان است که در آینده بیشتر به آن موارد خواهیم رسید. اما موردی که در این جلسه استفاده کردیم؛ فقط اختصاص به نمایش پیغام داشته است که همین پیغام به شکل دیگر توسط دستورات استاندارد موجود در زبان سی++ بدون کویت نیز قابل نمایش است.
به طور کلی این کد را برای این نوشتم تا نکته مهمی را متذکر شوم؛ در کیوت نیز میتوان کنار دستورات و کلاسهای کیوت، دستورات سی++ عادی را به راحتی نوشته و اجرا کنید. اما پیشنهاد میشود همواره از دستورات کیوت بهره گیرید. دستورات کیوت برای انحصار طلبی یا اختراع مجدد چرخ ایجاد نشدهاند؛ بلکه برای یهود موارد فعلی در سی++ ایجاد شدهاند و انعطاف بیشتری نسبت به دستورات سی++ ساده دارند. در آخر اگر کد را با تغییرات نوشته شده در بالا اجرا کنید؛ خروجی به صورت زیر خواهد بود. عبارات نوشته شده در کدهای بالا که با دستورات سی++ نوشته شده بودند نیز در پایین برنامه کنار عبارات نمایش داده شده توسط کیو-دیباگ به رنگ سفید نمایش داده میشود. در خروجی برنامه در نرمافزار محیط توسعه کیوت، تفاوت در رنگ عبارات نمایش داده شده مشابه شکل زیر، کاملا مشهود است.
جمعبندی نکات مطرح شده در این جلسه؛ در مثال ذکر شده در این جلسه که تا انتهای مطلب موارد جدیدی را به آن افزودیم؛ از نحوه استفاده از سیگنال و اسلات به شکل دیگر و اجرای تابع خاص در هنگام رخدادن رویداد خاص کمی آشنا شدید. همچنین به ساخت یک پروژه جدید گرافیکی بدون استفاده از فایل فرم با قالب اکسامال اقدام کردیم و کدهای خود را درون آن نوشتیم بدون اینکه فایل پروژه که در قسمت قبل مجبور به تنظیمش شدیم؛ را تغییر دهیم.
با وجود استفاده کیوت از زبان سی++ و نوشته شدن تمامی توابع آن در این زبان، اما شما برای نوشتن کد در کیوت باید با دستورات و کلاسهای اختصاصی کیوت که در کتابخانهها و سرآیندهای آن است آشنا شوید. نوشتن کد به زبان سی++ به تنهایی نمیتواند برای یادگیری کیوت مفید باشد اما مسلط بودن شما به زبان سی++ بیش از ۵۰ درصد از یادگیری شما را در بر گرفته و به هر میزان که دانش بیشتر از زبان سی++ داشته باشد؛ توانایی نوشتن کد در کیوت نیز بالاتر خواهد رفت. مفاهیمی مانند کلاس، تابع، استفاده از متغیرها و … همگی مانند زبان سی++ هستند، بنابراین برای یادگیری کیوت باید ابتدا آشنایی با زبان سی++ داشته باشید تا بتوانید کدهای بالا را درک کنید. البته در تمامی مثالهای گفته شده توضیحاتی جهت یادآوری مفاهیم در زبان سی++ داده شدهاست؛ مثلا در جلسه قبل توضیحات مفصلی درباره کلاسها نوشتم که برای یادآوری مفاهیم مذکور نوشته شده بودند.
همانطور که قول داده بودم کد تمامی مثالهای استفاده شده در هر جلسه آموزشی را در پایان هر جلسه قرار خواهم داد. کد مثال فعلی را نیز به صورت فایل آرشیو فشرده و با قالب زیپ «zip.» در گوگلدرایو بارگزاری کردهام که از این پیوند و از طریق گوگل درایو به صورت غیر مستقیم قابل بارگیری و دریافت است.