در نوشته های پیشین ننشان دادیم که به کمک کلاس PhotoImage می توانیم تصویری را بر چسبانده شدن بر روی یک ویجت مانند Label, Button یا Canvas آماده کنیم. در این نوشته انگیزه آن است که به کمک ماژل ImageTk از ماژول Pillow یک تصویر خوانده شده به کمک متد ()open از ماژول Pillow را برای نمایش بر روی ویجت های Tkinter آماده کنیم.

کلاس PhotoImage از ماژول ImageTk در Pillow

برای آنکه تصویری را به کمک Pillow بخوانیم و سپس آن را بر روی برچسبی از Tkinter نمایش دهیم، باید متد ()open از کلاس Image را همانند پیش به کار ببریم ولی به جای فرستادن تصویر خوانده شده به کلاس PhotoImage از Tkinter، این تصویر خوانده شده باید به کلاس PhotoImage از ماژول ImageTk فرستاده شود. کد زیر نمونه ای را نشان می دهد که در آن در تابع سازنده کلاس PhotoImage نخست تصویر با متد ()Image.open خوانده شده است و سپس به کمک متد سازنده ()ImageTk.PhotoImage آن تصویر برای نمایش بر روی ویجت های Tkinter آماده شده است که از این پس این تصویر به کمک متغیری به نام image1 در دسترس است. در پایان متغیر image1 به پارامتر image در تابع سازنده کلاس Label فرستاده شده است.

ساخت نمایشگر تصویر با Tkinter و Pillow

در یک برنامه ساده می خواهیم چندین تصویر (برای نمونه چهار تصویر از پیش و دستی آماده شده) را در یک برچسب نمایش دهیم. برای پیمایش میان دو کلید برای Backward و Forward و یک کلید دیگر به نام Exit برای بستن برنامه داریم. همچنین یک برچسب داریم که می خواهیم تصویر روی آن نشان داده شوند. بنابراین چهار ویجت زیر را خواهیم داشت.

  • متغیر imageLabel که از کلاس Label است و قرار است تا تصویر روی آن نشان داده شود. این برچسب به کمک متد ()grid در سطر و ستون شماره صفر قرار می گیرد و به اندازه سه ستون گسترش می یابد. بنابراین به ترتیب پارامترهای row و column برابر با مقدار صفر و متغیر دیگری به نام columnspan برابر با مقدار ۳ را دریافت می کنند.
  • متغیر button_exit از کلاس Button است و مقدار پارامتر text آن رشته >> و مقدار پارامتر command آن همان تابع نوشته شده به نام ()back است. این دگمه تصویر پیشین (قبلی) را نشان می دهد.
  • متغیر button_exit از کلاس Button است و مقدار پارامتر text آن رشته Exit و مقدار پارامتر command آن همان تابع ()main_window.destroy است. توجه کنید که برای بستن یک پنجره، برابر با آموزش های پیشین باید متد ()destroy را از طریق شی یا نمونه کلاس Tk فراخوانی کنیم که در این مثال این متغیر main_window نام دارد. همچنین به کمک متد ()grid این ویجت در سطر شماره ۱ row=1 و ستون شماره ۲ column=2 قرار می گیرد.
  • متغیر button_forward از کلاس Button است و مقدار پارامتر text آن رشته << و مقدار پارامتر command آن همان تابع نوشته شده به نام ()forward است. این دگمه تصویر پسین (بعدی) را نشان می دهد.

کد ساخت برچسب imageLabel

در پایان نوشته همه کد این پروژه ساده آورده شده است ولی در هر بخش تکه تکه بخش های گوناگون کد را آموزش داده ایم. در اینجا کدهای مربوط به Label نمایش دهنده تصویر آورده شده است که پیش از این در نوشته ها آن را آموزش داده بودیم. در کدهای زیر چهار تصویر در چهار متغیر جداگانه را به کمک متد ()Imgae.open خوانده ایم و سپس آنها را به کمک کلاس PhotoImage از ماژول ImageTk برای نمایش در Tkinter آماده کرده ایم. سپس یک لیست به نام images از نام این چهار متغیر ساخته ایم. در پایان یک Label ساخته شده که مقدار پارامتر image آن برابر با نام متغیر image1 است که همان نخستین تصویری است که خوانده بودیم. چرایی این کار برای آن است که پس از باز شدن برنامه، یکی از تصویر ها خودکار نمایش داده شود.

کد ساخت سه دگمه

در کدهای زیر سه Button ساخته شده اند که در دگمه button_exit مقدار main_window.destroy به پارامتر command فرستاده شده است و کاربرد این دگمه (Exit) بستن برنامه است. مقدار پارامتر command برای دو دگمه دیگر تابع های forward و back هستند که آنها را ادامه خواهیم نوشت. در پایان نیز تابع ()mainloop برای آغاز Event Loop فراخوانی شده است.

تابع forward

