برنامه‌نویسی Curses با پایتون

نویسنده:

A.M. Kuchling, Eric S. Raymond

انتشار:

2.04

curses چیست؟

کتابخانه‌ی curses سازوکار ترسیم صفحه و مدیریت صفحه‌کلید مستقل از پایانه را برای پایانه‌های مبتنی بر متن فراهم می‌کند؛ چنین پایانه‌هایی شامل پایانه‌های VT100، کنسول لینوکس و پایانه شبیه‌سازی‌شده‌ای می‌شوند که برنامه‌های مختلف آن را فراهم می‌کنند. پایانه‌های نمایشی از کدهای کنترلی گوناگونی برای انجام عملیات رایج مانند جابه‌جایی مکان‌نما، پیمایش صفحه و پاک‌سازی ناحیه‌ها پشتیبانی می‌کنند. پایانه‌های مختلف از کدهای بسیار متفاوتی استفاده می‌کنند و اغلب رفتارهای خاص جزئی خود را دارند.

در دنیای نمایشگرهای گرافیکی، ممکن است کسی بپرسد «چرا زحمت بکشیم؟». درست است که پایانه‌های نمایشی مبتنی بر سلول‌های نویسه‌ای یک فناوری منسوخ هستند، اما حوزه‌هایی وجود دارند که در آن‌ها توانایی انجام کارهای پیشرفته با آن‌ها همچنان ارزشمند است. یکی از این حوزه‌ها، یونیکس‌های کم‌حجم یا نهفته هستند که کارساز X را اجرا نمی‌کنند. حوزه‌ی دیگر، ابزارهایی مانند نصب‌کننده‌های سیستم‌عامل و پیکربندی‌کننده‌های هسته هستند که ممکن است لازم باشد پیش از در دسترس بودن هرگونه پشتیبانی گرافیکی اجرا شوند.

کتابخانه‌ی curses قابلیت‌های نسبتاً پایه‌ای را فراهم می‌کند و انتزاعی از یک صفحه‌نمایش حاوی چند پنجره‌ی متنی بدون هم‌پوشانی را در اختیار برنامه‌نویس قرار می‌دهد. محتوای یک پنجره را می‌توان به روش‌های مختلفی تغییر داد — افزودن متن، پاک کردن آن، تغییر ظاهر آن — و کتابخانه‌ی curses تشخیص می‌دهد که چه کدهای کنترلی باید به پایانه ارسال شوند تا خروجی صحیح تولید شود. curses مفاهیم زیادی از رابط کاربری مانند دکمه‌ها، جعبه‌های انتخاب یا کادرهای محاوره را فراهم نمی‌کند؛ اگر به چنین امکاناتی نیاز دارید، یک کتابخانه‌ی رابط کاربری مانند Urwid را در نظر بگیرید.

کتابخانه curses در ابتدا برای BSD Unix نوشته شد؛ نسخه‌های بعدی Unix از نوع System V که از سوی AT&T ارائه شدند، بهبودها و توابع جدید بسیاری را افزودند. BSD curses دیگر نگهداری نمی‌شود و با ncurses جایگزین شده است؛ ncurses پیاده‌سازی متن‌بازی از رابط AT&T است. اگر از یک Unix متن‌باز مانند Linux یا FreeBSD استفاده می‌کنید، سیستم شما تقریباً به‌طور قطع از ncurses استفاده می‌کند. از آنجا که بیشتر نسخه‌های تجاری کنونی Unix مبتنی بر کد System V هستند، احتمالاً همه توابع توصیف‌شده در اینجا در دسترس خواهند بود. البته ممکن است نسخه‌های قدیمی‌تر curses موجود در برخی Unixهای اختصاصی از همه موارد پشتیبانی نکنند.

نسخه پایتون برای ویندوز شامل ماژول curses نمی‌شود. بسته شخص ثالث windows-curses همان رابط را در ویندوز فراهم می‌کند.

ماژول curses پایتون

ماژول پایتون، پوششی نسبتاً ساده بر روی توابع C ارائه‌شده توسط curses است؛ اگر از قبل با برنامه‌نویسی curses در C آشنا هستید، انتقال آن دانش به پایتون بسیار آسان است. بزرگ‌ترین تفاوت این است که رابط پایتون با ادغام توابع مختلف C مانند addstr()، mvaddstr() و mvwaddstr() در یک متد addstr() واحد، کارها را ساده‌تر می‌کند. این موضوع را بعداً با جزئیات بیشتری خواهید دید.

