یک راه ساده برای برنامه نویسی Multi-Thread با کیوت

سلام دوستان.

حتما میدونید که کیوت برای برنامه نویسی چند نخی و Concurrent کلاس های زیادی داره. ان شاء الله در آینده بیشتر باهاشون آشنا میشیم. ولی امروز قصد داریم یه راه خیلی ساده و البته موثر رو برای کارهایی که ممکنه زمان بر باشه بگیم.

 Multithread Programming

توی این پست قصد نداریم مقدمات برنامه نویسی چند نخی بگیم که اگه بخوایم بگیم شاید ساعت ها باید از سیستم عامل و نحوه کارکردش حرف بزنیم. فقط بدونیم که thread اساسا در سیستم عامل کوچک ترین واحد پردازشی است که CPU در اختیارش قرار میگیره تا دستوراتش اجرا بشه. در واقع یه برنامه تشکیل شده از یک یا تعداد بیشتری ترد . در برنامه های ما چه بخوایم چه نخوایم یه ترد رو حتما داریم که معمولا با نام GUI-Thread شناخته میشه. پس پیشفرض این آموزش اینه که شما یه درک کلی از این مفهوم دارین.

شاید بارها براتون پیش اومده که یه کار زمان گیری دارید انجام میدید و مثلا دیگه دکمه ها و در کل واسط کاربری شما پاسخگو و Responsive نیستند. مثلا فرض کنید میخوایم یه برنامه بنویسیم که یه Image Viewer خیلی ساده باشه. یه فرم به صورت زیر طراحی کنید.

wind2

قراره به کاربر اجازه بدیم یه پوشه رو انتخاب کنه و بعد تصاویر داخل اون پوشه رو داخل QWidget مرکزی که داخل یک QScrollArea قرار داره ( برای داشتن اسکرول ) نمایش بده. برای نمایش تصاویر از QLabel استفاده میکنیم. پس قراره کاربر یه تعداد تصویر انتخاب کنه و ما هم یه شبکه ای از QLabel ها درست کنیم و اونا رو به عنوان فرزند روی QWidget قرار بدیم. چون خود QWidget فرزند QScrollArea هستش باعث میشه که اگه ارتفاع QWidget زیاد تر از حد مجاز شد اسکرول بار ها ظاهر بشن. خب بذارید اسلات دکمه Open رو ببینیم :

خب این تابع خیلی ساده است.  با استفاده از کلاس QFileDialog و تابع استاتیک getExistingDirectory به کاربر اجازه میدیم یه پوشه انتخاب کنه و اگه انتخابش تهی نبود تابع processDirectory رو صدا میزنیم.

برای ویجت ابتدا یک QGridLayout میسازیم. زیرا قراره یه سری لیبل به صورت شبکه ای بسازیم و داخل این layout قرار بدیم. فعلا فرض ما بر اینه که چهارتا ستون بیشتر نداریم و تعداد سطرها زیاد تر میشن ( میشه از روی سایز ویجت یه مقدار معقول برای cols به دست آورد )

خب ما یه دایرکتوری داریم که باید لیست تصاویر داخلش رو بدست بیاریم. برای این کار از کلاس QDir استفاده میکنیم. این کلاس که برای کار با Directory ها در کیوت تعبیه شده توابع جالبی داره از جمله تابع entryInfoList . این تابع لیستی از مشخصات فایلهای داخل دایرکتوری رو میده. کدوم دایرکتوری؟ همون که به سازنده QDir ارسال کردیم . همون که کاربر انتخاب کرده. ( البته این تابع لیست زیر پوشه های داخل یه پوشه رو هم میده ولی ما توی پارامتر این تابع گفتیم فقط فایل ها رو میخوایم ) در ضمن با استفاده از خط

 

گفتیم که آقا ما فقط فایل هایی با پسوند jpg , jpeg میخوایم ولاغیر!!

خب ما الان لیستی از مشخصات فایل های تصویری داخل دایرکتوری داریم که از جنس QFileInfo هستند . برای گشت و گذار داخل یه لیست میشه از کلاس تمپلیت QListIterato استفاده کرد. هر لیستی !

پس ما الان میتونیم با شی itr به کلیه عناصر داخل این لیست دسترسی پیدا کنیم. ( راه راحت ترش یه حلقه for ساده بود ) .  این کلاس QListIterator یه تابع داره به نام hasNext که تا زمانی که در لیست عنصری بعد از عنصر فعلی باشه مقدار true بر میگردونه . تابع next این کلاس هم عنصر جاری از لیست رو بر میگردونه. و در ضمن اشاره گر به اول لیست رو یکی جلو میبره. مثلا اگه در لیست شما عدد ۱ و ۲ و ۳ باشن تابع hasNext اول کار true برمیگردونه تابع next مقدار ۱ رو به ما میده و در ضمن میره سراغ ۲ . و این داستان ادامه داره. یعنی در حلقه بازم hasNext مقدار true بر میگردونه و next مقدار ۲ …

