کتابخانه Tkinter، ابزار استاندارد پایتون برای ساخت رابط های گرافیکی کاربر (GUI) است و توسعه رابط های گرافیکی را برای برنامه نویسان بسیار ساده میکند. این کتابخانه بهصورت چندسکویی (cross-platform) طراحی شده، بهطوریکه برنامه های ساختهشده با آن در سیستم عامل های مختلف مانند ویندوز، مک او اس و لینوکس ظاهر و عملکردی مشابه دارند.
آموزش ساخت رابط گرافیکی در پایتون با Tkinter
با اینکه گاهی از ظاهر قدیمی Tkinter انتقاد میشود، اما هنوز هم گزینهای کاربردی و سریع برای ساخت برنامه های گرافیکی ساده و قابل اجرا در چند پلتفرم محسوب میشود.
در پایان این آموزش، با مفاهیم زیر آشنا خواهید شد:
- GUI یا رابط کاربری گرافیکی چیست و چگونه با استفاده از Tkinter در پایتون ساخته میشود.
- Tkinter معمولاً همراه با اکثر نسخه های پایتون نصب میشود، بنابراین در بیشتر مواقع نیازی به نصب جداگانه ندارد.
- با وجود ابزارهای جدیدتر، Tkinter همچنان گزینهای مناسب برای ساخت برنامه های ساده و چندسکویی است.
- برای چیدمان عناصر در رابط کاربری Tkinter میتوان از مدیرهای چیدمان مانند .pack()، .place() و .grid() استفاده کرد.
- برای ساخت برنامه های تعاملی در Tkinter، میتوان رویدادهایی مثل کلیک روی دکمه را به توابع پایتونی متصل کرد.
در این آموزش، شما ابتدا با نحوه شروع کار با Tkinter آشنا میشوید، سپس یاد میگیرید چگونه ویجت ها را مدیریت کرده و برنامه های تعاملی بسازید. در انتهای هر بخش، تمرینهایی برای تثبیت مفاهیم ارائه شده که به شما کمک میکند مهارتهای خود را در عمل محک بزنید.
در نهایت، این آموزش با ساخت دو پروژه کاربردی جمعبندی میشود:
- مبدل دما
- ویرایشگر متن
پس وقت آن رسیده که شروع کنیم و گامبهگام ساخت یک برنامه گرافیکی با Tkinter را یاد بگیریم!
ساخت اولین رابط کاربری گرافیکی با پایتون و Tkinter
پایهایترین عنصر در یک رابط کاربری گرافیکی (GUI) با Tkinter، پنجره (Window) است. پنجره ها در واقع ظروف اصلی هستند که تمام عناصر دیگر رابط گرافیکی درون آنها قرار میگیرند. این عناصر مانند کادر متن (Text Box)، برچسب (Label) و دکمه (Button)، در دنیای Tkinter به عنوان ویجت (Widget) شناخته میشوند. هر ویجت باید درون یک پنجره قرار داشته باشد.
در این بخش، ابتدا یک پنجره ساده ایجاد میکنیم که تنها شامل یک ویجت است. یک محیط شِل پایتون (Python shell) را باز کرده و قدمبهقدم با ما همراه شوید!
نکته: تمام مثالهای کد در این آموزش روی سیستم عامل های ویندوز، مکاواس و اوبونتو لینوکس نسخه ۲۰.۰۴ با پایتون نسخه ۳.۱۰ تست شدهاند.
اگر پایتون را از طریق نصبکننده رسمی python.org برای ویندوز یا مک نصب کردهاید، بهراحتی میتوانید کدهای این آموزش را اجرا کنید و نیازی به خواندن ادامه این بخش نیست. مستقیماً وارد آموزش شوید!
نصب Tkinter در برخی سیستم عامل ها:
مک او اس (با Homebrew):
نسخه پایتون که از طریق Homebrew نصب میشود، معمولاً بهصورت پیشفرض کتابخانه موردنیاز Tkinter یعنی Tcl/Tk را ندارد و از نسخه قدیمیِ موجود در سیستم استفاده میکند. این نسخه ممکن است باعث شود نتوانید ماژول Tkinter را وارد کنید. برای جلوگیری از این مشکل، بهتر است پایتون را با نصبکننده رسمی مک نصب کنید.
اوبونتو لینوکس ۲۰.۰۴:
برای صرفهجویی در فضا، نسخه پیشفرض پایتون که همراه اوبونتو نصب میشود، از Tkinter پشتیبانی نمیکند. اگر میخواهید از همین نسخه پایتون استفاده کنید، کافی است پکیج زیر را نصب نمایید:
1 |
$ sudo apt-get install python3-tk |
این دستور، ماژول Tkinter را به سیستم شما اضافه میکند.
توزیعهای دیگر لینوکس:
اگر در نصب نسخه سازگار پایتون روی توزیع خاصی از لینوکس مشکل دارید، میتوانید پایتون را از کد منبع (source code) به همراه نسخه مناسب Tcl/Tk کامپایل و نصب کنید. همچنین ابزار pyenv گزینه خوبی برای مدیریت نسخه های مختلف پایتون است.
ایجاد اولین پنجره در Tkinter
بعد از اینکه شِل پایتون را باز کردید، اولین کاری که باید انجام دهید، وارد کردن ماژول Tkinter است:
1 |
>>> import tkinter as tk |
برای ایجاد یک پنجره جدید، باید نمونه ای از کلاس Tk بسازید و آن را در یک متغیر ذخیره کنید:
1 |
>>> window = tk.Tk() |
با اجرای این کد، یک پنجره جدید روی صفحه شما ظاهر میشود. ظاهر این پنجره ممکن است بسته به سیستم عامل شما متفاوت باشد. این پنجره همان محیطی است که در ادامه ویجت ها را در آن قرار خواهیم داد.
در ادامه این آموزش، تصاویر ارائهشده مربوط به سیستمعامل ویندوز خواهند بود.
افزودن ویجت به پنجره در Tkinter
حالا که یک پنجره ایجاد کردهاید، وقت آن است که یک ویجت (Widget) به آن اضافه کنید. برای اضافه کردن متن به پنجره، میتوانید از کلاس tk.Label استفاده کنید. برای مثال، یک ویجت Label بسازید که متن “Hello, Tkinter” را نمایش دهد و آن را در متغیری به نام greeting ذخیره کنید:
1 |
>>> greeting = tk.Label(text="Hello, Tkinter") |
در این مرحله، پنجرهای که قبلاً ساختید هنوز تغییری نکرده است. در واقع، شما تنها یک ویجت Label ساختهاید، اما هنوز آن را به پنجره اضافه نکردهاید. چند روش مختلف برای اضافه کردن ویجت ها به پنجره وجود دارد، اما فعلاً میتوانید از متد ()pack. استفاده کنید:
1 |
>>> greeting.pack() |
حالا پنجره طوری تغییر میکند که متن “Hello, Tkinter” در آن نمایش داده میشود.
متد ()pack
باعث میشود که Tkinter اندازه پنجره را بهصورت خودکار طوری تنظیم کند که ویجت کاملاً درون آن جا بگیرد.
در ادامه این خط را اجرا کنید:
1 |
>>> window.mainloop() |
در ظاهر ممکن است اتفاق خاصی نیفتد، اما توجه کنید که دیگر پرامپت جدیدی در محیط شِل (shell) ظاهر نمیشود.
متد window.mainloop() به پایتون میگوید که وارد حلقه رویدادهای Tkinter شود. این حلقه وظیفه دارد که به رویدادهایی مانند کلیک روی دکمه یا فشردن کلیدها گوش دهد. همچنین، وقتی این متد اجرا شود، اجرای کدهای بعدی تا زمانی که پنجره بسته شود متوقف خواهد شد. پس از بستن پنجره، خواهید دید که پرامپت جدید در شل ظاهر میشود.
⚠️ هشدار: وقتی در محیط REPL (محیط تعاملی پایتون) با Tkinter کار میکنید، تغییرات در پنجره بهصورت خطبهخط اعمال میشود. اما در صورتی که برنامه Tkinter را بهصورت یک فایل پایتون اجرا کنید، این رفتار متفاوت است!
اگر در انتهای فایل پایتون، متد window.mainloop() را ننویسید، برنامه رابط کاربری شما اصلاً اجرا نخواهد شد و هیچ چیزی نمایش داده نمیشود. در محیط REPL، برای اعمال تغییرات میتوانید بهجای mainloop() بهصورت مرحلهای از window.update() استفاده کنید تا تغییرات پس از هر خط کد نمایش داده شود.
ایجاد یک پنجره با Tkinter تنها به چند خط کد ساده نیاز دارد. اما پنجره خالی چندان کاربردی نیست! در بخش بعدی، با انواع ویجت های موجود در Tkinter و نحوه سفارشی سازی آنها آشنا خواهید شد تا بتوانید رابط کاربری موردنظر خود را بسازید.
کار با ویجت ها (Widgets)
ویجت ها ستون فقرات چارچوب گرافیکی Tkinter در پایتون هستند. آنها عناصری هستند که کاربران از طریق آنها با برنامه شما تعامل برقرار میکنند. در Tkinter، هر ویجت با یک کلاس مشخص تعریف میشود. در ادامه، برخی از ویجتهای پرکاربرد آورده شدهاند:
کلاس ویجت | توضیحات |
---|---|
Label |
برای نمایش متن روی صفحه استفاده میشود. |
Button |
دکمهای که میتواند شامل متن باشد و هنگام کلیک، عملی را اجرا کند. |
Entry |
یک فیلد متنی برای ورود تنها یک خط متن. |
Text |
فیلدی برای وارد کردن متن چندخطی. |
Frame |
ناحیهای مستطیلی برای گروهبندی ویجت های مرتبط یا ایجاد فاصله بین آنها. |
در بخشهای بعدی آموزش، با نحوه کار با هرکدام از این ویجت ها آشنا خواهید شد. با این حال، توجه داشته باشید که Tkinter ویجت های بیشتری از این فهرست دارد.
اگر وارد دنیای ویجت های با ظاهر مدرنتر (که با عنوان themed widgets شناخته میشوند) شوید، انتخاب ویجت مناسب حتی پیچیدهتر هم میشود. اما در ادامه این آموزش، فقط از ویجت های کلاسیک Tkinter استفاده خواهیم کرد.
در حال حاضر، ویجت های Tkinter بهطور کلی به دو دسته اصلی تقسیم میشوند:
۱. ویجت های کلاسیک (Classic Widgets)
این ویجت ها در بسته اصلی tkinter
موجودند؛ برای مثال: tkinter.Label
.
ویجت های کلاسیک ساده، قابلدرک و بهراحتی قابل سفارشی سازی هستند. با این حال، ظاهر آنها در اغلب سیستم عامل های امروزی قدیمی یا ناهماهنگ به نظر میرسد.
۲. ویجتهای با ظاهر مدرن یا تمدار (Themed Widgets)
این دسته در زیرماژول tkinter.ttk
قرار دارند؛ برای مثال: tkinter.ttk.Label
.
ویجت های تمدار (Themed) با ظاهر بومی سیستم عامل کار میکنند و تجربهای آشناتر برای کاربران فراهم میکنند. به همین دلیل، اگر میخواهید ظاهر برنامه شما شبیه به سایر برنامه های معمول سیستم عامل باشد، بهتر است از ویجت های ttk
استفاده کنید.
بسیاری از این ویجت های تمدار، جایگزین مستقیمی برای نسخه های کلاسیک خود هستند و تنها تفاوت آنها در ظاهر مدرنترشان است. همچنین، برخی ویجت های جدید مانند نوار پیشرفت (Progress Bar) تنها در مجموعه ویجت های تمدار وجود دارند و در نسخه های قدیمی در دسترس نیستند.
با این حال، برخی ویجت های کلاسیک که معادل تمدار ندارند، همچنان باید از نسخه کلاسیک آنها استفاده کرد.
💡 نکته: ویجت های
ttk
بهطور پیشفرض از ظاهر بومی سیستمعامل استفاده میکنند، اما شما میتوانید ظاهر آنها را از طریق تِم (Theme) تغییر دهید؛ برای مثال به حالت تاریک یا روشن. تم ها مجموعهای از استایل های قابلاستفاده مجدد هستند، شبیه به فایل های CSS در طراحی صفحات وب.
برای امکان تغییر استایل، بیشتر اطلاعات ظاهری ویجت های ttk
به اجزای جداگانهای منتقل شدهاند. این تفکیک از نظر طراحی مزیت محسوب میشود، اما باعث پیچیدگی در سفارشیسازی آنها نسبت به ویجت های کلاسیک میشود.
استفاده همزمان از ویجت های کلاسیک و تمدار
برای استفاده از هر دو نوع ویجت در یک برنامه، معمولاً بهصورت زیر آنها را فراخوانی میکنیم:
1 2 |
>>> import tkinter as tk >>> import tkinter.ttk as ttk |
tk.Label
یا ttk.Label
استفاده کنیم:
1 2 3 4 5 |
tk.Label() # خروجی: <tkinter.Label object .!label> ttk.Label() # خروجی: <tkinter.ttk.Label object .!label2> |
استفاده از import با علامت *
در برخی موارد ممکن است بخواهید ویجت های کلاسیک را با نمونه های تم دار جایگزین کنید. برای این کار میتوانید از وارد کردن سراسری (Wildcard Import) استفاده کنید:
1 2 |
from tkinter import * from tkinter.ttk import * |
1 2 3 4 5 |
Label() # خروجی: <tkinter.ttk.Label object .!label> Text() # خروجی: <tkinter.Text object .!text> |
import
بسیار مهم است. ابتدا باید tkinter
و سپس ttk
را وارد کنید.با این حال، استفاده از
import *
معمولاً توصیه نمیشود، چون باعث ابهام در کد میشود و خوانایی آن را کاهش میدهد. مگر اینکه بهطور آگاهانه و در شرایط خاص انجام شود.
برای مشاهده فهرست کامل ویجت های Tkinter، میتوانید به بخش های «Basic Widgets» و «More Widgets» در آموزش TkDocs مراجعه کنید. با اینکه این مستندات بیشتر بر روی ویجت های تمدار معرفیشده در Tcl/Tk نسخه ۸.۵ تمرکز دارند، اما بسیاری از مطالب آنها برای ویجت های کلاسیک نیز کاربردی هستند.
نمایش متن و تصویر با استفاده از ویجت Label
ویجت های Label برای نمایش متن یا تصویر در رابط گرافیکی استفاده میشوند. متنی که در این ویجت نمایش داده میشود، توسط کاربر قابل ویرایش نیست و صرفاً برای نمایش است.
همانطور که در ابتدای این آموزش دیدید، میتوانید با نمونه سازی از کلاس Label
و ارسال یک رشته متنی به پارامتر text
، یک ویجت Label ایجاد کنید:
1 |
label = tk.Label(text="Hello, Tkinter") |
ویجت های Label بهطور پیشفرض از رنگ متن و پسزمینه تعیینشده توسط سیستم عامل استفاده میکنند. معمولاً این رنگ ها بهترتیب مشکی و سفید هستند، اما در صورتی که تنظیمات سیستم عامل شما متفاوت باشد، ممکن است رنگ های دیگری ببینید.
تغییر رنگ متن و پسزمینه
میتوانید رنگ متن و پسزمینه را با استفاده از پارامترهای foreground
(یا مخفف fg
) و background
(یا مخفف bg
) تنظیم کنید:
1 2 3 4 5 |
label = tk.Label( text="Hello, Tkinter", foreground="white", # رنگ متن: سفید background="black" # رنگ پسزمینه: مشکی ) |
1 |
label = tk.Label(text="Hello, Tkinter", fg="white", bg="black") |
رنگ های پشتیبانی شده
برخی از نام های رنگی معتبر شامل موارد زیر هستند:
-
"red"
(قرمز) -
"orange"
(نارنجی) -
"yellow"
(زرد) -
"green"
(سبز) -
"blue"
(آبی) -
"purple"
(بنفش)
بسیاری از نام های رنگ HTML نیز در Tkinter قابل استفادهاند. برای مشاهده فهرست کامل رنگ ها، از جمله رنگ های خاص سیستم عامل های macOS و Windows، میتوانید به راهنمای رنگ ها در Tkinter مراجعه کنید.
استفاده از رنگ های HEX
همچنین میتوانید از کدهای رنگ هگزادسیمال (hexadecimal) برای تعریف رنگ استفاده کنید:
1 |
label = tk.Label(text="Hello, Tkinter", background="#34A2FE") |
کد بالا پسزمینه برچسب را به رنگ آبی روشن تنظیم میکند. اگرچه استفاده از کدهای HEX ممکن است کمتر قابل خواندن باشد، اما انعطافپذیری بیشتری نسبت به نام های رنگ دارد. خوشبختانه ابزارهای زیادی برای انتخاب کد رنگ HEX بهصورت بصری وجود دارد.
تنظیم عرض و ارتفاع ویجت Label
میتوانید اندازه ویجت Label را نیز با استفاده از پارامترهای width
(عرض) و height
(ارتفاع) مشخص کنید:
1 2 3 4 5 6 7 |
label = tk.Label( text="Hello, Tkinter", fg="white", bg="black", width=10, height=10 ) |
مقادیر width
و height
به تعداد کاراکترهای متنی (برای عرض) و تعداد خطوط متنی (برای ارتفاع) اشاره دارند. این اندازه ها به پیکسل نیستند، بلکه وابسته به فونت و اندازه متن هستند.
در ادامه آموزش، با سایر ویژگی های قابل تنظیم در ویجت Label و همچنین ویجت های دیگر در Tkinter آشنا خواهید شد.
در اینجا نمای برچسب (Label) ایجادشده در یک پنجره Tkinter را مشاهده میکنید:
ممکن است عجیب به نظر برسد که این برچسب در پنجره بهصورت مربعی نمایش داده نمیشود، حتی با اینکه مقادیر width
و height
هر دو برابر با ۱۰ تعیین شدهاند. دلیل این موضوع این است که اندازه گیری عرض و ارتفاع در Tkinter براساس واحدهای متنی (text units) انجام میشود.
هر واحد افقی متنی برابر با عرض کاراکتر “۰” (عدد صفر) در فونت پیشفرض سیستم است. بهطور مشابه، هر واحد عمودی نیز بر اساس ارتفاع همین کاراکتر تعیین میشود.
🔔 نکته: برای اندازه گیری عرض و ارتفاع، Tkinter از واحدهای متنی استفاده میکند (نه پیکسل، سانتیمتر یا اینچ) تا عملکرد رابط کاربری روی سیستمهای مختلف ثابت و یکنواخت باقی بماند.
این روش باعث میشود اندازهی ویجت ها نسبت به فونت پیشفرض سیستم کاربر تنظیم شود، تا متون بهدرستی درون برچسبها و دکمهها جا بگیرند، صرفنظر از اینکه برنامه روی چه سیستمی اجرا میشود.
نمایش دکمه های قابل کلیک با ویجت Button
ویجت های دکمه (Button) برای ایجاد دکمه هایی بهکار میروند که کاربر بتواند روی آنها کلیک کند. این دکمه ها را میتوان بهگونهای پیکربندی کرد که هنگام کلیک، یک تابع خاص را اجرا کنند. (نحوه اتصال دکمه ها به توابع را در بخش بعدی خواهید دید.)
در حال حاضر، فقط با نحوه ساخت و استایل دهی به دکمه آشنا شوید.
ویجت های Button و Label شباهت زیادی به یکدیگر دارند. در واقع، میتوان گفت یک دکمه چیزی جز یک برچسب قابل کلیک نیست! بسیاری از آرگومان هایی که برای ساخت و استایل دهی Label بهکار میروند، برای دکمه ها هم قابل استفاده هستند.
مثلاً کد زیر، دکمهای با پسزمینه آبی و متن زرد ایجاد میکند. همچنین عرض و ارتفاع آن را بهترتیب روی ۲۵ و ۵ واحد متنی تنظیم میکند:
1 2 3 4 5 6 7 |
button = tk.Button( text="Click me!", width=25, height=5, bg="blue", fg="yellow", ) |
خیلی جالبه! حالا میتوانید از دو ویجت بعدی برای دریافت ورودی متنی از کاربر استفاده کنید.
دریافت ورودی کاربر با استفاده از ویجت Entry در Tkinter
زمانیکه قصد دارید متن کوتاهی از کاربر دریافت کنید، مانند نام یا آدرس ایمیل، ویجت Entry
گزینهای بسیار مناسب خواهد بود. این ویجت یک کادر متنی کوچک در اختیار کاربر قرار میدهد تا اطلاعات مورد نظر خود را وارد کند.
فرآیند ایجاد و شخصیسازی ویجت Entry تقریباً مشابه ویجت های Label
و Button
است. برای نمونه، قطعه کد زیر یک کادر متنی با پس زمینه آبی، رنگ متن زرد و عرض ۵۰ واحد متنی ایجاد میکند:
1 |
entry = tk.Entry(fg="yellow", bg="blue", width=50) |
نکته قابلتوجه در مورد ویجت Entry، نحوه استفاده از آن جهت دریافت اطلاعات ورودی از کاربر است. این ویجت سه عملیات اصلی را پشتیبانی میکند:
-
دریافت مقدار متنی واردشده با متد
.get()
-
حذف محتوا با متد
.delete()
-
درج متن جدید با متد
.insert()
برای درک بهتر عملکرد ویجت Entry، پیشنهاد میشود یک نمونه از آن را در محیط تعاملی (Python Shell) اجرا و بررسی کنید. ابتدا ماژول Tkinter را وارد کرده و یک پنجره جدید ایجاد نمایید:
1 2 |
>>> import tkinter as tk >>> window = tk.Tk() |
1 2 |
>>> label = tk.Label(text="Name") >>> entry = tk.Entry() |
.pack()
، ویجت ها را به پنجره اضافه کنید تا قابلنمایش شوند:
1 2 |
>>> label.pack() >>> entry.pack() |
در ادامه، ظاهر این پنجره را مشاهده میکنید:
توجه داشته باشید که متد .pack()
به صورت پیشفرض عناصر را به شکل عمودی در مرکز پنجره قرار میدهد.
اکنون میتوانید با کلیک بر روی کادر متنی، عبارتی مانند Real Python
را وارد نمایید:
در این بخش، متنی را در ویجت Entry وارد کردهاید، اما این متن هنوز به برنامه شما منتقل نشده است. برای دریافت متن و اختصاص آن به یک متغیر به نام name
میتوانید از متد .get()
استفاده کنید:
1 2 3 |
>>> name = entry.get() >>> name 'Real Python' |
علاوه بر دریافت متن، میتوانید متن موجود در Entry را حذف کنید. متد .delete()
برای این کار به کار میرود و یک آرگومان عدد صحیح دریافت میکند که مشخص میکند کدام کاراکتر باید حذف شود. برای مثال، قطعه کد زیر نشان میدهد که .delete(0)
اولین کاراکتر موجود در Entry را حذف میکند:
1 |
>>> entry.delete(0) |
پس از اجرای این دستور، متن موجود در ویجت به این صورت تغییر میکند:
نکته: درست مانند رشته های (string) پایتون، ایندکس گذاری کاراکترهای موجود در Entry نیز از صفر آغاز میشود.
اگر بخواهید چند کاراکتر را به طور همزمان حذف کنید، میتوانید یک آرگومان دوم نیز به .delete()
بدهید که نشان میدهد حذف تا کدام ایندکس ادامه داشته باشد. برای نمونه، کد زیر چهار حرف اول را از Entry حذف میکند:
1 |
>>> entry.delete(0, 4) |
متد Entry.delete()
بسیار شبیه به برش رشته ها (string slicing) در پایتون عمل میکند؛ آرگومان اول ایندکس شروع حذف و آرگومان دوم ایندکس پایان حذف (که خود آن کاراکتر حذف نمیشود) را مشخص میکند. همچنین، اگر بخواهید تمام متن موجود در Entry را پاک کنید، میتوانید از ثابت tk.END
به عنوان آرگومان دوم استفاده کنید:
1 |
>>> entry.delete(0, tk.END) |
در مقابل، میتوانید متن جدیدی را نیز وارد Entry کنید. برای این کار متد .insert()
استفاده میشود:
1 |
>>> entry.insert(0, "Python") |
Python
را نمایش میدهد:
آرگومان اول متد .insert()
مشخص میکند که متن در چه موقعیتی درج شود. اگر Entry خالی باشد، متن وارد شده همیشه در ابتدای ویجت قرار میگیرد، صرفنظر از اینکه چه مقداری به عنوان آرگومان اول وارد کردهاید. برای مثال، اگر به جای ۰ عدد ۱۰۰ را وارد میکردید، باز هم متن از ابتدای Entry درج میشد.
اگر Entry قبلاً دارای متنی باشد، متد .insert()
متن جدید را در موقعیت مشخصشده درج میکند و باقی متن را به سمت راست جابهجا میکند:
1 |
>>> entry.insert(0, "Real ") |
Real Python
درخواهد آمد:
ویجت های Entry برای دریافت مقادیر متنی کوچک از کاربر بسیار مناسباند. با این حال، چون فقط روی یک خط نمایش داده میشوند، برای دریافت مقادیر متنی طولانی چندان کاربردی نیستند. برای این موارد، از ویجت های Text استفاده میشود که در ادامه به آنها خواهیم پرداخت.
دریافت ورودی چندخطی از کاربر با استفاده از ویجت های Text
ویجت های Text برای وارد کردن متن به کار میروند و از این نظر شبیه به ویجت های Entry هستند. تفاوت اصلی در این است که ویجت های Text میتوانند شامل چندین خط متن باشند. بنابراین، با استفاده از Text، کاربر میتواند یک پاراگراف یا حتی چندین صفحه متن را وارد کند.
مشابه ویجت های Entry، سه عملیات اصلی را میتوان روی ابزارکهای Text انجام داد:
-
دریافت متن با
.get()
-
حذف متن با
.delete()
-
درج متن با
.insert()
هرچند نام این متدها همانند متدهای Entry است، اما نحوه عملکرد آنها کمی متفاوت است. در ادامه با ساخت یک ویجت Text و مشاهده قابلیت های آن، این تفاوت ها را بررسی میکنیم.
نکته:
آیا هنوز پنجره ایجادشده در بخش قبلی باز است؟
اگر بله، میتوانید آن را با دستور زیر ببندید:
1 |
>>> window.destroy() |
آموزش های کاربردی پایتون
- راهنمای گام به گام یادگیری برنامه نویسی پایتون در سال 2025 برای مبتدیان
- آموزش پروژه محور طراحی سایت با پایتون و جنگو مختص بازار کار
- آموزش بهینه سازی کدها در برنامه نویسی پایتون
- 10 کاربرد برنامه نویسی پایتون
- نکات مهم در یادگیری برنامه نویسی پایتون برای تازه کارها
- آموزش ساخت ربات گرید با پایتون برای معاملات الگوریتمی
- آموزش تشخیص نفوذ با یادگیری ماشین و پایتون
- سورس و پروژه رایگان پایتون
اکنون در محیط شل پایتون، یک پنجره جدید ایجاد کرده و یک ویجت Text در آن قرار دهید:
1 2 3 |
>>> window = tk.Tk() >>> text_box = tk.Text() >>> text_box.pack() |
به طور پیشفرض، جعبه های متنی (Text) از ویجت های Entry بزرگتر هستند. پنجرهای که در بالا ایجاد شده است، به این شکل است:
برای فعال کردن کادر متن، در هر جایی از پنجره کلیک کنید. کلمه Hello
را تایپ کنید. سپس Enter را فشار دهید و در خط دوم World
را تایپ کنید. اکنون پنجره باید به شکل زیر باشد:
دریافت متن از ابزارک Text
مشابه Entry، میتوانید متن را با استفاده از متد .get()
دریافت کنید. با این حال، اگر بدون آرگومان .get()
را فراخوانی کنید، برخلاف Entry که کل متن را بازمیگرداند، در اینجا خطا ایجاد میشود:
1 2 3 4 |
>>> text_box.get() Traceback (most recent call last): ... TypeError: get() missing 1 required positional argument: 'index1' |
متد Text.get()
حداقل به یک آرگومان نیاز دارد. اگر تنها یک اندیس (Index) به آن بدهید، یک کاراکتر برمیگرداند. برای دریافت چند کاراکتر، باید اندیس شروع و اندیس پایان را مشخص کنید.
از آنجایی که ویجت Text میتواند چندین خط متن داشته باشد، اندیس دهی در آن کمی متفاوت است:
-
شماره خط (شروع از ۱)
-
شماره کاراکتر در آن خط (شروع از ۰)
برای مشخص کردن یک اندیس، باید رشته ای به فرمت "<line>.<char>"
بسازید. به عنوان نمونه:
-
"1.0"
به معنای اولین کاراکتر در خط اول است. -
"2.3"
به معنای چهارمین کاراکتر در خط دوم است.
برای دریافت اولین حرف از جعبه متنی ایجادشده، اینطور عمل کنید:
1 2 |
>>> text_box.get("1.0") 'H' |
Hello
پنج حرف دارد و چون شماره کاراکترها از صفر شروع میشود، حرف o
در موقعیت ۴ قرار دارد. برای دریافت کل واژه Hello
باید اندیس شروع را "1.0"
و اندیس پایان را "1.5"
تعیین کنید (مشابه برش رشته ها در پایتون، اندیس پایان شامل نمیشود):
1 2 |
>>> text_box.get("1.0", "1.5") 'Hello' |
World
که در خط دوم قرار دارد، کافی است شماره خط را تغییر دهید:
1 2 |
>>> text_box.get("2.0", "2.5") 'World' |
"1.0"
به عنوان اندیس شروع و از ثابت tk.END
به عنوان اندیس پایان استفاده کنید:
1 2 |
>>> text_box.get("1.0", tk.END) 'Hello\nWorld\n' |
\n
) نیز میشود. همچنین، در ویجت Text، هر خط با یک نویسه خط جدید خاتمه مییابد، حتی آخرین خط.
حذف متن در ابزارک Text
برای حذف متن از جعبه متنی، از متد .delete()
استفاده میشود. این متد عملکردی مشابه با .delete()
در ویجت Entry دارد و به دو روش قابل استفاده است:
-
با یک آرگومان
-
با دو آرگومان
در حالت تک آرگومانی، اندیس کاراکتری که میخواهید حذف شود را به .delete()
میدهید. برای نمونه، دستور زیر اولین کاراکتر (یعنی حرف H
) را حذف میکند:
1 |
>>> text_box.delete("1.0") |
ello
نمایش داده میشود:
در حالت دوم که .delete()
دو آرگومان میگیرد، شما باید دو ایندکس وارد کنید تا یک بازه از کاراکترها، از ایندکس اول تا قبل از ایندکس دوم، حذف شوند.
برای مثال، برای حذف باقیمانده کلمه ello از خط اول ویجت متنی، از ایندکس های “1.0” و “1.4” استفاده کنید:
1 |
>>> text_box.delete("1.0", "1.4") |
حتی اگر چیزی روی خط اول نمیبینید، باز هم یک کاراکتر در آنجا وجود دارد؛ یک کاراکتر خط جدید (newline)! میتوانید با استفاده از .get() این موضوع را بررسی کنید:
1 2 |
>>> text_box.get("1.0") '\n' |
1 |
>>> text_box.delete("1.0") |
سعی کنید باقی متن داخل ویجت را هم پاک کنید. ایندکس شروع را “1.0” قرار دهید و برای ایندکس پایان از tk.END
استفاده کنید:
1 |
>>> text_box.delete("1.0", tk.END) |
شما میتوانید با استفاده از .insert() متن جدیدی داخل ویجت متنی وارد کنید:
1 |
>>> text_box.insert("1.0", "Hello") |
"<line>.<column>"
که برای .get() هم استفاده میشد:
حالا ببینید اگر سعی کنید کلمه World را روی خط دوم وارد کنید چه اتفاقی میافتد:
1 |
>>> text_box.insert("2.0", "World") |
اگر میخواهید متن روی یک خط جدید درج شود، باید یک کاراکتر خط جدید (\n) به صورت دستی داخل رشته وارد شده قرار دهید:
1 |
>>> text_box.insert("2.0", "\nWorld") |
عملکرد .insert() به یکی از دو صورت زیر است:
-
اگر در موقعیت مشخصشده متنی وجود داشته باشد یا بعد از آن، متن جدید درج میشود.
-
اگر موقعیت مشخصشده بعد از آخرین کاراکتر باشد، متن به انتهای آن خط افزوده میشود.
معمولاً پیگیری موقعیت آخرین کاراکتر کار راحتی نیست. بهترین روش برای درج متن در انتهای ویجت متنی، این است که tk.END
را به عنوان پارامتر اول به .insert() بدهید:
1 |
>>> text_box.insert(tk.END, "Put me at the end!") |
1 |
>>> text_box.insert(tk.END, "\nPut me on a new line!") |
اختصاص دادن ویجت ها به فریم ها با استفاده از ویجت Frame
در این بخش، شما فقط با پنج ویجت کار خواهید کرد:
-
Label
-
Button
-
Entry
-
Text
-
Frame
این چهار ویجتی هستند که قبلاً با آنها آشنا شدهاید، به اضافه ویجت Frame. ویجت Frame برای سازماندهی چیدمان ویجت ها در یک برنامه اهمیت زیادی دارد.
قبل از اینکه وارد جزئیات مربوط به چیدمان بصری ویجت ها شوید، بیایید دقیقتر ببینیم که ویجت Frame چگونه کار میکند و چطور میتوان سایر ویجت ها را به آن نسبت داد. اسکریپت زیر یک ویجت Frame خالی ایجاد میکند و آن را به پنجره اصلی برنامه اختصاص میدهد:
1 2 3 4 5 6 7 |
import tkinter as tk window = tk.Tk() frame = tk.Frame() frame.pack() window.mainloop() |
frame.pack()
فریم را داخل پنجره قرار میدهد تا پنجره اندازه خود را تا حد ممکن کوچک کند و فقط فضای لازم برای فریم را در بر بگیرد. زمانی که اسکریپت بالا را اجرا میکنید، خروجی بسیار سادهای مشاهده میکنید:
ویجت Frame را بهتر است به عنوان یک ظرف برای سایر ویجت ها در نظر بگیرید. شما میتوانید با تعیین مشخصه master
برای یک ویجت، آن را به یک فریم نسبت دهید:
1 2 |
frame = tk.Frame() label = tk.Label(master=frame) |
برای اینکه بهتر درک کنید این فرآیند چگونه کار میکند، یک اسکریپت بنویسید که دو ویجت Frame به نام های frame_a
و frame_b
ایجاد کند. در این اسکریپت، frame_a
شامل یک لیبل با متن “I’m in Frame A” و frame_b
شامل لیبلی با متن “I’m in Frame B” خواهد بود. یکی از روش های انجام این کار به این صورت است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import tkinter as tk window = tk.Tk() frame_a = tk.Frame() frame_b = tk.Frame() label_a = tk.Label(master=frame_a, text="I'm in Frame A") label_a.pack() label_b = tk.Label(master=frame_b, text="I'm in Frame B") label_b.pack() frame_a.pack() frame_b.pack() window.mainloop() |
دقت کنید که ابتدا frame_a
به پنجره اضافه شده و سپس frame_b
. بنابراین، در پنجره ای که باز میشود، لیبل مربوط به frame_a
بالای لیبل frame_b
نمایش داده میشود:
حالا ببینید چه اتفاقی میافتد اگر ترتیب frame_a.pack()
و frame_b.pack()
را عوض کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import tkinter as tk window = tk.Tk() frame_a = tk.Frame() label_a = tk.Label(master=frame_a, text="I'm in Frame A") label_a.pack() frame_b = tk.Frame() label_b = tk.Label(master=frame_b, text="I'm in Frame B") label_b.pack() # Swap the order of `frame_a` and `frame_b` frame_b.pack() frame_a.pack() window.mainloop() |
در این حالت، خروجی به این صورت تغییر میکند:
حالا لیبل label_b
در بالا قرار گرفته است. چون label_b
به frame_b
نسبت داده شده، به هرجایی که frame_b
قرار بگیرد، منتقل میشود.
تمام ویجت هایی که تا اینجا یاد گرفتهاید — یعنی Label، Button، Entry و Text — هنگام ساخته شدن دارای یک ویژگی master
هستند که میتوانید با تعیین آن مشخص کنید که هر ویجت به کدام فریم تعلق داشته باشد.
استفاده از ویجت های Frame راهی عالی برای سازماندهی منطقی سایر ویجت هاست. ویجت های مرتبط میتوانند به یک فریم نسبت داده شوند، به طوری که اگر آن فریم در پنجره جابهجا شود، ویجتهای مربوطه نیز همراه آن حرکت کنند.
نکته: اگر هنگام ساخت یک ویجت، آرگومان master
را مشخص نکنید، ویجت به طور پیشفرض داخل پنجره سطح بالای (Top-Level Window) قرار میگیرد.
علاوه بر گروهبندی منطقی ویجت ها، ویجت Frame میتواند به ظاهر گرافیکی برنامه شما نیز جلوه بیشتری بدهد. در ادامه خواهید دید که چطور میتوانید انواع مختلفی از حاشیه ها (border) را برای ویجت های Frame ایجاد کنید.
تنظیم ظاهر Frame با استفاده از ویژگی relief
ویجت های Frame را میتوان با ویژگی relief
پیکربندی کرد تا یک حاشیه دور آنها ایجاد شود. میتوانید مقدار relief
را روی یکی از مقادیر زیر تنظیم کنید:
-
tk.FLAT
: بدون اثر حاشیه (مقدار پیشفرض) -
tk.SUNKEN
: ایجاد یک ظاهر فرو رفته -
tk.RAISED
: ایجاد یک ظاهر برجسته -
tk.GROOVE
: ایجاد یک حاشیه شیاردار -
tk.RIDGE
: ایجاد یک حاشیه لبدار
برای اعمال این جلوه حاشیه، باید ویژگی borderwidth
را روی مقداری بیشتر از 1 تنظیم کنید. این ویژگی عرض حاشیه را بر حسب پیکسل تعیین میکند. بهترین روش برای درک بهتر از شکل ظاهری هر یک از این جلوه ها، دیدن آنها در عمل است. اسکریپت زیر پنج ویجت Frame را در یک پنجره قرار میدهد، که هرکدام دارای مقدار متفاوتی برای relief
هستند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import tkinter as tk border_effects = { "flat": tk.FLAT, "sunken": tk.SUNKEN, "raised": tk.RAISED, "groove": tk.GROOVE, "ridge": tk.RIDGE, } window = tk.Tk() for relief_name, relief in border_effects.items(): frame = tk.Frame(master=window, relief=relief, borderwidth=5) frame.pack(side=tk.LEFT) label = tk.Label(master=frame, text=relief_name) label.pack() window.mainloop() |
شرح این اسکریپت:
-
خطوط 3 تا 9 یک دیکشنری ایجاد میکنند که کلیدهای آن نام جلوههای مختلف
relief
در Tkinter هستند و مقادیر آنها اشیای مربوط به Tkinter هستند. این دیکشنری در متغیرborder_effects
ذخیره میشود. -
خط 13 یک حلقه for را برای پیمایش در آیتمهای دیکشنری
border_effects
آغاز میکند. -
خط 14 یک ویجت Frame جدید ایجاد کرده و آن را به شیء
window
نسبت میدهد. ویژگیrelief
به مقدار مناسب از دیکشنری تنظیم میشود وborderwidth
روی 5 قرار میگیرد تا جلوهی حاشیه قابل مشاهده باشد. -
خط 15 ویجت Frame را با استفاده از متد
.pack()
در پنجره قرار میدهد. آرگومانside
به Tkinter میگوید که Frameها را در کدام جهت قرار دهد. (در بخش بعدی بیشتر درباره این موضوع خواهید خواند.) -
خطوط 16 و 17 یک ویجت Label برای نمایش نام جلوه
relief
ایجاد کرده و آن را داخل Frame قرار میدهد.
خروجی پنجره حاصل از اجرای این اسکریپت:
در این تصویر، میتوانید جلوههای زیر را مشاهده کنید:
-
tk.FLAT
: Frame را به صورت کاملاً تخت نمایش میدهد. -
tk.SUNKEN
: حاشیهای اضافه میکند که باعث میشود Frame به نظر برسد داخل پنجره فرو رفته است. -
tk.RAISED
: حاشیهای ایجاد میکند که باعث میشود Frame از صفحه بیرون زده به نظر برسد. -
tk.GROOVE
: حاشیهای به شکل یک شیار دور Frame ایجاد میکند. -
tk.RIDGE
: نمایی شبیه لبه برجسته در اطراف Frame ایجاد میکند.
این جلوهها میتوانند به برنامه گرافیکی شما در Tkinter جلوهای بصری و جذابتر ببخشند.
درک نام گذاری ویجت ها
هنگامی که یک ویجت ایجاد میکنید، میتوانید هر نام معتبری که در پایتون مجاز است به آن اختصاص دهید. معمولاً توصیه میشود که در نام متغیر اختصاص داده شده به ویجت، به نام کلاس ویجت نیز اشاره شود. برای مثال، اگر یک ویجت Label برای نمایش نام کاربر دارید، ممکن است نام متغیر را label_user_name
بگذارید. اگر یک ویجت Entry برای دریافت سن کاربر دارید، میتوانید آن را entry_age
بنامید.
نکته: گاهی ممکن است ویجتی را بدون اختصاص دادن به متغیر جدید تعریف کنید و مستقیماً متد .pack()
را روی همان خط فراخوانی نمایید:
1 |
>>> tk.Label(text="Hello, Tkinter").pack() |
این کار زمانی مفید است که قصد ندارید در ادامه به نمونه ویجت ارجاع دهید. در حالت معمول، پایتون این اشیای بدون متغیر را به دلیل مدیریت خودکار حافظه حذف میکند، اما Tkinter با ثبت داخلی هر ویجت جدید، از این کار جلوگیری میکند.
هنگامی که نام کلاس ویجت را در نام متغیر قرار میدهید، به خودتان و دیگران کمک میکنید تا بفهمند که متغیر مربوط به چه نوع ویجتی است. البته استفاده از نام کامل کلاس ویجت ممکن است باعث طولانی شدن نامها شود. به همین دلیل، ممکن است بخواهید برای اشاره به هر نوع ویجت، از یک پیشوند کوتاه استفاده کنید.
در ادامه این آموزش، از پیشوندهای زیر برای نام گذاری ویجت ها استفاده خواهد شد:
کلاس ویجت | پیشوند نام متغیر | مثال |
Label | lbl | lbl_name |
Button | btn | btn_submit |
Entry | ent | ent_age |
Text | txt | txt_notes |
Frame | frm | frm_address |
در این بخش یاد گرفتید چگونه یک پنجره بسازید، از ویجت ها استفاده کنید، و با Frame ها کار کنید. در این مرحله، میتوانید پنجره های ساده ای بسازید که پیام هایی را نمایش میدهند، اما هنوز یک اپلیکیشن کامل طراحی نکردهاید. در بخش بعدی، یاد خواهید گرفت که چطور با استفاده از مدیرهای چیدمان قدرتمند Tkinter، نحوه چیدمان ویجت ها در برنامه را کنترل کنید.
کنترل چیدمان با استفاده از مدیرهای چیدمان (Geometry Managers)
تا اینجا، شما ویجت ها را با استفاده از متد .pack()
به پنجره ها و ویجت های Frame اضافه کردهاید، اما هنوز دقیقاً نمیدانید که این متد چه کاری انجام میدهد. بیایید این موضوع را روشن کنیم!
چیدمان برنامه ها در Tkinter با استفاده از چیزی به نام “مدیر چیدمان” (geometry manager) کنترل میشود. متد .pack()
یکی از این مدیرها است، اما تنها گزینه موجود نیست. Tkinter دو مدیر چیدمان دیگر نیز دارد:
-
.place()
-
.grid()
هر پنجره یا Frame در برنامه شما فقط میتواند از یکی از این مدیرهای چیدمان استفاده کند. با این حال، فریم های مختلف میتوانند مدیرهای چیدمان متفاوتی داشته باشند، حتی اگر در داخل فریمی قرار گرفته باشند که از مدیر چیدمان دیگری استفاده میکند.
ابتدا بیایید دقیقتر به .pack()
نگاه کنیم.
مدیر چیدمان.pack()
مدیر چیدمان .pack()
از یک الگوریتم بسته بندی برای قرار دادن ویجت ها در داخل یک Frame یا پنجره استفاده میکند، به ترتیبی که مشخص کردهاید. این الگوریتم برای هر ویجت دو مرحله اصلی دارد:
-
محاسبه یک ناحیه مستطیل شکل به نام “بسته” (parcel) که به اندازه کافی بلند (یا عریض) است تا ویجت را در خود جای دهد و باقی مانده عرض (یا ارتفاع) پنجره را با فضای خالی پر میکند.
-
قرار دادن ویجت در مرکز بسته، مگر این که مکان دیگری مشخص شده باشد.
.pack()
قدرتمند است، اما تصور نحوه عملکرد آن ممکن است دشوار باشد. بهترین راه برای درک .pack()
مشاهده مثالهای عملی است. ببینید وقتی سه ویجت Frame را با استفاده از .pack()
به یک پنجره اضافه میکنید چه اتفاقی میافتد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import tkinter as tk window = tk.Tk() frame1 = tk.Frame(master=window, width=100, height=100, bg="red") frame1.pack() frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow") frame2.pack() frame3 = tk.Frame(master=window, width=25, height=25, bg="blue") frame3.pack() window.mainloop() |
.pack()
به طور پیشفرض هر Frame را زیر مورد قبلی قرار میدهد، به ترتیبی که به پنجره اختصاص داده شدهاند:
-
هر Frame در بالاترین موقعیت موجود قرار میگیرد. بنابراین ابتدا Frame قرمز در بالای پنجره قرار میگیرد، سپس Frame زرد در زیر آن، و در نهایت Frame آبی در پایینترین موقعیت.
-
سه بسته نامرئی وجود دارند که هرکدام شامل یکی از این سه Frame هستند.
-
هر بسته به اندازه عرض پنجره عریض است و به اندازه ارتفاع Frame خودش بلند.
-
چون هنگام فراخوانی
.pack()
برای هر Frame هیچ موقعیت خاصی مشخص نشده، همه آنها به طور پیشفرض در مرکز بسته خود قرار گرفتهاند. به همین دلیل، هر Frame در مرکز پنجره قرار میگیرد.
استفاده از fill در .pack()
متد .pack()
چند آرگومان کلیدی برای کنترل دقیقتر چیدمان ویجت ها دارد. برای مثال، میتوانید از آرگومان fill
استفاده کنید تا مشخص کنید ویجت در چه جهتی فضای باقیمانده را پر کند. گزینه های موجود:
-
tk.X
برای پر کردن افقی -
tk.Y
برای پر کردن عمودی -
tk.BOTH
برای پر کردن در هر دو جهت
در مثال زیر، هر سه Frame طوری قرار داده میشوند که تمام عرض پنجره را پر کنند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import tkinter as tk window = tk.Tk() frame1 = tk.Frame(master=window, height=100, bg="red") frame1.pack(fill=tk.X) frame2 = tk.Frame(master=window, height=50, bg="yellow") frame2.pack(fill=tk.X) frame3 = tk.Frame(master=window, height=25, bg="blue") frame3.pack(fill=tk.X) window.mainloop() |
width
ای برای Frame ها تعیین نشده است، چون .pack(fill=tk.X)
باعث میشود عرض هر Frame به طور خودکار با عرض پنجره برابر شود، حتی اگر عرض مشخصی تعیین شده باشد.
پنجره ای که توسط این اسکریپت تولید میشود به شکل زیر است:
یکی از ویژگیهای مثبت استفاده از .pack()
برای پر کردن پنجره این است که خاصیت پر شدن (fill) نسبت به تغییر اندازه پنجره واکنش گرا (responsive) است. برای درک بهتر این موضوع، کافی است پنجره ای که توسط اسکریپت قبلی تولید شده را عریض تر کنید. با افزایش عرض پنجره، عرض هر سه ویجت Frame نیز افزایش مییابد تا کل عرض پنجره را پر کنند:
نکته: توجه کنید که این Frame ها فقط در جهت افقی بزرگ میشوند، نه عمودی.
استفاده از side در .pack()
آرگومان کلیدی دیگر در .pack()
، side
است که تعیین میکند ویجت در کدام سمت پنجره قرار بگیرد. گزینههای موجود:
-
tk.TOP
-
tk.BOTTOM
-
tk.LEFT
-
tk.RIGHT
اگر از side
استفاده نکنید، .pack()
به طور پیشفرض از tk.TOP
استفاده میکند و ویجت ها را از بالا به پایین در پنجره قرار میدهد.
در مثال زیر، سه Frame به صورت افقی کنار هم از چپ به راست چیده میشوند و در جهت عمودی کل ارتفاع پنجره را پر میکنند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import tkinter as tk window = tk.Tk() frame1 = tk.Frame(master=window, width=200, height=100, bg="red") frame1.pack(fill=tk.Y, side=tk.LEFT) frame2 = tk.Frame(master=window, width=100, bg="yellow") frame2.pack(fill=tk.Y, side=tk.LEFT) frame3 = tk.Frame(master=window, width=50, bg="blue") frame3.pack(fill=tk.Y, side=tk.LEFT) window.mainloop() |
height
را مشخص کنید تا پنجره ارتفاع داشته باشد.
نتیجه به این شکل خواهد بود:
درست همانطور که با تنظیم fill=tk.X
باعث شدید فریمها هنگام تغییر عرض پنجره بهصورت افقی واکنش پذیر باشند، میتوانید با استفاده از fill=tk.Y
این واکنش پذیری را برای تغییر ارتفاع پنجره نیز فعال کنید.
برای اینکه چیدمان واقعاً واکنش گرا (responsive) باشد، میتوانید ابتدا اندازه اولیهای برای فریم ها با استفاده از ویژگی های width
و height
مشخص کنید. سپس با استفاده از آرگومان کلیدی fill=tk.BOTH
و همچنین تنظیم expand=True
، فریم ها را طوری تنظیم کنید که به هر دو جهت گسترش پیدا کنند و فضای پنجره را بهطور کامل و پویا پر کنند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import tkinter as tk window = tk.Tk() frame1 = tk.Frame(master=window, width=200, height=100, bg="red") frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True) frame2 = tk.Frame(master=window, width=100, bg="yellow") frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True) frame3 = tk.Frame(master=window, width=50, bg="blue") frame3.pack(fill=tk.BOTH, side=tk.LEFT, expand=True) window.mainloop() |
مدیریت چیدمان با .place()
در Tkinter
با استفاده از متد .place()
میتوانید موقعیت دقیق یک ویجت (widget) را در داخل یک پنجره یا فریم مشخص کنید. برای این کار، باید دو آرگومان کلیدی x
و y
را تعیین کنید که به ترتیب مختصات افقی و عمودی گوشهی بالا-چپ ویجت را مشخص میکنند. توجه داشته باشید که این مختصات بر حسب پیکسل هستند، نه بر حسب کاراکتر یا خطوط متنی.
در نظر داشته باشید که مبدأ مختصات (جایی که x و y هر دو صفر هستند)، گوشهی بالا-چپ پنجره یا فریم است. بنابراین:
-
آرگومان
x
در متد.place()
نشاندهندهی تعداد پیکسلها از لبه چپ پنجره یا فریم است. -
آرگومان
y
نیز نشاندهندهی تعداد پیکسلها از لبه بالای پنجره یا فریم میباشد.
بهعبارت دیگر، شما با استفاده از .place()
میتوانید دقیقاً مشخص کنید که یک ویجت در چه فاصلهای از بالا و چپ قرار بگیرد.
مثال از نحوه عملکرد .place()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import tkinter as tk window = tk.Tk() frame = tk.Frame(master=window, width=150, height=150) frame.pack() label1 = tk.Label(master=frame, text="I'm at (0, 0)", bg="red") label1.place(x=0, y=0) label2 = tk.Label(master=frame, text="I'm at (75, 75)", bg="yellow") label2.place(x=75, y=75) window.mainloop() |
شرح کد:
-
در خط ۵ و ۶، یک فریم با اندازه ۱۵۰ در ۱۵۰ پیکسل ایجاد شده و با استفاده از
.pack()
در پنجره قرار گرفته است. -
در خطوط ۸ و ۹، یک Label با متن و پس زمینه قرمز ساخته شده و در مختصات (۰، ۰) درون فریم قرار گرفته است.
-
در خطوط ۱۱ و ۱۲، Label دوم با پس زمینه زرد در مختصات (۷۵، ۷۵) قرار میگیرد.
پنجره ای که کد تولید میکند به صورت زیر است:
توجه داشته باشید که اگر این کد را روی سیستم عامل متفاوتی اجرا کنید (مثلاً لینوکس، ویندوز یا مک)، که از فونت ها و استایل های متفاوتی استفاده میکند، ممکن است Label دوم (زردرنگ) تا حدی از لبه پنجره بیرون بزند و دیده نشود به همین دلیل، متد .place() چندان مورد استفاده قرار نمیگیرد. علاوه بر این، این متد دو اشکال اصلی دارد:
- مدیریت چیدمان با .place() میتواند دشوار باشد. این مشکل به ویژه زمانی که برنامه شما شامل تعداد زیادی ویجت (Widget) باشد، بیشتر نمایان میشود.
- چیدمان های ایجاد شده با .place() واکنش گرا (Responsive) نیستند. این چیدمان ها با تغییر اندازه پنجره تغییر نمیکنند.
یکی از چالشهای اصلی توسعه رابط کاربری گرافیکی (GUI) چندپلتفرمی، طراحی چیدمان هایی است که در هر پلتفرمی بهخوبی نمایش داده شوند. متد .place() گزینه مناسبی برای ایجاد چیدمان های واکنش گرا و سازگار با پلتفرم های مختلف نیست.
با این حال، این به این معنا نیست که هرگز نباید از .place() استفاده کنید! در برخی موارد، این متد میتواند دقیقاً همان چیزی باشد که نیاز دارید. برای مثال، اگر در حال طراحی رابط کاربری گرافیکی برای یک نقشه هستید، .place() میتواند انتخابی ایدهآل باشد تا ویجت ها در فاصله های دقیق و مناسب نسبت به یکدیگر روی نقشه قرار گیرند.
متد .pack() معمولاً گزینه بهتری نسبت به .place() است، اما حتی این متد نیز معایبی دارد. محل قرارگیری ویجت ها به ترتیب فراخوانی .pack() بستگی دارد، بنابراین اصلاح برنامه های موجود بدون درک کامل کد مربوط به چیدمان میتواند دشوار باشد. مدیر چیدمان .grid() بسیاری از این مشکلات را برطرف میکند، که در بخش بعدی به آن خواهیم پرداخت.
مدیریت چیدمان با .grid()
مدیر چیدمانی که احتمالاً بیشتر از همه از آن استفاده خواهید کرد، .grid()
است. این روش تمام قابلیتهای .pack()
را دارد اما با ساختاری که درک و نگهداری آن آسانتر است.
.grid()
با تقسیم کردن پنجره یا Frame به ردیف ها و ستون ها کار میکند. برای مشخص کردن محل قرارگیری یک ویجت، کافی است متد .grid()
را فراخوانی کرده و شماره ردیف و ستون را به ترتیب از طریق آرگومان های row
و column
به آن بدهید. توجه داشته باشید که شماره گذاری ردیف ها و ستون ها از صفر شروع میشود. بنابراین، اگر مقدار row=1
و column=2
را بدهید، یعنی ویجت را در سومین ستون از دومین ردیف قرار دهید.
اسکریپت زیر یک جدول ۳×۳ از Frame ها ایجاد میکند که در هرکدام یک Label قرار داده شده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import tkinter as tk window = tk.Tk() for i in range(3): for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j) label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}") label.pack() window.mainloop() |
در این مثال، شما از دو «مدیر چیدمان» استفاده کردهاید. هر frame
با استفاده از مدیر چیدمان .grid()
به پنجره اصلی (window
) اضافه شده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import tkinter as tk window = tk.Tk() for i in range(3): for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j) label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}") label.pack() window.mainloop() |
label
نیز با استفاده از .pack()
به Frame
مربوط به خودش متصل شده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import tkinter as tk window = tk.Tk() for i in range(3): for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j) label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}") label.pack() window.mainloop() |
نکته مهمی که باید درک کنید این است که با وجود اینکه متد .grid()
روی هر شی Frame
فراخوانی میشود، اما مدیر چیدمان در واقع بر روی شی window
اعمال میشود. بههمین ترتیب، چیدمان درون هر frame
با استفاده از مدیر چیدمان .pack()
کنترل میشود.
در مثال قبلی، فریم ها دقیقاً در کنار یکدیگر قرار گرفتهاند. برای اینکه مقداری فاصله بین فریم ها ایجاد کنید، میتوانید برای هر خانه جدول، padding (فضای خالی) تعیین کنید. Padding فقط فضای خالی است که اطراف یک ویجت قرار میگیرد و محتوا را از نظر بصری متمایز میکند.
دو نوع padding وجود دارد: padding داخلی و padding خارجی. padding خارجی فضایی است که اطراف بیرونی یک سلول جدول اضافه میشود. این نوع padding با دو آرگومان کلیدی برای .grid()
کنترل میشود:
-
padx
فاصله افقی را اضافه میکند. -
pady
فاصله عمودی را اضافه میکند.
هر دو padx
و pady
برحسب پیکسل اندازهگیری میشوند، نه برحسب واحدهای متنی، بنابراین اگر مقدار یکسانی برای هر دو تنظیم کنید، به همان میزان در هر دو جهت فاصله ایجاد میشود.
سعی کنید مقداری padding به اطراف فریم ها در مثال قبلی اضافه کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import tkinter as tk window = tk.Tk() for i in range(3): for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j, padx=5, pady=5) label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}") label.pack() window.mainloop() |
متد .pack()
نیز دارای پارامترهای padx
و pady
است. کد زیر تقریباً با کد قبلی یکسان است، با این تفاوت که شما پنج پیکسل فضای خالی اضافی در اطراف هر Label
، هم در جهت افقی و هم در جهت عمودی اضافه کردهاید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import tkinter as tk window = tk.Tk() for i in range(3): for j in range(3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j, padx=5, pady=5) label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}") label.pack(padx=5, pady=5) window.mainloop() |
فضای خالی اضافه شده در اطراف ویجت های Label
باعث میشود که هر سلول در جدول کمی فضای تنفس بین حاشیه Frame
و متن داخل label
داشته باشد:
این ظاهر نسبتاً خوبی دارد! اما اگر تلاش کنید پنجره را در هر جهتی بزرگ تر کنید، متوجه خواهید شد که چیدمان چندان واکنش گرا (responsive) نیست:
کل جدول همچنان در گوشه بالا-چپ باقی میماند حتی وقتی پنجره بزرگ تر میشود.
با استفاده از متدهای .columnconfigure()
و .rowconfigure()
روی شی window
، میتوانید مشخص کنید که ردیف ها و ستون های جدول هنگام تغییر اندازه پنجره چگونه رشد کنند. به خاطر داشته باشید که جدول به شی window
متصل است، حتی اگر متد .grid()
را روی هر ویجت Frame
فراخوانی کرده باشید.
هر دو متد .columnconfigure()
و .rowconfigure()
سه آرگومان اساسی دریافت میکنند:
-
Index: شماره ای که نشاندهنده اندیس ستون یا ردیفی است که میخواهید پیکربندی کنید (یا فهرستی از اندیس ها برای پیکربندی چند ردیف یا ستون بهطور همزمان).
-
Weight: یک آرگومان کلیدی به نام
weight
که مشخص میکند ردیف یا ستون مورد نظر در مقایسه با سایر ردیف ها و ستون ها چگونه نسبت به تغییر اندازه پنجره واکنش نشان دهد. -
Minimum Size: یک آرگومان کلیدی به نام
minsize
که حداقل اندازه ارتفاع ردیف یا عرض ستون را برحسب پیکسل تعیین میکند.
مقدار پیشفرض weight
برابر ۰ است، به این معنا که ستون یا ردیف هنگام تغییر اندازه پنجره، رشد نمیکند. اگر به تمام ردیف ها یا ستون ها weight=1
بدهید، همه آنها با نرخ یکسان رشد میکنند. اگر یک ستون weight=1
و دیگری weight=2
داشته باشد، ستون دوم دو برابر سریعتر از ستون اول رشد خواهد کرد.
در ادامه، اسکریپت قبلی را طوری تغییر دهید که بهتر با تغییر اندازه پنجره سازگار شود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import tkinter as tk window = tk.Tk() for i in range(3): window.columnconfigure(i, weight=1, minsize=75) window.rowconfigure(i, weight=1, minsize=50) for j in range(0, 3): frame = tk.Frame( master=window, relief=tk.RAISED, borderwidth=1 ) frame.grid(row=i, column=j, padx=5, pady=5) label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}") label.pack(padx=5, pady=5) window.mainloop() |
متدهای .columnconfigure()
و .rowconfigure()
در بدنه حلقه for
بیرونی قرار گرفتهاند. شما میتوانید هر ستون و ردیف را بهطور صریح و خارج از حلقه پیکربندی کنید، اما این کار نیاز به نوشتن شش خط کد اضافی دارد.
در هر تکرار از حلقه، ستون و ردیف با اندیس i
طوری پیکربندی میشوند که وزن (weight) آنها برابر با ۱ باشد. این موضوع تضمین میکند که ستون و ردیف با همان نرخ رشد کنند، هرگاه که اندازه پنجره تغییر کند. آرگومان minsize
برای هر ستون برابر با ۷۵ و برای هر ردیف برابر با ۵۰ تنظیم شده است. این باعث میشود که ویجت Label
همیشه متن خود را کامل نمایش دهد و هیچ کاراکتری حتی در کوچک ترین اندازه پنجره قطع نشود.
نتیجه، یک چیدمان جدولی (grid layout) است که هنگام تغییر اندازه پنجره بهصورت روان بزرگ و کوچک میشود:
امتحان کنید تا نحوه عملکرد را بهتر درک کنید! با پارامترهای weight
و minsize
بازی کنید تا ببینید چگونه روی جدول تأثیر میگذارند.
بهصورت پیشفرض، ویجت ها در مرکز خانه های جدول قرار میگیرند. بهعنوان مثال، کد زیر دو ویجت Label
ایجاد میکند و آنها را در جدولی با یک ستون و دو ردیف قرار میدهد:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import tkinter as tk window = tk.Tk() window.columnconfigure(0, minsize=250) window.rowconfigure([0, 1], minsize=100) label1 = tk.Label(text="A") label1.grid(row=0, column=0) label2 = tk.Label(text="B") label2.grid(row=1, column=0) window.mainloop() |
هر خانه جدول دارای عرض ۲۵۰ پیکسل و ارتفاع ۱۰۰ پیکسل است. همانطور که در تصویر زیر مشخص است، Label
ها در مرکز هر سلول قرار دارند:
شما میتوانید مکان هر Label
درون خانه جدول را با استفاده از پارامتر sticky
تغییر دهید. این پارامتر رشته ای از یک یا چند حرف زیر را میپذیرد:
-
"n"
یا"N"
برای چسباندن به قسمت بالای وسط سلول (north) -
"e"
یا"E"
برای چسباندن به سمت راست وسط سلول (east) -
"s"
یا"S"
برای چسباندن به پایین وسط سلول (south) -
"w"
یا"W"
برای چسباندن به سمت چپ وسط سلول (west)
حروف "n"
، "s"
، "e"
و "w"
برگرفته از جهات اصلی شمال، جنوب، شرق و غرب هستند. اگر در کد قبلی، پارامتر sticky
را برای هر دو Label
برابر "n"
قرار دهید، هر Label
در قسمت بالای وسط سلول خود قرار میگیرد.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import tkinter as tk window = tk.Tk() window.columnconfigure(0, minsize=250) window.rowconfigure([0, 1], minsize=100) label1 = tk.Label(text="A") label1.grid(row=0, column=0, sticky="n") label2 = tk.Label(text="B") label2.grid(row=1, column=0, sticky="n") window.mainloop() |
شما میتوانید چند حرف را در یک رشته با هم ترکیب کنید تا Label
ها را در گوشه های سلول های جدول قرار دهید:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import tkinter as tk window = tk.Tk() window.columnconfigure(0, minsize=250) window.rowconfigure([0, 1], minsize=100) label1 = tk.Label(text="A") label1.grid(row=0, column=0, sticky="ne") label2 = tk.Label(text="B") label2.grid(row=1, column=0, sticky="sw") window.mainloop() |
در این مثال، پارامتر sticky
برای label1
برابر با "ne"
قرار داده شده است که باعث میشود لیبل در گوشه بالا-راست سلول قرار گیرد. label2
نیز با مقدار "sw"
در پایین-چپ سلول قرار میگیرد. شکل پنجره به این صورت خواهد بود:
زمانی که یک ویجت با استفاده از sticky
جایگذاری میشود، اندازهی خود ویجت فقط بهاندازه ای خواهد بود که محتوای داخلی آن (مثل متن) را در خود جای دهد. یعنی ویجت کل سلول جدول را پر نمیکند. اگر میخواهید ویجت تمام فضای سلول را بپوشاند، میتوانید از "ns"
برای پر کردن سلول در جهت عمودی، یا "ew"
برای پر کردن در جهت افقی استفاده کنید. برای پر کردن کامل سلول در هر دو جهت، sticky
را روی "nsew"
قرار دهید.
مثال زیر هر یک از این گزینه ها را نمایش میدهد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import tkinter as tk window = tk.Tk() window.rowconfigure(0, minsize=50) window.columnconfigure([0, 1, 2, 3], minsize=50) label1 = tk.Label(text="1", bg="black", fg="white") label2 = tk.Label(text="2", bg="black", fg="white") label3 = tk.Label(text="3", bg="black", fg="white") label4 = tk.Label(text="4", bg="black", fg="white") label1.grid(row=0, column=0) label2.grid(row=0, column=1, sticky="ew") label3.grid(row=0, column=2, sticky="ns") label4.grid(row=0, column=3, sticky="nsew") window.mainloop() |
این مثال نشان میدهد که پارامتر sticky
در .grid()
میتواند همان کاری را انجام دهد که پارامتر fill
در .pack()
انجام میدهد. جدول زیر تطابق بین این دو را خلاصه میکند:
.grid() |
.pack() |
---|---|
sticky="ns" |
fill=tk.Y |
sticky="ew" |
fill=tk.X |
sticky="nsew" |
fill=tk.BOTH |
.grid()
یک مدیر چیدمان قدرتمند است. اغلب درک آن نسبت به .pack()
سادهتر و نسبت به .place()
بسیار انعطافپذیرتر است. زمانی که در حال ساخت برنامههای جدید با Tkinter هستید، بهتر است از .grid()
بهعنوان مدیر چیدمان اصلی خود استفاده کنید.
نکته:
.grid()
انعطاف پذیری بسیار بیشتری نسبت به آنچه تا حالا دیدید دارد. به عنوان مثال، میتوانید سلول هایی را تنظیم کنید که چند ردیف یا چند ستون را اشغال کنند. برای اطلاعات بیشتر، به بخش Grid Geometry Manager در آموزش TkDocs مراجعه کنید.
حالا که اصول اولیه مدیران چیدمان (Geometry Managers) در فریم ورک گرافیکی Tkinter را یاد گرفتید، قدم بعدی این است که دکمه ها را طوری تنظیم کنید که با کلیک روی آن ها، عملیات خاصی در برنامه اجرا بشود.
تعاملی کردن برنامه ها در Tkinter
تا این مرحله، با نحوه ایجاد یک پنجره در کتابخانه Tkinter، افزودن ویجت ها و مدیریت چیدمان آن ها آشنا شده اید. این موارد، گام های مهمی در طراحی واسط کاربری به شمار میروند. با این حال، تنها زیبایی ظاهری کافی نیست؛ برنامه ها باید دارای عملکرد نیز باشند. در این بخش، با نحوه زنده کردن برنامه های خود از طریق اجرای عملیات در پاسخ به رخدادهای مختلف آشنا خواهید شد.
استفاده از رویدادها و توابع پاسخ دهنده به رویداد (Event Handlers)
هنگامی که یک برنامه Tkinter ایجاد میکنید، باید با فراخوانی window.mainloop()
، حلقه رویداد را آغاز نمایید. در طول اجرای این حلقه، برنامه به طور پیوسته بررسی میکند که آیا رویدادی اتفاق افتاده است یا خیر. در صورت وقوع یک رویداد، کدی که برای واکنش به آن نوشتهاید اجرا خواهد شد.
حلقه رویداد توسط خود Tkinter در اختیار شما قرار میگیرد و نیازی به نوشتن کدی برای بررسی رویدادها ندارید. با این حال، شما موظف هستید تابعی را بنویسید که در پاسخ به رویداد اجرا شود. در Tkinter، این توابع به عنوان «پاسخدهنده به رویداد» یا event handler
شناخته میشوند.
نکته: رویداد، هر کنشی است که در جریان حلقه رویداد رخ میدهد و میتواند به اجرای رفتاری در برنامه منجر شود؛ مانند فشرده شدن کلید یا کلیک روی دکمه ماوس.
هنگامی که یک رویداد رخ میدهد، شیئی از نوع رویداد ساخته میشود. این فرآیند توسط Tkinter انجام میشود و نیازی به ساخت دستی آن توسط برنامهنویس نیست.
در ادامه برای درک بهتر نحوه عملکرد حلقه رویداد در Tkinter، یک حلقه رویداد مفهومی را به صورت دستی پیاده سازی میکنیم. این کار به شما در درک بهتر مکانیزم داخلی Tkinter کمک خواهد کرد و مشخص میکند که کدام بخش ها باید توسط شما نوشته شوند.
فرض کنید لیستی به نام events
وجود دارد که شامل اشیای رویداد است. هر بار که رویدادی در برنامه رخ دهد، به صورت خودکار یک شیء به این لیست افزوده میشود. شما مسئول پیاده سازی این مکانیسم نیستید؛ بلکه صرفاً باید نحوه واکنش به رویدادها را تعریف نمایید. در مثال زیر، با استفاده از یک حلقه بینهایت میتوان بررسی کرد که آیا رویدادی در لیست وجود دارد یا خیر:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Assume that this list gets updated automatically events = [] # Run the event loop while True: # If the event list is empty, then no events have occurred # and you can skip to the next iteration of the loop if events == []: continue # If execution reaches this point, then there is at least one # event object in the event list event = events[0] |
در حال حاضر، حلقه رویدادی که ایجاد کردهاید، هیچ واکنشی نسبت به اشیای رویداد (event) نشان نمیدهد. بیایید این رفتار را تغییر دهیم. فرض کنید برنامه شما باید به فشرده شدن کلیدهای صفحهکلید پاسخ دهد. در این حالت، باید بررسی کنید که آیا رویداد موردنظر از نوع فشردن کلید (keypress) بوده است یا خیر. در صورت تأیید، باید شیء رویداد را به تابعی ارسال نمایید که برای مدیریت این نوع رویداد طراحی شده است.
در این سناریو فرض میکنیم شیء event
دارای ویژگی .type
باشد که مقدار آن، در صورت فشرده شدن یک کلید، برابر با رشتهی "keypress"
خواهد بود. همچنین، ویژگی .char
نیز وجود دارد که کاراکتر مربوط به کلید فشردهشده را در خود نگه میدارد.
در ادامه، یک تابع به نام handle_keypress()
ایجاد میکنیم و کد مربوط به حلقه رویداد را بهروزرسانی مینماییم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
events = [] # Create an event handler def handle_keypress(event): """Print the character associated to the key pressed""" print(event.char) while True: if events == []: continue event = events[0] # If event is a keypress event object if event.type == "keypress": # Call the keypress event handler handle_keypress(event) |
وقتی window.mainloop()
را فراخوانی میکنید، چیزی شبیه به حلقه بالا برای شما اجرا میشود. این متد دو بخش از حلقه را برای شما مدیریت میکند:
-
لیستی از رویدادهایی که رخ دادهاند را نگهداری میکند.
-
هر زمان که یک رویداد جدید به این لیست اضافه شود، یک تابع مدیریت رویداد (event handler) را اجرا میکند.
حلقه رویداد خود را بهروزرسانی کنید تا بهجای حلقه دستی، از window.mainloop()
استفاده کند:
1 2 3 4 5 6 7 8 9 10 11 12 |
import tkinter as tk # Create a window object window = tk.Tk() # Create an event handler def handle_keypress(event): """Print the character associated to the key pressed""" print(event.char) # Run the event loop window.mainloop() |
.mainloop()
کارهای زیادی را برای شما انجام میدهد، اما یک نکته در کد بالا جا افتاده است. چگونه Tkinter متوجه میشود که چه زمانی باید از handle_keypress()
استفاده کند؟ ویجتهای Tkinter متدی به نام .bind()
دارند که دقیقاً برای همین منظور طراحی شده است.
استفاده از .bind()
برای فراخوانی یک تابع مدیریت رویداد (event handler) در هر بار وقوع یک رویداد روی یک ویجت، از متد .bind()
استفاده میشود. گفته میشود که تابع مدیریت رویداد به رویداد «بسته شده» است، زیرا هر بار که رویداد رخ میدهد، این تابع فراخوانی میشود. در ادامه مثال مربوط به فشردن کلید از بخش قبلی، از .bind()
استفاده میکنید تا تابع handle_keypress()
را به رویداد فشردن کلید متصل کنید:
1 2 3 4 5 6 7 8 9 10 11 12 |
import tkinter as tk window = tk.Tk() def handle_keypress(event): """Print the character associated to the key pressed""" print(event.char) # Bind keypress event to handle_keypress() window.bind("<Key>", handle_keypress) window.mainloop() |
در اینجا، تابع handle_keypress()
با استفاده از window.bind()
به رویداد "<Key>"
متصل شده است. هر زمان که کلیدی در حین اجرای برنامه فشرده شود، برنامه نویسه مربوط به آن کلید را چاپ خواهد کرد.
نکته: خروجی این برنامه در پنجره رابط گرافیکی Tkinter چاپ نمیشود. این خروجی در جریان استاندارد خروجی (stdout) نمایش داده میشود.
اگر برنامه را در محیط IDLE اجرا کنید، خروجی در پنجره تعاملی نمایش داده میشود. اگر برنامه را از طریق ترمینال اجرا نمایید، خروجی را در ترمینال مشاهده خواهید کرد.
متد .bind()
همواره حداقل دو آرگومان دریافت میکند:
-
رویدادی که بهصورت رشته ای به شکل
"<event_name>"
نمایش داده میشود.event_name
میتواند هر یک از رویدادهای پشتیبانی شده توسط Tkinter باشد. -
یک تابع مدیریت رویداد (event handler) که نام تابعی است که در هنگام وقوع رویداد فراخوانی خواهد شد.
تابع مدیریت رویداد به ویجتی متصل میشود که .bind()
روی آن فراخوانی شده است. هنگامی که تابع مدیریت رویداد اجرا میشود، شیء رویداد بهعنوان آرگومان به آن تابع ارسال میگردد.
در مثال بالا، تابع مدیریت رویداد به خود پنجره متصل شده بود. اما شما میتوانید تابع مدیریت رویداد را به هر ویجتی در برنامهتان متصل کنید. برای مثال، میتوانید یک تابع را به ویجت دکمه (Button) متصل نمایید تا در صورت کلیک روی دکمه، عملی انجام شود:
1 2 3 4 5 6 |
def handle_click(event): print("The button was clicked!") button = tk.Button(text="Click me!") button.bind("<Button-1>", handle_click) |
در این مثال، رویداد "<Button-1>"
روی ویجت دکمه به تابع مدیریت رویداد handle_click
متصل شده است. رویداد "<Button-1>"
زمانی رخ میدهد که دکمه چپ ماوس در حالی که نشانگر روی ویجت قرار دارد، فشرده شود. رویدادهای دیگری نیز برای کلیک ماوس وجود دارد، از جمله:
-
"<Button-2>"
برای کلیک دکمه میانی ماوس -
"<Button-3>"
برای کلیک دکمه راست ماوس
نکته: برای مشاهده فهرستی از رویدادهای پرکاربرد، به بخش Event types در مستندات Tkinter نسخهی ۸.۵ مراجعه کنید.
شما میتوانید هر تابع مدیریت رویدادی را با استفاده از .bind()
به هر نوع ویجتی متصل کنید، اما برای دکمه ها (Button)، روش ساده تری نیز وجود دارد و آن استفاده از ویژگی command
در هنگام تعریف دکمه است.
استفاده از command
هر ویجت دکمه (Button) در Tkinter دارای ویژگی به نام command
است که میتوانید آن را به یک تابع نسبت دهید. هر زمان که دکمه فشرده شود، تابع مورد نظر اجرا خواهد شد.
به مثال زیر توجه کنید. ابتدا یک پنجره با یک ویجت Label
ایجاد میکنید که یک مقدار عددی را نمایش میدهد. سپس دو دکمه در سمت چپ و راست این Label قرار میدهید. دکمه سمت چپ برای کاهش مقدار و دکمه سمت راست برای افزایش مقدار استفاده میشود.
در ادامه، کد مربوط به این پنجره آورده شده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import tkinter as tk window = tk.Tk() window.rowconfigure(0, minsize=50, weight=1) window.columnconfigure([0, 1, 2], minsize=50, weight=1) btn_decrease = tk.Button(master=window, text="-") btn_decrease.grid(row=0, column=0, sticky="nsew") lbl_value = tk.Label(master=window, text="0") lbl_value.grid(row=0, column=1) btn_increase = tk.Button(master=window, text="+") btn_increase.grid(row=0, column=2, sticky="nsew") window.mainloop() |
اکنون که چیدمان برنامه تعریف شده است، میتوانید با اختصاص دادن دستوراتی به دکمه ها، آن را تعاملی کنید. با دکمه سمت چپ شروع میکنیم. هنگامی که این دکمه فشرده میشود، باید مقدار موجود در Label
را یک واحد کاهش دهد. برای این منظور ابتدا باید به دو سؤال پاسخ داده شود:
-
چگونه میتوان متن موجود در
Label
را دریافت کرد؟ -
چگونه میتوان متن موجود در
Label
را بهروزرسانی کرد؟
ویجت های Label
متدی به نام .get()
مانند ویجت های Entry
یا Text
ندارند. با این حال، میتوانید متن آنها را با استفاده از نحوه نوشتار به سبک دیکشنری دریافت یا تنظیم کنید:
1 2 3 4 5 6 7 |
label = tk.Label(text="Hello") # Retrieve a label's text text = label["text"] # Set new text for the label label["text"] = "Good bye" |
Label
را دریافت و تنظیم کنید، تابعی به نام increase()
بنویسید که مقدار موجود در lbl_value
را یک واحد افزایش دهد:
1 2 3 4 5 6 7 |
import tkinter as tk def increase(): value = int(lbl_value["text"]) lbl_value["text"] = f"{value + 1}" # ... |
تابع increase()
ابتدا متن موجود در lbl_value
را دریافت کرده و آن را به عدد صحیح تبدیل میکند. سپس مقدار را یک واحد افزایش داده و متن Label
را با مقدار جدید جایگزین میکند.
همچنین به تابعی به نام decrease()
نیاز دارید تا مقدار موجود در lbl_value
را یک واحد کاهش دهد:
1 2 3 4 5 6 7 |
# ... def decrease(): value = int(lbl_value["text"]) lbl_value["text"] = f"{value - 1}" # ... |
increase()
و decrease()
را درست پس از دستور import
در کد خود قرار دهید.
برای اتصال دکمه ها به توابع، کافی است تابع مورد نظر را به ویژگی command
دکمه اختصاص دهید. این کار را میتوانید هنگام ایجاد دکمه ها انجام دهید. به عنوان مثال، دو خطی که دکمه ها را ایجاد میکنند، به شکل زیر بهروزرسانی کنید:
1 2 3 4 5 6 7 8 9 10 11 12 |
# ... btn_decrease = tk.Button(master=window, text="-", command=decrease) btn_decrease.grid(row=0, column=0, sticky="nsew") lbl_value = tk.Label(master=window, text="0") lbl_value.grid(row=0, column=1) btn_increase = tk.Button(master=window, text="+", command=increase) btn_increase.grid(row=0, column=2, sticky="nsew") window.mainloop() |
همین کافی است تا دکمه ها را به توابع increase()
و decrease()
متصل کرده و برنامه را به یک برنامه کاربردی تبدیل کنید. تغییرات خود را ذخیره کرده و برنامه را اجرا کنید! با کلیک روی دکمه ها، مقدار عددی در وسط پنجره افزایش یا کاهش مییابد.
کد کامل برنامه برای شما در اینجا آمده است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import tkinter as tk def increase(): value = int(lbl_value["text"]) lbl_value["text"] = f"{value + 1}" def decrease(): value = int(lbl_value["text"]) lbl_value["text"] = f"{value - 1}" window = tk.Tk() window.rowconfigure(0, minsize=50, weight=1) window.columnconfigure([0, 1, 2], minsize=50, weight=1) btn_decrease = tk.Button(master=window, text="-", command=decrease) btn_decrease.grid(row=0, column=0, sticky="nsew") lbl_value = tk.Label(master=window, text="0") lbl_value.grid(row=0, column=1) btn_increase = tk.Button(master=window, text="+", command=increase) btn_increase.grid(row=0, column=2, sticky="nsew") window.mainloop() |
این برنامه شاید کاربرد خاصی نداشته باشد، اما مهارتهایی که در اینجا آموختید، در تمام برنامه هایی که خواهید ساخت کاربرد دارند:
-
از ویجت ها برای ایجاد اجزای رابط کاربری استفاده کنید.
-
از مدیرهای چیدمان (geometry managers) برای کنترل نحوه چیدمان اجزای برنامه بهره ببرید.
-
رویداد هایی (event handlers) بنویسید که با اجزای مختلف تعامل داشته باشند تا ورودی کاربر را دریافت و پردازش کنند.
در دو بخش بعدی، برنامه های کاربردیتری خواهید ساخت. ابتدا یک مبدل دما طراحی میکنید که مقدار دما را از فارنهایت به سلسیوس تبدیل میکند. پس از آن، یک ویرایشگر متن خواهید ساخت که امکان باز کردن، ویرایش و ذخیره فایل های متنی را فراهم میکند.
آیا مایلید همین حالا با پروژه مبدل دما شروع کنیم؟
ساخت یک مبدل دما
در این بخش، یک اپلیکیشن ساده برای تبدیل دما میسازید. این برنامه به کاربر اجازه میدهد که مقدار دما را بر حسب درجه فارنهایت وارد کرده و با فشردن یک دکمه، آن را به درجه سلسیوس (سانتیگراد) تبدیل کند. مراحل کدنویسی این برنامه را مرحله به مرحله بررسی خواهیم کرد. در پایان این بخش نیز میتوانید نسخه کامل کد را مشاهده کنید.
نکته: برای بهرهمندی بهتر از این آموزش، توصیه میشود مراحل را در محیط Python اجرا کنید.
پیش از شروع کدنویسی، ابتدا رابط کاربری اپلیکیشن را طراحی میکنیم. به سه عنصر اصلی نیاز داریم:
-
Entry: یک ویجت با نام
ent_temperature
برای وارد کردن مقدار فارنهایت -
Label: یک ویجت با نام
lbl_result
برای نمایش نتیجه تبدیلشده به سلسیوس -
Button: یک ویجت با نام
btn_convert
که هنگام کلیک، مقدار را از Entry میخواند، آن را تبدیل کرده و متن Label را به نتیجه تبدیلشده تغییر میدهد
این اجزا را میتوانید به صورت یک سطر و سه ستون در یک جدول (Grid) قرار دهید. البته برای کاربرپسند بودن، بهتر است هرکدام برچسب مناسبی داشته باشند.
برای مثال، کنار ویجت ent_temperature
یک برچسب قرار میدهید که علامت فارنهایت (℉) را نشان دهد تا مشخص شود مقدار ورودی باید به فارنهایت باشد. برای این کار، متن برچسب را برابر با "\N{DEGREE FAHRENHEIT}"
قرار میدهید که از قابلیت Unicode در پایتون برای نمایش این علامت استفاده میکند.
برای زیباتر شدن دکمه تبدیل، میتوانید متن آن را برابر "\N{RIGHTWARDS BLACK ARROW}"
بگذارید که یک فلش سیاه به سمت راست نمایش میدهد. همچنین مطمئن میشوید که برچسب lbl_result
همیشه شامل علامت سلسیوس (℃) پس از متن نتیجه باشد. برای این منظور، از "\N{DEGREE CELSIUS}"
استفاده میکنید.
در پایان، ظاهر نهایی پنجره به صورت زیر خواهد بود:
حالا که میدانید به چه ویجت هایی نیاز دارید و ظاهر پنجره چگونه خواهد بود، میتوانید کدنویسی برنامه را آغاز کنید! ابتدا ماژول tkinter
را وارد کرده و یک پنجره جدید ایجاد کنید:
1 2 3 4 5 |
import tkinter as tk window = tk.Tk() window.title("Temperature Converter") window.resizable(width=False, height=False) |
تابع window.title()
عنوان پنجره را روی «Temperature Converter» قرار میدهد. همچنین، با window.resizable(False, False)
اندازه پنجره ثابت میماند و قابل تغییر توسط کاربر نخواهد بود.
در مرحله بعد، ویجت ent_temperature
را برای ورود مقدار دما تعریف میکنیم. برای مشخص کردن اینکه این مقدار بر حسب فارنهایت است، یک برچسب به نام lbl_temp
در کنار آن قرار میدهیم. این دو ویجت را در یک Frame
به نام frm_entry
قرار میدهیم تا گروهبندی شوند:
1 2 3 4 5 |
# ... frm_entry = tk.Frame(master=window) ent_temperature = tk.Entry(master=frm_entry, width=10) lbl_temp = tk.Label(master=frm_entry, text="\N{DEGREE FAHRENHEIT}") |
کاربر مقدار دمای مورد نظر را در ent_temperature
وارد میکند و برچسب lbl_temp
بهوضوح نشان میدهد که این مقدار بر حسب فارنهایت است.
برای اینکه lbl_temp
دقیقاً در سمت راست ent_temperature
قرار گیرد، از مدیریت موقعیت .grid()
استفاده میکنیم و هر ویجت را در یک ستون جداگانه از یک سطر قرار میدهیم:
1 2 3 4 |
# ... ent_temperature.grid(row=0, column=0, sticky="e") lbl_temp.grid(row=0, column=1, sticky="w") |
پارامتر sticky="e"
باعث میشود که ent_temperature
به لبه راست سلول خود بچسبد، و sticky="w"
برای lbl_temp
باعث میشود که به لبه چپ سلول خود بچسبد. این تنظیمات تضمین میکند که این دو ویجت در کنار هم و منظم قرار بگیرند.
اکنون نوبت ساخت دکمه btn_convert
برای تبدیل دما و برچسب lbl_result
برای نمایش نتیجه است:
1 2 3 4 5 6 7 |
# ... btn_convert = tk.Button( master=window, text="\N{RIGHTWARDS BLACK ARROW}" ) lbl_result = tk.Label(master=window, text="\N{DEGREE CELSIUS}") |
frm_entry
، دو ویجت btn_convert
و lbl_result
نیز مستقیماً به پنجره اصلی (window
) متصل میشوند. این سه ویجت در مجموع، سه سلول اصلی از جدول (grid) رابط کاربری شما را تشکیل میدهند. حالا با استفاده از متد .grid()
آنها را در پنجره قرار دهید:
1 2 3 4 5 |
# ... frm_entry.grid(row=0, column=0, padx=10) btn_convert.grid(row=0, column=1, pady=10) lbl_result.grid(row=0, column=2, padx=10) |
window.mainloop()
آغاز کنید:
1 2 3 |
# ... window.mainloop() |
fahrenheit_to_celsius()
اضافه کنید.
1 2 3 4 5 6 7 8 9 10 11 |
import tkinter as tk def fahrenheit_to_celsius(): """Convert the value for Fahrenheit to Celsius and insert the result into lbl_result. """ fahrenheit = ent_temperature.get() celsius = (5 / 9) * (float(fahrenheit) - 32) lbl_result["text"] = f"{round(celsius, 2)} \N{DEGREE CELSIUS}" # ... |
این تابع مقدار واردشده در ent_temperature
را میخواند، آن را از فارنهایت به سلسیوس تبدیل میکند و سپس نتیجه را در lbl_result
نمایش میدهد.
در ادامه، به خطی که دکمه btn_convert
را تعریف کردهاید بروید و ویژگی command
آن را برابر با تابع fahrenheit_to_celsius
قرار دهید:
1 2 3 4 5 6 7 8 9 |
# ... btn_convert = tk.Button( master=window, text="\N{RIGHTWARDS BLACK ARROW}", command=fahrenheit_to_celsius # <--- Add this line ) # ... |
همین! شما با موفقیت یک برنامه مبدل دمای کامل را تنها در ۲۶ خط کد ساختهاید! جالب است، نه؟
میتوانید کد کامل برنامه را در زیر ببینید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import tkinter as tk def fahrenheit_to_celsius(): """Convert the value for Fahrenheit to Celsius and insert the result into lbl_result. """ fahrenheit = ent_temperature.get() celsius = (5 / 9) * (float(fahrenheit) - 32) lbl_result["text"] = f"{round(celsius, 2)} \N{DEGREE CELSIUS}" # Set up the window window = tk.Tk() window.title("Temperature Converter") window.resizable(width=False, height=False) # Create the Fahrenheit entry frame with an Entry # widget and label in it frm_entry = tk.Frame(master=window) ent_temperature = tk.Entry(master=frm_entry, width=10) lbl_temp = tk.Label(master=frm_entry, text="\N{DEGREE FAHRENHEIT}") # Layout the temperature Entry and Label in frm_entry # using the .grid() geometry manager ent_temperature.grid(row=0, column=0, sticky="e") lbl_temp.grid(row=0, column=1, sticky="w") # Create the conversion Button and result display Label btn_convert = tk.Button( master=window, text="\N{RIGHTWARDS BLACK ARROW}", command=fahrenheit_to_celsius ) lbl_result = tk.Label(master=window, text="\N{DEGREE CELSIUS}") # Set up the layout using the .grid() geometry manager frm_entry.grid(row=0, column=0, padx=10) btn_convert.grid(row=0, column=1, pady=10) lbl_result.grid(row=0, column=2, padx=10) # Run the application window.mainloop() |
ساخت ویرایشگر متن
در این بخش، یک برنامه ویرایشگر متن طراحی میکنید که میتواند فایل های متنی را ایجاد، باز، ویرایش و ذخیره کند. این برنامه شامل سه عنصر اصلی است:
-
یک ویجت دکمه (Button) به نام
btn_open
برای باز کردن فایل جهت ویرایش -
یک ویجت دکمه (Button) به نام
btn_save
برای ذخیره سازی فایل -
یک ویجت جعبه متن (TextBox) به نام
txt_edit
برای ایجاد و ویرایش محتوای فایل
نحوه چیدمان این سه ویجت به این صورت است:
دو دکمه در سمت چپ پنجره قرار میگیرند، و جعبه متن در سمت راست پنجره. کل پنجره باید حداقل ارتفاع ۸۰۰ پیکسل داشته باشد، و txt_edit
نیز باید حداقل عرض ۸۰۰ پیکسل را داشته باشد.
نکته مهم: طراحی باید واکنش گرا (responsive) باشد. یعنی اگر کاربر پنجره را تغییر اندازه دهد، تنها txt_edit
تغییر اندازه دهد، نه قاب دکمه ها. عرض قاب دکمه ها باید ثابت باقی بماند.
در ادامه طرح کلی ظاهر این برنامه آمده است:
برای رسیدن به چیدمان موردنظر، میتوانید از مدیر چیدمان .grid()
استفاده کنید. این چیدمان شامل یک ردیف و دو ستون است:
-
ستونی باریک در سمت چپ برای دکمه ها
-
ستونی پهن تر در سمت راست برای جعبه متن (
txt_edit
)
برای اینکه پنجره و txt_edit
حداقل اندازه ۸۰۰ پیکسل داشته باشند، میتوانید از متدهای .rowconfigure()
و .columnconfigure()
روی پنجره استفاده کنید و پارامتر minsize
را برابر ۸۰۰ قرار دهید. همچنین برای اینکه txt_edit
با تغییر اندازه پنجره، تغییر اندازه دهد (واکنش گرا باشد)، باید پارامتر weight
را روی ۱ تنظیم کنید.
برای قرار دادن دکمه ها در یک ستون، نیاز دارید که یک ویجت فریم (Frame) به نام frm_buttons
بسازید. طبق طرح شماتیک، دو دکمه باید بهصورت عمودی در این قاب قرار بگیرند، بهطوریکه btn_open
در بالا باشد. برای این کار، میتوانید از .grid()
یا .pack()
استفاده کنید. در حال حاضر، از .grid()
استفاده میکنیم چون کار با آن آسانتر است و کنترل بیشتری میدهد.
حالا که برنامهریزی انجام شد، میتوانیم کدنویسی برنامه را آغاز کنیم. اولین گام، ایجاد تمام ویجت های موردنیاز است.
1 2 3 4 5 6 7 8 9 10 11 12 |
import tkinter as tk window = tk.Tk() window.title("Simple Text Editor") window.rowconfigure(0, minsize=800, weight=1) window.columnconfigure(1, minsize=800, weight=1) txt_edit = tk.Text(window) frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2) btn_open = tk.Button(frm_buttons, text="Open") btn_save = tk.Button(frm_buttons, text="Save As...") |
در اینجا شرحی از کد آمده است:
-
خط ۱: کتابخانه
tkinter
را وارد میکند. -
خطوط ۳ و ۴: یک پنجره جدید ایجاد کرده و عنوان آن را “Simple Text Editor” میگذارند.
-
خطوط ۶ و ۷: تنظیمات مربوط به ردیف و ستون پنجره را انجام میدهند.
-
خطوط ۹ تا ۱۲: چهار ویجتی که لازم دارید ایجاد میکنند: یک جعبه متن (
txt_edit
)، یک فریم (frm_buttons
) و دو دکمه باز کردن (btn_open
) و ذخیره کردن (btn_save
).
بررسی دقیقتر خط ۶:
1 |
window.rowconfigure(0, minsize=800, weight=1) |
-
آرگومان اول
0
مشخص میکند که این تنظیمات برای اولین ردیف اعمال شود. -
minsize=800
یعنی حداقل ارتفاع این ردیف ۸۰۰ پیکسل خواهد بود. -
weight=1
باعث میشود این ردیف نسبت به تغییر اندازه پنجره، مقیاسپذیر باشد.
از آنجا که تنها یک ردیف در این رابط کاربری وجود دارد، این تنظیمات به کل پنجره اعمال میشوند.
بررسی دقیقتر خط ۷:
در این خط از .columnconfigure()
استفاده شده است تا برای ستون شماره ۱ (ستونی که txt_edit
در آن قرار میگیرد) تنظیماتی انجام شود:
1 |
window.columnconfigure(1, minsize=800, weight=1) |
-
minsize=800
تضمین میکند که حداقل عرض ستون دوم (ستونی که حاوی جعبه متن است)، ۸۰۰ پیکسل باشد. -
weight=1
باعث میشود این ستون نیز با تغییر اندازه پنجره، به صورت خودکار تغییر اندازه دهد.
به این ترتیب، فقط جعبه متن (نه دکمه ها) به صورت واکنش گرا رفتار خواهد کرد.
به یاد داشته باشید که اندیس های ردیف و ستون در tkinter از صفر شروع میشوند؛ بنابراین تنظیماتی که برای ستون شماره ۱ انجام دادید، فقط به دومین ستون (یعنی ستونی که جعبه متن در آن قرار دارد) اعمال میشود. از آنجا که فقط این ستون واکنش گرا شده، هنگام تغییر اندازه پنجره، فقط جعبه متن بهصورت پویا بزرگ و کوچک میشود، در حالی که ستونی که دکمه ها در آن هستند عرض ثابتی خواهد داشت.
حالا میتوانید روی چیدمان عناصر در اپلیکیشن کار کنید. ابتدا، دکمه ها را با استفاده از مدیریت چیدمان .grid()
داخل فریم frm_buttons
قرار دهید:
1 2 3 4 |
# ... btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5) btn_save.grid(row=1, column=0, sticky="ew", padx=5) |
توضیح کد:
-
این دو خط یک شبکه ۲ ردیفی و ۱ ستونی در قاب
frm_buttons
ایجاد میکنند. -
btn_open
در ردیف ۰ وbtn_save
در ردیف ۱ قرار میگیرد، بنابراین دکمه “باز کردن فایل” بالاتر از دکمه “ذخیره فایل” نمایش داده میشود، دقیقاً همانطور که در طرح اولیه پیشبینی کرده بودید. -
ویژگی
sticky="ew"
باعث میشود هر دکمه در جهت افقی (از شرق تا غرب) کشیده شود و کل عرض ستون را پر کند. -
padx
وpady
برای ایجاد فاصله داخلی در اطراف دکمه ها استفاده میشوند تا ظاهری مرتبتر داشته باشند.
در مرحله بعد، باید frm_buttons
و txt_edit
را در پنجره اصلی قرار دهید.
شما با تنظیم مقادیر padx
و pady
برابر با ۵، به هر دکمه پنج پیکسل فاصله داخلی (padding) از اطراف اختصاص میدهید. فقط دکمه btn_open
دارای فاصله عمودی (pady) است. از آنجا که این دکمه در بالای پنجره قرار دارد، این فاصله عمودی باعث میشود کمی از بالای پنجره پایینتر قرار بگیرد و در نتیجه فاصله کوچکی بین آن و دکمه btn_save
ایجاد شود.
حالا که بخش frm_buttons
(فریم دکمه ها) چیدمان شده و آماده است، میتوانید چیدمان شبکهای (grid layout) بقیه پنجره را تنظیم کنید:
1 2 3 4 |
# ... frm_buttons.grid(row=0, column=0, sticky="ns") txt_edit.grid(row=0, column=1, sticky="nsew") |
این دو خط کد یک شبکه با یک ردیف و دو ستون در پنجره ایجاد میکنند. بخش frm_buttons
را در ستون اول و txt_edit
را در ستون دوم قرار میدهید، بنابراین در چیدمان پنجره، frm_buttons
در سمت چپ txt_edit
نمایش داده میشود.
پارامتر sticky
برای frm_buttons
روی "ns"
تنظیم شده که باعث میشود این قاب به صورت عمودی کشیده شود و تمام ارتفاع ستون خود را پر کند. ویجت txt_edit
نیز با استفاده از پارامتر sticky="nsew"
در تمام جهات کشیده میشود و کل فضای اختصاصیافته در سلول شبکه اش را پر میکند.
اکنون که چیدمان رابط کاربری کامل شده است، دستور window.mainloop()
را در انتهای برنامه اضافه کرده، فایل را ذخیره کرده و اجرا کنید:
1 2 3 |
# ... window.mainloop() |
این عالی به نظر میرسد! اما در حال حاضر هیچ عملکردی ندارد، بنابراین باید شروع به نوشتن دستورات مربوط به دکمه ها کنید. دکمه btn_open
باید یک پنجره انتخاب فایل (File Open Dialog) نمایش دهد و به کاربر اجازه دهد یک فایل را انتخاب کند. سپس باید آن فایل را باز کند و محتوای آن را در ناحیه متنی txt_edit
نمایش دهد. تابع open_file()
زیر دقیقاً همین کار را انجام میدهد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import tkinter as tk def open_file(): """Open a file for editing.""" filepath = askopenfilename( filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")] ) if not filepath: return txt_edit.delete("1.0", tk.END) with open(filepath, mode="r", encoding="utf-8") as input_file: text = input_file.read() txt_edit.insert(tk.END, text) window.title(f"Simple Text Editor - {filepath}") # ... |
در ادامه، شرح خط به خط عملکرد این تابع آمده است:
-
خطوط ۷–۵: از تابع
askopenfilename()
که در ماژولtkinter.filedialog
قرار دارد، برای نمایش پنجره انتخاب فایل استفاده میشود. مسیر فایلی که کاربر انتخاب میکند، در متغیرfilepath
ذخیره میشود. -
خطوط ۸ و ۹: بررسی میشود که آیا کاربر پنجره را بسته یا روی دکمه “لغو” (Cancel) کلیک کرده است. در این صورت مقدار
filepath
خالی خواهد بود و تابع بدون انجام هیچ کاری بازمیگردد. -
خط ۱۰: محتوای فعلی
txt_edit
با استفاده از متد.delete()
پاک میشود. -
خطوط ۱۱ و ۱۲: فایل انتخابشده باز شده و محتوای آن با استفاده از متد
.read()
خوانده و به صورت یک رشته در متغیرtext
ذخیره میشود. -
خط ۱۳: رشته متنی
text
با متد.insert()
در ناحیه متنیtxt_edit
قرار داده میشود. -
خط ۱۴: عنوان پنجره بهروزرسانی میشود تا مسیر فایل باز شده در آن نمایش داده شود.
اکنون میتوانید برنامه را طوری بهروزرسانی کنید که دکمه btn_open
هنگام کلیک، تابع open_file()
را اجرا کند. برای این کار، ابتدا باید تابع askopenfilename()
را از ماژول tkinter.filedialog
وارد (import) کنید. بنابراین خط زیر را به ابتدای برنامه اضافه کنید:
1 2 3 4 |
import tkinter as tk from tkinter.filedialog import askopenfilename # ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import tkinter as tk from tkinter.filedialog import askopenfilename def open_file(): """Open a file for editing.""" filepath = askopenfilename( filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")] ) if not filepath: return txt_edit.delete("1.0", tk.END) with open(filepath, mode="r", encoding="utf-8") as input_file: text = input_file.read() txt_edit.insert(tk.END, text) window.title(f"Simple Text Editor - {filepath}") window = tk.Tk() window.title("Simple Text Editor") window.rowconfigure(0, minsize=800, weight=1) window.columnconfigure(1, minsize=800, weight=1) txt_edit = tk.Text(window) frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2) btn_open = tk.Button(frm_buttons, text="Open", command=open_file) btn_save = tk.Button(frm_buttons, text="Save As...") # ... |
فایل را ذخیره کرده و آن را اجرا کنید تا مطمئن شوید همه چیز درست کار میکند. سپس یک فایل متنی را باز کنید تا نتیجه را ببینید!
اکنون که دکمه btn_open
بهدرستی عمل میکند، نوبت به پیاده سازی تابع مربوط به دکمه btn_save
رسیده است. این دکمه باید یک پنجره ذخیره فایل نمایش دهد تا کاربر بتواند محل و نام فایل خروجی را انتخاب کند. برای این کار، از تابع asksaveasfilename()
در ماژول tkinter.filedialog
استفاده خواهید کرد.
این تابع همچنین باید محتوای موجود در txt_edit
را استخراج کرده و آن را در فایل انتخاب شده توسط کاربر ذخیره کند. تابع زیر دقیقاً همین کار را انجام میدهد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# ... def save_file(): """Save the current file as a new file.""" filepath = asksaveasfilename( defaultextension=".txt", filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")], ) if not filepath: return with open(filepath, mode="w", encoding="utf-8") as output_file: text = txt_edit.get("1.0", tk.END) output_file.write(text) window.title(f"Simple Text Editor - {filepath}") # ... |
در ادامه، شرح عملکرد این کد آورده شده است:
-
خطوط ۲۲–۱۹: پنجره ذخیره فایل با استفاده از تابع
asksaveasfilename()
باز میشود تا مسیر مورد نظر برای ذخیره فایل را از کاربر دریافت کند. مسیر انتخاب شده در متغیرfilepath
ذخیره میشود. -
خطوط ۲۳ و ۲۴: بررسی میشود که آیا کاربر پنجره را بسته یا دکمه “لغو” (Cancel) را زده است. در این صورت،
filepath
خالی خواهد بود و تابع بدون انجام عملیات ذخیره، به پایان میرسد. -
خط ۲۵: فایل جدیدی در مسیر انتخابشده ایجاد میشود.
-
خط ۲۶: محتوای ناحیه متنی
txt_edit
با استفاده از متد.get()
استخراج شده و در متغیرtext
ذخیره میشود. -
خط ۲۷: متن استخراج شده در فایل خروجی نوشته میشود.
-
خط ۲۸: عنوان پنجره بهروزرسانی میشود تا مسیر فایل ذخیرهشده در آن نمایش داده شود.
اکنون میتوانید برنامه را بهروزرسانی کنید تا دکمه btn_save
هنگام کلیک، تابع save_file()
را اجرا کند. برای انجام این کار، چند مرحله را باید طی کنید.
ابتدا باید تابع asksaveasfilename()
را از ماژول tkinter.filedialog
وارد (import) کنید. برای این کار، بخش import در ابتدای اسکریپت را به شکل زیر بهروزرسانی کنید:
1 2 3 4 |
import tkinter as tk from tkinter.filedialog import askopenfilename, asksaveasfilename # ... |
command
دکمه btn_save
را طوری تنظیم کنید که هنگام کلیک، تابع save_file()
فراخوانی شود:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# ... window = tk.Tk() window.title("Simple Text Editor") window.rowconfigure(0, minsize=800, weight=1) window.columnconfigure(1, minsize=800, weight=1) txt_edit = tk.Text(window) frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2) btn_open = tk.Button(frm_buttons, text="Open", command=open_file) btn_save = tk.Button(frm_buttons, text="Save As...", command=save_file) btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5) btn_save.grid(row=1, column=0, sticky="ew", padx=5) frm_buttons.grid(row=0, column=0, sticky="ns") txt_edit.grid(row=0, column=1, sticky="nsew") window.mainloop() |
فایل را ذخیره کرده و اجرا کنید. اکنون شما یک ویرایشگر متن ساده اما کاملاً کاربردی در اختیار دارید!
در صورت تمایل، میتوانید بخش کد کامل برنامه را در زیر مشاهده کنید تا تصویر جامعتری از آن داشته باشید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import tkinter as tk from tkinter.filedialog import askopenfilename, asksaveasfilename def open_file(): """Open a file for editing.""" filepath = askopenfilename( filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")] ) if not filepath: return txt_edit.delete("1.0", tk.END) with open(filepath, mode="r", encoding="utf-8") as input_file: text = input_file.read() txt_edit.insert(tk.END, text) window.title(f"Simple Text Editor - {filepath}") def save_file(): """Save the current file as a new file.""" filepath = asksaveasfilename( defaultextension=".txt", filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")], ) if not filepath: return with open(filepath, mode="w", encoding="utf-8") as output_file: text = txt_edit.get("1.0", tk.END) output_file.write(text) window.title(f"Simple Text Editor - {filepath}") window = tk.Tk() window.title("Simple Text Editor") window.rowconfigure(0, minsize=800, weight=1) window.columnconfigure(1, minsize=800, weight=1) txt_edit = tk.Text(window) frm_buttons = tk.Frame(window, relief=tk.RAISED, bd=2) btn_open = tk.Button(frm_buttons, text="Open", command=open_file) btn_save = tk.Button(frm_buttons, text="Save As...", command=save_file) btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5) btn_save.grid(row=1, column=0, sticky="ew", padx=5) frm_buttons.grid(row=0, column=0, sticky="ns") txt_edit.grid(row=0, column=1, sticky="nsew") window.mainloop() |
نتیجه گیری
در این آموزش، با برنامه نویسی رابط کاربری گرافیکی (GUI) در پایتون آشنا شدید. کتابخانه Tkinter یکی از گزینه های مناسب برای ساخت رابط گرافیکی در پایتون است، زیرا بهصورت پیشفرض در کتابخانه استاندارد پایتون وجود دارد و ساخت برنامه ها با آن نسبتاً ساده و بدون دردسر است.
در طول این آموزش، با چند مفهوم کلیدی در Tkinter آشنا شدید:
-
نحوه کار با ابزارهای گرافیکی (ویجت ها)
-
مدیریت چیدمان اجزای برنامه با استفاده از مدیرهای چیدمان (Geometry Managers)
-
ایجاد تعامل و پاسخدهی برنامه به ورودی های کاربر
-
استفاده از پنج ویجت پایه Tkinter شامل:
Label
(برچسب)،Button
(دکمه)،Entry
(ورودی تک خطی)،Text
(ورودی چند خطی) وFrame
(فریم)
اکنون که اصول اولیه ساخت رابط گرافیکی با Tkinter را یاد گرفتهاید، قدم بعدی این است که پروژه های اختصاصی خودتان را بسازید و تمرین کنید. چه چیزی خلق خواهید کرد؟ پروژه های سرگرمکننده خود را در بخش نظرات به اشتراک بگذارید!
سوالات متداول
اکنون که با برنامه نویسی رابط گرافیکی (GUI) در پایتون با استفاده از Tkinter آشنا شدهاید، میتوانید با مرور سوالات زیر، درک خود را محک بزنید و آموخته هایتان را مرور کنید.
این سوالات متداول به مهمترین مفاهیمی مربوط میشوند که در این آموزش با آنها سروکار داشتهاید.
Tkinter در پایتون چیست و چرا استفاده میشود؟
Tkinter ابزار استاندارد ساخت رابط گرافیکی (GUI) در پایتون است که در کتابخانه استاندارد پایتون وجود دارد. این ابزار به شما اجازه میدهد برنامه های گرافیکی طراحی کنید که روی سیستم عامل های مختلف مانند ویندوز، مک و لینوکس اجرا شوند. Tkinter امکان ساخت عناصر رابط کاربری مانند دکمه ها، جعبه های متن و منو ها را فراهم میکند.
چگونه میتوان با استفاده از Tkinter یک پنجره ساده ایجاد کرد؟
برای ساخت یک پنجره ابتدایی در Tkinter، ابتدا باید ماژول tkinter
را import کنید، سپس یک نمونه از کلاس Tk
بسازید و در نهایت متد mainloop()
را فراخوانی کنید. این متد، حلقه رویدادها (event loop) را آغاز میکند و باعث میشود پنجره باز بماند تا زمانی که کاربر آن را ببندد.
مدیرهای چیدمان (Geometry Managers) در Tkinter چه هستند و چگونه کار میکنند؟
مدیرهای چیدمان مسئول کنترل نحوه قرارگیری ویجت ها (widgets) در پنجره هستند. سه مدیر اصلی چیدمان در Tkinter عبارتاند از:
-
.pack()
که ویجت ها را بهصورت بلوکی در پنجره والد قرار میدهد. -
.grid()
که ویجت ها را در قالب جدول، با استفاده از ردیف (row) و ستون (column) جای میدهد. -
.place()
که ویجت ها را در مکان دقیق و با مختصات مشخصشده قرار میدهد.
چگونه میتوان یک برنامه Tkinter را تعاملی کرد؟
برای ایجاد تعامل در برنامه های Tkinter، باید رویدادها (events) را به ویجت ها متصل کنید. برای دکمه ها معمولاً از ویژگی command
استفاده میشود تا با کلیک کاربر، یک تابع اجرا شود. برای دیگر رویدادها مانند فشردن کلید یا کلیک ماوس میتوان از متد .bind()
استفاده کرد.
برخی از ویجت های رایج در برنامه های Tkinter کداماند؟
-
Label
: برای نمایش متن یا تصویر -
Button
: دکمه ای که با کلیک کاربر میتواند یک تابع را اجرا کند -
Entry
: برای دریافت ورودی متنی یک خطی از کاربر -
Text
: برای دریافت یا نمایش متن چند خطی -
Frame
: برای گروه بندی و سازماندهی سایر ویجت ها در یک بخش مجزا از پنجره
راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.
- منبع : سورس باران
- رمز فايل : www.sourcebaran.com
- انتشار: ۱۶ اردیبهشت ۱۴۰۴
دسته بندی موضوعات
- آموزش ارز دیجیتال
- آموزش برنامه نویسی
- آموزش متنی برنامه نویسی
- اطلاعیه و سایر مطالب
- پروژه برنامه نویسی
- دوره های تخصصی برنامه نویسی
- رپورتاژ
- فیلم های آموزشی
- ++C
- ADO.NET
- Adobe Flash
- Ajax
- AngularJS
- apache
- ARM
- Asp.Net
- ASP.NET MVC
- AVR
- Bootstrap
- CCNA
- CCNP
- CMD
- CSS
- Dreameaver
- EntityFramework
- HTML
- IOS
- jquery
- Linq
- Mysql
- Oracle
- PHP
- PHPMyAdmin
- Rational Rose
- silver light
- SQL Server
- Stimulsoft Reports
- Telerik
- UML
- VB.NET&VB6
- WPF
- Xml
- آموزش های پروژه محور
- اتوکد
- الگوریتم تقریبی
- امنیت
- اندروید
- اندروید استودیو
- بک ترک
- بیسیک فور اندروید
- پایتون
- جاوا
- جاوا اسکریپت
- جوملا
- دلفی
- دوره آموزش Go
- دوره های رایگان پیشنهادی
- زامارین
- سئو
- ساخت CMS
- سی شارپ
- شبکه و مجازی سازی
- طراحی الگوریتم
- طراحی بازی
- طراحی وب
- فتوشاپ
- فریم ورک codeigniter
- فلاتر
- کانستراکت
- کریستال ریپورت
- لاراول
- معماری کامپیوتر
- مهندسی اینترنت
- هوش مصنوعی
- یونیتی
- کتاب های آموزشی
- Android
- ASP.NET
- AVR
- LINQ
- php
- Workflow
- اچ تی ام ال
- بانک اطلاعاتی
- برنامه نویسی سوکت
- برنامه نویسی موبایل
- پاسکال
- پایان نامه
- پایتون
- جاوا
- جاوا اسکریپت
- جی کوئری
- داده کاوی
- دلفی
- رباتیک
- سئو
- سایر کتاب ها
- سخت افزار
- سی اس اس
- سی پلاس پلاس
- سی شارپ
- طراحی الگوریتم
- فتوشاپ
- مقاله
- مهندسی نرم افزار
- هک و امنیت
- هوش مصنوعی
- ویژوال بیسیک
- نرم افزار و ابزار برنامه نویسی
- وردپرس