این HOWTO مقدمه‌ای است برای نوشتن برنامه‌های حالت متنی با curses و پایتون. این راهنما تلاش نمی‌کند راهنمای کاملی برای API curses باشد؛ برای آن، بخش مربوط به ncurses در راهنمای کتابخانه‌ی پایتون و صفحات راهنمای C برای ncurses را ببینید. با این حال، ایده‌های پایه را به شما ارائه می‌دهد.

شروع و پایان یک برنامه curses

پیش از انجام هر کاری، باید curses راه‌اندازی شود. این کار با فراخوانی تابع initscr() انجام می‌شود؛ این تابع نوع پایانه را مشخص می‌کند، کدهای راه‌اندازی لازم را به پایانه ارسال می‌کند و ساختارهای داده درونی مختلفی را ایجاد می‌کند. در صورت موفقیت، initscr() یک شیء پنجره را برمی‌گرداند که نمایانگر تمام صفحه است؛ این شیء معمولاً stdscr نامیده می‌شود، برگرفته از نام متغیر متناظر در C.

import curses
stdscr = curses.initscr()

معمولاً برنامه‌های curses بازتاب خودکار کلیدها در صفحه را خاموش می‌کنند، تا بتوانند کلیدها را بخوانند و تنها تحت شرایط خاصی آن‌ها را نمایش دهند. این کار نیازمند فراخوانی تابع noecho() است.

curses.noecho()

برنامه‌ها نیز معمولاً نیاز دارند که به کلیدها بلافاصله واکنش نشان دهند، بدون نیاز به فشرده شدن کلید Enter؛ به این حالت، حالت cbreak گفته می‌شود، در مقابل حالت معمول ورودی بافرشده.

curses.cbreak()

پایانه‌ها معمولاً کلیدهای خاصی، مانند کلیدهای مکان‌نما یا کلیدهای پیمایش مانند Page Up و Home را به‌صورت یک دنباله‌ی گریز چندبایتی برمی‌گردانند. اگرچه می‌توانستید برنامه‌ی خود را طوری بنویسید که چنین دنباله‌هایی را انتظار داشته باشد و آن‌ها را متناسب با آن پردازش کند، اما curses می‌تواند این کار را برای شما انجام دهد و مقدار خاصی مانند curses.KEY_LEFT را برگرداند. برای اینکه curses این کار را انجام دهد، باید حالت keypad را فعال کنید.

stdscr.keypad(True)

پایان دادن به یک برنامه‌ی curses بسیار آسان‌تر از آغاز آن است. باید آن را فراخوانی کنید:

curses.nocbreak()
stdscr.keypad(False)
curses.echo()

برای معکوس کردن تنظیمات پایانه سازگار با curses. سپس تابع endwin() را فراخوانی کنید تا پایانه به حالت عملیاتی اصلی خود بازگردد.

curses.endwin()

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

در پایتون می‌توانید با ایمپورت کردن تابع curses.wrapper() و استفاده از آن به این شکل، از این پیچیدگی‌ها اجتناب کنید و اشکال‌زدایی را بسیار آسان‌تر سازید:

from curses import wrapper

def main(stdscr):
    # Clear screen
    stdscr.clear()

    # This raises ZeroDivisionError when i == 10.
    for i in range(0, 11):
        v = i-10
        stdscr.addstr(i, 0, '10 divided by {} is {}'.format(v, 10/v))

        stdscr.refresh()
        stdscr.getkey()

wrapper(main)

تابع wrapper() یک شیء فراخوانی‌پذیر می‌گیرد، مقداردهی‌های اولیه‌ی توصیف‌شده در بالا را انجام می‌دهد و در صورت وجود پشتیبانی از رنگ، رنگ‌ها را نیز مقداردهی اولیه می‌کند. سپس wrapper() فراخوانی‌پذیر ارائه‌شده‌ی شما را اجرا می‌کند. پس از بازگشت فراخوانی‌پذیر، wrapper() وضعیت اصلی پایانه را بازیابی خواهد کرد. فراخوانی‌پذیر درون یک try...except فراخوانی می‌شود که استثناها را می‌گیرد، وضعیت پایانه را بازیابی می‌کند و سپس استثنا را دوباره پرتاب می‌کند. بنابراین در صورت بروز استثنا، پایانه‌ی شما در وضعیت نامناسبی باقی نخواهد ماند و می‌توانید پیام و ردگیری پشته‌ی استثنا را بخوانید.