خب ما به ازای هر فایل یه QLabel میسازیم و براش تصویر ست میکنیم. با استفاده از تابع setPixmap . با استفاده از تابع absoluteFilePath عضو QFileInfo میتونیم آدرس مطلق تصویر رو بدست بیاریم مثل c:/aaa/bbb.jpg . بعد برای اینکه تصویرامون مرتب نمایش داده بشن تصاویر رو با استفاده از تابع scaled عضو کلاس QPixmap ریسایز میکنیم. تا همه ۱۰۰*۱۰۰ پیکسل باشن.

خب الان کافیه برنامه رو اجرا کنیم و یه پوشه به برنامه بدیم. سعی کنید یکم تعداد عکسا توی پوشه زیاد باشه و ببینید که دیگه برنامه جواب گو نیست تا چند لحظه.

wind3خب حالا چی کار کنیم پس ؟ یه کلاس به صورت زیر طراحی کنید:

یه کلاس از جنس QObject . که یه اسلات داره به نام resize و یه سیگنال به نام imageReady . میخوایم یه شی ( یا بیشتر ) از این کلاس بسازیم تصویرمون رو با تابع resize بهش بدیم و بعد بهش بگیم آقا ریسایزش کن و تموم که شد با imageReady خبر بده قربون دستت 🙂 اون پارامتر های row , col هم برای اینه که بدونیم این تصویر مال کدوم یکی از QLabel هاست تا براش ست کنیم.

اون پارامتر Qt::SmoothTransformation هم باعث میشه تصویر ریسایز شده کیفیت بهتری داشته باشه ( کاری که قبلا به دلیل محدودیت در سرعت انجام ندادیم )

خب حالا چی کار کنیم با این کلاس ؟! بذارید یه اشاره گر بهش توی MainWindow مون داشته باشیم .

دقت کنید که هم عضوی به نام _scaler اضافه شد و هم اسلاتی که تصویر تولید شده رو به QLabel مورد نظر نسبت بده و البته یه سیگنالی به نام resizeImage که قراره امیت بشه تا به ترد ما خبر بده تصویر جدیدی رو باید ریسایز کنه ( شاید یکی بپرسه خب صدا زدن resize از Scaler مگه نمیشه؟! امتحان کنید ببینید چه فرقی داره)

خب توی سازنده این کارا رو انجام میدیم :

اینجاست که اصن اصل ماجرا اتفاق می افته !  خیلی ساده یه شی از کلاس Scaler ساختیم :

و البته یه شی از کلاس QThread :

کلاس QThread برای مدیریت Thread ها به صورت Low Level هستش که یکم کار باهاش دردسر داره ( باید ازش ارث ببرید و تابع run رو ازش باز نویسی کنید ) ولی نگران نباشید . راه راحت تری هم هست . وقتی یه شی از کلاس Scaler ( یا هر کلاسی از جنس QObject ) میسازیم میتونیم بگیم که آقا اصن برو توی یه ترد دیگه اجرا بشو و مزاحم ما نشو ( واقعا همینه ها ) این کار با استفاده از تابع moveToThread انجام میشه. پس الان عملا شی از کلاس Scaler توی یه ترد دیگه منتظر ماست تا ما با سیگنال resizeImage بهش خبر بدیم که آقا یه تصویر دیگه هم هست باید ریسایز کنی و اونم شروع به کارش بکنه. وقتی که scaler کارش رو توی یه ترد دیگه انجام داد خبرش رو به ما با سیگنال imageReady میده و ما هم با استفاده از اسلاتی با  همین نام به هدفمون میرسیم.

خب اسلات imageReady خیلی ساده است :

ولی تابع processDirectory هم نیاز به تغییر داره :

البته تغییر خاصی نیست فقط به جای ست کردن پیکمپ واسه لیبل سیگنال resizeImage رو امیت میکنیم همین. البته من برای اینکه کار زیبا تر باشه برای Label ها با استفاده از QMovie یه لودینگ به صورت فایل گیف ست میکنم .

اینم خروجی نهایی کار : ( بذارید کامل لود بشه ! نگید چرا این هی وسطش میمیره!  در ضمن الکی از عکسهای سمپل ویندوز کپی گرفتم تا زیاد باشن. حالش رو نداشتم هارد رو بیارم و عکس های اون رو لود کنم!  بابا ساعت سه نصم شبه)

final_thread

خب با اعلام ساعت ۲:۵۷ دقیقه شب خدمت شما بینندگان عزیز عرض میکنم که دیگه اگه مشکل ریزی توی کار بود خودتون درستش کنید ! عمرا کسی الان بیدار باشه !