این تابع کارکرد دگمه forwrad را انجام می دهد. یک ورودی دریافت کرده که شماره یا اندیس تصویری از لیست images است که می خواهیم نمایش دهیم. در سه خط آغازین، سه متغیر global (سراسری) ساخته می شوند که در تمام برنامه و در تابع back نیز شناخته شده هستند. این سه متغیر به نام imageLabel برای ویجت برچسب نمایش عکس، button_back برای دگمه Backward و button_forward برایدگمه Forward را میزبانی می کنند. چرایی، این است که می خواهیم درون این تابع، پس از هر کلیک می خواهیم دو دگمه پیش فرض از نو ساخته یا فراخوانی دوباره شوند. بنابراین سه متغیر سراسری همنام با سه ویجت پیشتر گفته شده ساخته ایم تا همگی به ویجت های از پیش ساخته شده خودشان اشاره کنند.

متد ()grid_forget از سامانه گرید در Tkinter

پیش از این درباره ساخت گرید و سازماندهی و چیدمان ویجت ها گفتیم. در این نوشته می خواهیم دو جنبه دیگر از گرید را بگوییم. نخست پارامتر columnspan است که در ساخت برچسب imageLabel آن را با شماره ۳ مقدار دهی شده است. معنی این پارامتر این است که ویجت مربوطه را به اندازه ۳ تا ستون گسترش بده یا به گفته دیگر، ویجت مربوطه ۳ تا ستون را برای خودش بر می دارد. با توجه به اینکه سه دگمه دیگر در سطر شماره ۱ و به ترتیب در ستون های صفر و ۱ و ۲ هستند، پس ویجت بالایی آنها که همان برچسب است، سه ستون را اشغال می کند تا همسان با دگمه های زیرین، گسترش یابد.

در کدهای زیر که بخشی از تابع ()forward و همچنین تابع ()back هستند، به کمک متد ()grid_forget، ویجت مربوطه که در اینجا برچسب imageLabel است، از سامانه گرید بیرون انداخته می شود. توجه کنید متد ()grid_forget ویجت را از میان نمی برد بلکه تنها آن ویجت دیگر نمایش داده نمی شود ولی می تواند دوباره آن را در ویجت نمایش داد. دومین خط کد زیر همین کار را انجام می دهد، بدین معنی که پس از پاک کردن ویجت برچسب imageLabel از گرید، دوباره یک برچسب همنام ساخته شده و به همان شیوه پیشین به گرید افزوده می شود، پس از این رو همان ویجت از گرید پاک شده، از نو نمایش داده می شود.

دریافت ورودی در تابع پارامتر command در دگمه ها

پیش از گفتیم که در آغاز و بیرون تابع های ()forward و ()back که برچسب imageLabel ساخته شده بود، یکی از تصویرهای (متغیرهای) ساخته شده را به برچسب داده ایم تا در آغاز کار نمایش داده شود. همچنین گفتیم که تابع ()forward یک ورودی که شماره اندیسی در لیست images است را دریافت می کند که همان تصویر نشان داده شود. 

در کد بالا و هر زمان که دگمه Forward کلیک شود، شماره ۲ به تابع ()forward فرستاده می شود که سپس نخست به خط زیر در تابع ()forward فرستاده می شود. خط زیر بدان معنی است که پس از نمایش دوباره برچسب، تصویر با اندیس شماره ۱ – ۲ یا همان تصویر شماره ۱ را نمایش بده که همان تصویر پسین (پس از نخستین تصویر آغازین) است.

دو خط زیر نیز در هر دو تابع هستند و دلیل آن این است که انگار می خواهیم خود دو تابع در درون خودشان از نو فراخوانی کنیم ولی چه زمانی فراخوانی می کنیم؟ زمانی که دگمه مربوطه کلیک شود. ولی چگونه در خود تابع کلیک شدن دگمه مربوطه را پیاده سازی کنیم؟ برای این، دوباره خود دگمه را از نو می سازیم. توجه کنید انگیزه از دگمه Forward نمایش تصویر پسین است، پس شماره اندیس با image_number + 1، یکی افزایش پیدا می کند.

غیر فعال کردن دگمه

توجه کنید ما در اینجا تنها ۴ تصویر داریم و هر بار که Forward را کلیک می کنیم، یک تصویر پسین نمایش داده می شود و پس از نمایش این چهار تصویر، چون دیگر هیچ چیزی برای نمایش نیست، پس یک پنجره کوچک تهی نمایش داده می شود. پس در تابع ()forward چون با هر کیلک یکی به شماره اندیس ورودی افزوده می شود، پس بررسی می شود که اگر اندیس کنونی برابر با اندازه لیست imags بود، پس این واپسین تصویر بوده و به کمک مقدار DISABLE برای پارامتر state در تابع سازنده کلاس Button، آن دگمه غیر فعال شود.

همین نیز برای دگمه Backward هست و زمانی که نخستین تصویر نمایش داده شد، پس این دگمه نیز باید غیر فعال شود. همچنین اگر به کدهای تعریف سه دگمه و برای button_back نگاه کنید، می بینید که در آغاز چون پارامتر state برای button_back دارای مقدار DISABLE است، پس این دگمه در آغاز غیر فعال بوده است.

ساختار تابع ()back نیز همین گونه است و دیگر آن را بررسی نکرده ایم ولی می توانید آن را از کد کامل ببینید. توجه کنید که برای انجام پروژه شما نیاز دارید به طور پیش فرض در محلی که اسکریپت پایتون است یک دایرکتوری به نام images بسازید و سپس چهار تصویر با نام های photoX.png در آن داشته باشید که X شماره های ۱ تا ۴ است.