پنجره‌ها و پدها

پنجره‌ها انتزاع پایه در curses هستند. یک شیء پنجره نشان‌دهنده‌ی یک ناحیه‌ی مستطیلی از صفحه است و از متدهایی برای نمایش متن، پاک کردن آن، اجازه دادن به کاربر برای وارد کردن رشته‌ها و غیره پشتیبانی می‌کند.

شیء stdscr که توسط تابع initscr() برگردانده می‌شود، یک شیء پنجره است که کل صفحه را می‌پوشاند. بسیاری از برنامه‌ها ممکن است تنها به همین یک پنجره نیاز داشته باشند، اما شما ممکن است بخواهید صفحه را به پنجره‌های کوچک‌تر تقسیم کنید تا آن‌ها را به‌طور جداگانه بازترسیم یا پاک کنید. تابع newwin() یک پنجره جدید با اندازه‌ای مشخص ایجاد می‌کند و شیء پنجره جدید را برمی‌گرداند.

begin_x = 20; begin_y = 7
height = 5; width = 40
win = curses.newwin(height, width, begin_y, begin_x)

توجه داشته باشید که دستگاه مختصات استفاده‌شده در curses غیرمعمول است. مختصات همیشه به ترتیب y,x ارسال می‌شود و گوشه‌ی بالا سمت چپ یک پنجره، مختصات (۰،۰) است. این امر قرارداد رایج برای مدیریت مختصات را می‌شکند؛ قراردادی که در آن مختصات x ابتدا می‌آید. این یک تفاوت ناخوشایند با بیشتر برنامه‌های کاربردی رایانه‌ای دیگر است، اما از زمان نخستین نگارش curses بخشی از آن بوده است و اکنون برای تغییر این موارد خیلی دیر است.

برنامه‌ی شما می‌تواند با استفاده از متغیرهای curses.LINES و curses.COLS اندازه‌ی صفحه را تعیین کند تا اندازه‌های y و x را به دست آورد. در این صورت، مختصات مجاز از (0,0) تا (curses.LINES - 1, curses.COLS - 1) خواهد بود.

هنگامی که یک متد را برای نمایش یا پاک کردن متن فراخوانی می‌کنید، اثر آن بلافاصله روی نمایشگر ظاهر نمی‌شود. در عوض، باید متد refresh() اشیای پنجره را فراخوانی کنید تا صفحه به‌روزرسانی شود.

دلیل این امر آن است که curses در ابتدا با در نظر گرفتن اتصالات پایانه‌ای کند با نرخ ۳۰۰ بود نوشته شده بود؛ در این پایانه‌ها، به حداقل رساندن زمان لازم برای بازترسیم صفحه بسیار مهم بود. در عوض، curses تغییرات صفحه را انباشته می‌کند و هنگامی که شما refresh() را فراخوانی می‌کنید، آن‌ها را به کارآمدترین شیوه نمایش می‌دهد. برای مثال، اگر برنامه شما متنی را در یک پنجره نمایش دهد و سپس پنجره را پاک کند، نیازی به ارسال متن اصلی نیست، زیرا هرگز قابل مشاهده نیست.

در عمل، اینکه به‌صراحت به curses بگوییم یک پنجره را دوباره ترسیم کند، چندان برنامه‌نویسی با curses را پیچیده نمی‌کند. بیشتر برنامه‌ها وارد دوره‌ای از فعالیت فشرده می‌شوند و سپس برای انتظار فشردن یک کلید یا اقدام دیگری از سوی کاربر مکث می‌کنند. تنها کاری که باید انجام دهید این است که پیش از مکث برای انتظار ورودی کاربر، ابتدا با فراخوانی stdscr.refresh() یا متد refresh() یک پنجره مرتبط دیگر، مطمئن شوید که صفحه دوباره ترسیم شده است.

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