کد نهایی رو هم میتونید از اینجا دانلود کنید. simple_thread

۲۵,۹۷۵ total views, 1 views today

Print Friendly, PDF & Email

درباره ی سعید دادخواه

یه برنامه نویس !

همچنین ببینید

اجرای تنها یک نمونه از برنامه (Single Instance)

درود. چند روز پیش در بخش سوال و جواب یکی از دوستان به نام tmtt …

27
دیدگاه بگذارید

avatar
15 Comment threads
12 Thread replies
0 دنبال کنندگان
 
Most reacted comment
داغ ترین نخ نظرات
10 کامنت گذاران
دانشجوی مهاجرmn_nahviمرتضیسید امیرaidin کامنت گذاران اخیر
  مشترک شو!  
جدیدترین قدیمی‌ترین دارای بیشترین امتیاز
میخوام باخبر شم از
حامد
Guest
حامد

خیلی عالی بود مچکرم.

علیرضا
Guest
علیرضا

الآن اگه یه خارجی این مطلبو میخوند میگفت
wowwww wonderful
حالا با املاش کاری ندارم 😀 منوظرش همون بوده ! بازم مثل همیشه عالی بود ممنون از زحماتتون
در ضمن منتظرم بهمن بشه برای qml

HadiAbbasi
عضو

جناب دادخواه…واقعا ممنون که تو این وضعیتی که کنکوری هستید ،این زحماتو واسه بچه ها می کشید….

علی رضا پژوهش
Member
علی رضا پژوهش

حالا هی میگن چرا گیر می‌دی http://qtips.ir/wp-content/plugins/wp-monalisa/icons/wpml_whistle3.gif
سعید جان برادر چشممون خشک شد به انتظار
آموزش‌های قبلی رو ادامه نمی‌دی‌؟ 
http://qtips.ir/wp-content/plugins/wp-monalisa/icons/wpml_heart.gif
 

HadiAbbasi
عضو

ظاهرا آقا سعید دارن خودشونو برا کنکور ارشد آماده می کنن و بعد از اوایل یا اواسط بهمن حضورشونو گرمتر حس می کنیم انشاءال…

aidin
Guest
aidin

تشکر فراوان آقا سعید 🙂

سید امیر
Guest
سید امیر

بسیار عالی بود
 
فقط این ترد ها سطح یوزر هستند یا سطح کرنل؟ یعنی یکیش بلاک بشه بقیه بلاک میشن یا نه؟

HadiAbbasi
عضو

سعید جان تصاویر پریده…یه پیشنهاد…
اگه یه آپلود سنتر داشته باشید که فایل ها توش بمونه بهتر نیست؟
دیگه اسناد و فایل های داخل سایت ، محفوظ می مونه…البته تو قسمت متا هم بیان می کنم…

علیرضا
Guest
علیرضا

وبلاگ در سکوتو آرامش ….
داره خوابم میبره 🙁

HadiAbbasi
عضو

آقا سعید مشغول خوندن درس برا ارشد هستند…از نیمه های بهمن انشاءال… بر میگردند…الانم فصل امتحانات دوستان دانشجو و … هست…

علیرضا
Guest
علیرضا

این وبلاگ هم به تاریخ پیوست ……
بریم فیلماشو تا نپریده دانلود کنیم

علی رضا پژوهش
Member
علی رضا پژوهش

سعید چی کار کردی؟
صنعتی بودی؟

علی رضا پژوهش
Member
علی رضا پژوهش

خدارو شکر
مگه نجف آباد هم بود؟

علی رضا پژوهش
Member
علی رضا پژوهش

راستی این وردپرست هم هی میگه به سعید بگو منو آپدیت کنه 🙂

aidin
Guest
aidin

آقا سعید باور کن از الان بخوای واسه کنکور دکترا آماده بشی زوده هاااااا 😀
کجاین پس ؟ 🙂

مرتضی
Guest
مرتضی

سلام من این راه رو برای برنامه ی خودم امتحان کردم  و نشد! یعنی همین روالی رو که شما گفته بودید رو پیاده کردم ولی هنوز موقع انجام محاسبات برنامه، gui پاسخگو نیست. شاید هم اشکال در کد من باشد نمی دانم، اما چون در کیوت کلا دو نوع نخ داریم، main_thread و worker_thread، که gui فقط در نخ اولی (اصلی) اجرا می شود و نمی تواند در نخ کارگر باشد. به نظر من چون ما یک کلاس محاسبه کننده داریم که آن را در کلاس gui تعریف کرده ایم، بالاخره کلاس gui آن را کنترل می کند و منطقی… Read more »

mn_nahvi
Member

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

دانشجوی مهاجر
Guest
دانشجوی مهاجر

آقا سعید خیلی خیلی عالی بود.