pad = curses.newpad(100, 100)
# These loops fill the pad with letters; addch() is
# explained in the next section
for y in range(0, 99):
    for x in range(0, 99):
        pad.addch(y,x, ord('a') + (x*x+y*y) % 26)

# Displays a section of the pad in the middle of the screen.
# (0,0) : coordinate of upper-left corner of pad area to display.
# (5,5) : coordinate of upper-left corner of window area to be filled
#         with pad content.
# (20, 75) : coordinate of lower-right corner of window area to be
#          : filled with pad content.
pad.refresh( 0,0, 5,5, 20,75)

فراخوانی refresh() بخشی از پد را در مستطیلی که از مختصات (۵،۵) تا مختصات (۲۰،۷۵) روی صفحه امتداد دارد، نمایش می‌دهد؛ گوشه‌ی بالا سمت چپ بخش نمایش‌داده‌شده، مختصات (۰،۰) روی پد است. جدای از این تفاوت، پدها دقیقاً مانند پنجره‌های معمولی هستند و از همان متدها پشتیبانی می‌کنند.

اگر چندین پنجره و پد روی صفحه دارید، راه کارآمدتری برای به‌روزرسانی صفحه و جلوگیری از سوسوی آزاردهنده‌ی صفحه هنگام به‌روزرسانی هر بخش از آن وجود دارد. refresh() در واقع دو کار انجام می‌دهد:

  1. متد noutrefresh() هر پنجره را فراخوانی می‌کند تا یک ساختار داده زیربنایی را که وضعیت مطلوب صفحه را نشان می‌دهد به‌روزرسانی کند.

  2. تابع doupdate() را فراخوانی می‌کند تا صفحه فیزیکی را با وضعیت دلخواه ثبت‌شده در ساختار داده هماهنگ کند.

در عوض می‌توانید noutrefresh() را روی تعدادی از پنجره‌ها فراخوانی کنید تا ساختار داده به‌روزرسانی شود، و سپس doupdate() را برای به‌روزرسانی صفحه فراخوانی کنید.

نمایش متن

از دیدگاه یک برنامه‌نویس C، ممکن است curses گاهی شبیه هزارتوی پیچ‌درپیچ از توابع باشد که همگی تفاوت‌های ظریفی با یکدیگر دارند. برای مثال، addstr() یک رشته را در موقعیت فعلی مکان‌نما در پنجره stdscr نمایش می‌دهد، در حالی که mvaddstr() پیش از نمایش رشته، ابتدا به یک مختصات y,x داده‌شده می‌رود. waddstr() درست مانند addstr() است، اما امکان تعیین یک پنجره برای استفاده به‌جای استفاده پیش‌فرض از stdscr را می‌دهد. mvwaddstr() امکان تعیین هم یک پنجره و هم یک مختصات را می‌دهد.

خوشبختانه رابط پایتون تمام این جزئیات را پنهان می‌کند. stdscr یک شیء پنجره مانند هر شیء دیگری است و متدهایی مانند addstr() چندین شکل آرگومان را می‌پذیرند. معمولاً ۴ شکل مختلف وجود دارد.

فرم

توضیحات

str یا ch

نمایش رشته‌ی str یا نویسه‌ی ch در موقعیت فعلی

str یا ch، attr

رشته‌ی str یا نویسه‌ی ch را با استفاده از ویژگی attr در موقعیت فعلی نمایش دهید

y، x، str یا ch

به موقعیت y,x درون پنجره بروید و str یا ch را نمایش دهید

y، x، str یا ch، attr

به موقعیت y,x درون پنجره بروید و str یا ch را با استفاده از ویژگی attr نمایش دهید

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

The addstr() method takes a Python string or bytestring as the value to be displayed. The contents of bytestrings are sent to the terminal as-is. On a build without wide-character support strings are encoded using the value of the window's encoding attribute; this defaults to the default system encoding as returned by locale.getencoding().

متدهای addch() یک نویسه دریافت می‌کنند، که می‌تواند یک رشته به طول ۱، یک رشته‌بایت (bytestring) به طول ۱، یا یک عدد صحیح باشد.

Constants are provided for the characters of the terminal's alternate character set. For example, ACS_PLMINUS is a +/- symbol, and ACS_ULCORNER is the upper left corner of a box (handy for drawing borders). You can also use the appropriate Unicode character.

پنجره‌ها به یاد می‌آورند که مکان‌نما پس از آخرین عملیات کجا مانده است، بنابراین اگر مختصات y,x را وارد نکنید، رشته یا نویسه در هر جایی که آخرین عملیات مکان‌نما را رها کرده است نمایش داده می‌شود. همچنین می‌توانید مکان‌نما را با متد move(y,x) جابه‌جا کنید. از آنجا که برخی پایانه‌ها همیشه یک مکان‌نمای چشمک‌زن را نمایش می‌دهند، ممکن است بخواهید اطمینان حاصل کنید که مکان‌نما در موقعیتی قرار گرفته است که باعث حواس‌پرتی نمی‌شود؛ چشمک زدن مکان‌نما در موقعیتی که ظاهراً تصادفی است می‌تواند گیج‌کننده باشد.

If your application doesn't need a blinking cursor at all, you can call curs_set(False) to make it invisible. The window method leaveok() does something different: when its argument is true, curses leaves the cursor wherever the last update put it, instead of moving it back to the window's cursor position.

ویژگی‌ها و رنگ

نویسه‌ها را می‌توان به روش‌های مختلفی نمایش داد. ردیف‌های وضعیت در یک برنامه‌ی مبتنی بر متن معمولاً به‌حالت معکوس (reverse video) نمایش داده می‌شوند، یا ممکن است یک نمایشگر متن نیاز داشته باشد که کلمات خاصی را برجسته کند. curses از این موضوع پشتیبانی می‌کند و به شما اجازه می‌دهد برای هر سلول روی صفحه یک ویژگی تعیین کنید.

یک ویژگی یک عدد صحیح است که هر بیت آن نشان‌دهنده‌ی یک ویژگی متفاوت است. شما می‌توانید سعی کنید متنی را با چند بیت ویژگی فعال نمایش دهید، اما curses تضمین نمی‌کند که همه‌ی ترکیب‌های ممکن در دسترس باشند، یا اینکه همه‌ی آن‌ها از نظر بصری متمایز باشند. این امر به توانایی پایانه‌ی مورد استفاده بستگی دارد، بنابراین امن‌تر است که به رایج‌ترین ویژگی‌های در دسترس، که در اینجا فهرست شده‌اند، پایبند باشید.

ویژگی

توضیحات

A_BLINK

متن چشمک‌زن

A_BOLD

متن بسیار روشن یا پررنگ

A_DIM

متن نیمه‌روشن

A_REVERSE

متن ویدیویی معکوس

A_STANDOUT

بهترین حالت برجسته‌سازی موجود

A_UNDERLINE

متن زیرخط‌دار

بنابراین، برای نمایش یک خط وضعیت با نمایش معکوس در خط بالای صفحه، می‌توانید این‌گونه کد بنویسید:

stdscr.addstr(0, 0, "Current mode: Typing mode",
              curses.A_REVERSE)
stdscr.refresh()

کتابخانه curses همچنین از رنگ در آن پایانه‌هایی که آن را فراهم می‌کنند، پشتیبانی می‌کند. رایج‌ترین پایانه از این نوع احتمالاً کنسول لینوکس است و پس از آن پایانه‌های xterm رنگی قرار دارند.

برای استفاده از رنگ، باید تابع start_color() را بلافاصله پس از فراخوانی initscr() صدا بزنید تا مجموعه‌رنگ پیش‌فرض راه‌اندازی شود (تابع curses.wrapper() این کار را به‌طور خودکار انجام می‌دهد). پس از انجام این کار، اگر پایانه‌ی مورد استفاده واقعاً بتواند رنگ را نمایش دهد، تابع has_colors() مقدار TRUE را برمی‌گرداند. (توجه: curses از املای آمریکایی 'color' به‌جای املای کانادایی/بریتانیایی 'colour' استفاده می‌کند. اگر به املای بریتانیایی عادت دارید، باید بپذیرید که به‌خاطر این توابع آن را با املای اشتباه بنویسید.)

کتابخانه curses تعداد محدودی جفت‌رنگ را نگه می‌دارد که حاوی یک رنگ پیش‌زمینه (یا رنگ متن) و یک رنگ پس‌زمینه هستند. می‌توانید مقدار ویژگی متناظر با یک جفت‌رنگ را با تابع color_pair() دریافت کنید؛ می‌توان این مقدار را با ویژگی‌های دیگر مانند A_REVERSE با OR بیتی ترکیب کرد، اما باز هم، کار کردن چنین ترکیب‌هایی در تمام پایانه‌ها تضمین نمی‌شود.

مثالی که سطری از متن را با استفاده از جفت‌رنگ (color pair) ۱ نمایش می‌دهد:

stdscr.addstr("Pretty text", curses.color_pair(1))
stdscr.refresh()

همان‌طور که پیش‌تر گفتم، یک جفت‌رنگ از یک رنگ پیش‌زمینه و یک رنگ پس‌زمینه تشکیل شده است. تابع init_pair(n, f, b) تعریف جفت‌رنگ n را به رنگ پیش‌زمینه f و رنگ پس‌زمینه b تغییر می‌دهد. جفت‌رنگ ۰ به‌صورت ذاتی سفید روی سیاه است و نمی‌توان آن را تغییر داد.

رنگ‌ها شماره‌گذاری شده‌اند و start_color() هنگام فعال‌سازی حالت رنگ، ۸ رنگ پایه را مقداردهی اولیه می‌کند. این رنگ‌ها عبارت‌اند از: ۰:سیاه، ۱:قرمز، ۲:سبز، ۳:زرد، ۴:آبی، ۵:سرخابی، ۶:فیروزه‌ای و ۷:سفید. ماژول curses برای هر یک از این رنگ‌ها ثابت‌های نام‌دار تعریف می‌کند: curses.COLOR_BLACK، curses.COLOR_RED و غیره.

بیایید همه این‌ها را کنار هم بگذاریم. برای تغییر رنگ ۱ به متن قرمز روی زمینه سفید، باید این‌گونه فراخوانی کنید:

curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)

هنگامی که یک جفت‌رنگ را تغییر می‌دهید، هر متنی که از قبل با استفاده از آن جفت‌رنگ نمایش داده شده باشد، به رنگ‌های جدید تغییر خواهد کرد. همچنین می‌توانید متن جدیدی را با این رنگ به‌صورت زیر نمایش دهید:

stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1))

پایانه‌های بسیار پیشرفته می‌توانند تعاریف رنگ‌های واقعی را به یک مقدار RGB داده‌شده تغییر دهند. این امکان را به شما می‌دهد که رنگ ۱ را، که معمولاً قرمز است، به بنفش یا آبی یا هر رنگ دیگری که بخواهید تغییر دهید. متأسفانه، کنسول لینوکس از این قابلیت پشتیبانی نمی‌کند، بنابراین من نمی‌توانم آن را امتحان کنم و نمی‌توانم هیچ مثالی ارائه دهم. می‌توانید با فراخوانی can_change_color()، که در صورت وجود این قابلیت True را برمی‌گرداند، بررسی کنید که آیا پایانه شما این کار را انجام می‌دهد یا خیر. اگر آن‌قدر خوش‌شانس هستید که چنین پایانه توانمندی دارید، برای اطلاعات بیشتر به صفحات man سیستم خود مراجعه کنید.

ورودی کاربر

کتابخانه curses در C فقط سازوکارهای ورودی بسیار ساده‌ای را ارائه می‌دهد. ماژول curses پایتون یک ابزارک پایه برای ورودی متن می‌افزاید. (سایر کتابخانه‌ها مانند Urwid مجموعه‌های گسترده‌تری از ابزارک‌ها دارند.)

There are three methods for getting input from a window:

  • get_wch() refreshes the screen and then waits for the user to hit a key, displaying the key if echo() has been called earlier. You can optionally specify a coordinate to which the cursor should be moved before pausing.

  • getch() does the same thing but returns the code of the key instead of a character. With ncurses this is a single byte of the key's encoding in the current locale, so a character encoded with several bytes takes several calls, one byte per call.

  • getkey() does the same as getch() but returns a string: an ordinary key as a 1-character string, and a special key as its name, such as KEY_UP.

It's possible to not wait for the user using the nodelay() window method. After nodelay(True), the reads for the window become non-blocking. To signal that no input is ready, get_wch() and getkey() raise an exception, and getch() returns -1. There's also a halfdelay() function, which can be used to (in effect) set a timer on each read; if no input becomes available within a specified delay (measured in tenths of a second), the read fails the same way.

Special keys such as Page Up, Home, or the cursor keys are returned by all three as one of the KEY_* constants, all larger than 255. You can compare the value returned to constants such as curses.KEY_PPAGE, curses.KEY_HOME, or curses.KEY_LEFT. The main loop of your program may look something like this:

while True:
    c = stdscr.get_wch()
    if c == 'p':
        PrintDocument()
    elif c == 'q':
        break  # Exit the while loop
    elif c == curses.KEY_HOME:
        x = y = 0

ماژول curses.ascii توابع عضویت در کلاس‌های ASCII را فراهم می‌کند که آرگومان‌هایی از نوع عدد صحیح یا رشته‌ی تک‌نویسه‌ای می‌پذیرند؛ این توابع ممکن است در نوشتن آزمون‌های خواناتر برای چنین حلقه‌هایی مفید باشند. این ماژول همچنین توابع تبدیل را فراهم می‌کند که آرگومان‌هایی از نوع عدد صحیح یا رشته‌ی تک‌نویسه‌ای می‌پذیرند و همان نوع را برمی‌گردانند. برای مثال، curses.ascii.ctrl() نویسه‌ی کنترلی متناظر با آرگومان خود را برمی‌گرداند.

There's also a method to retrieve an entire line, getstr(). It isn't used very often, because its functionality is quite limited; the only editing keys available are the erase and kill characters, and the Enter key, which terminates the line. It returns a bytes object, and can optionally be limited to a fixed number of bytes.

curses.echo()            # Enable echoing of characters

# Get a line of at most 15 bytes, with the cursor on the top line
s = stdscr.getstr(0,0, 15)

ماژول curses.textpad یک جعبه‌ی متن فراهم می‌کند که از مجموعه‌ای از کلیدبندی‌ها (keybindings) شبیه به Emacs پشتیبانی می‌کند. متدهای مختلفی از کلاس Textbox از ویرایش با اعتبارسنجی ورودی و جمع‌آوری نتایج ویرایش، چه با فاصله‌های انتهایی و چه بدون آن‌ها، پشتیبانی می‌کنند. در ادامه مثالی آمده است:

import curses
from curses.textpad import Textbox, rectangle

def main(stdscr):
    stdscr.addstr(0, 0, "Enter IM message: (hit Ctrl-G to send)")

    editwin = curses.newwin(5,30, 2,1)
    rectangle(stdscr, 1,0, 1+5+1, 1+30+1)
    stdscr.refresh()

    box = Textbox(editwin)

    # Let the user edit until Ctrl-G is struck.
    box.edit()

    # Get resulting contents
    message = box.gather()

برای جزئیات بیشتر، مستندات کتابخانه در مورد curses.textpad را ببینید.

برای اطلاعات بیشتر

این HOWTO برخی موضوعات پیشرفته را پوشش نمی‌دهد، مانند خواندن محتوای صفحه یا گرفتن رویدادهای ماوس از یک نمونه xterm، اما صفحه کتابخانه پایتون برای ماژول curses اکنون نسبتاً کامل است. بهتر است در ادامه آن را مرور کنید.

اگر در مورد رفتار دقیق توابع curses تردید دارید، به صفحه‌های راهنمای پیاده‌سازی curses خود مراجعه کنید، چه ncurses باشد و چه پیاده‌سازی یک فروشنده‌ی یونیکس اختصاصی. صفحه‌های راهنما هرگونه رفتار غیرعادی را مستند می‌کنند و فهرست‌های کاملی از همه‌ی توابع، ویژگی‌ها و نویسه‌های ACS_* در دسترس شما ارائه می‌دهند.

از آنجا که API curses بسیار بزرگ است، برخی از توابع در رابط پایتون پشتیبانی نمی‌شوند. اغلب این موضوع به این دلیل نیست که پیاده‌سازی آن‌ها دشوار است، بلکه به این دلیل است که هنوز کسی به آن‌ها نیاز نداشته است. همچنین، پایتون هنوز از کتابخانه‌ی menu مرتبط با ncurses پشتیبانی نمی‌کند. از وصله‌هایی که پشتیبانی از این موارد را اضافه کنند استقبال می‌شود؛ برای آموختن بیشتر در مورد ارسال وصله‌ها به پایتون، راهنمای توسعه‌دهنده‌ی پایتون را ببینید.