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

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 نمایش دهید

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

متد addstr() یک رشته یا بایت‌رشته پایتون را به عنوان مقداری که باید نمایش داده شود می‌پذیرد. محتوای بایت‌رشته‌ها به صورت همانند به ترمینال ارسال می‌شوند. در ساخت بدون پشتیبانی از نویسه‌های پهن، رشته‌ها با استفاده از مقدار ویژگی encoding پنجره کدگذاری می‌شوند؛ این به طور پیش‌فرض به کدگذاری پیش‌فرض سیستم پیش می‌رود که توسط locale.getencoding() برگردانده می‌شود.

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

ثابت‌ها برای نویسه‌های مجموعه‌ی جایگزین ترمینال فراهم شده‌اند. برای مثال، ACS_PLMINUS نماد +/- است، و ACS_ULCORNER گوشه‌ی بالا-چپ یک جعبه است (مناسب برای رسم کادرها). شما همچنین می‌توانید از نویسه‌ی یونیکد مناسب استفاده کنید.

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

اگر برنامه‌ی شما اصلاً به یک نشانگر چشمک‌زن نیاز ندارد، می‌توانید curs_set(False) را فراخوانی کنید تا نامرئی شود. متد پنجره leaveok() کار متفاوتی انجام می‌دهد: وقتی آرگومان آن درست باشد، curses نشانگر را هر جایی که آخرین به‌روزرسانی آن را قرار داده است رها می‌کند، به جای اینکه آن را به موقعیت نشانگر پنجره بازگرداند.

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

نویسه‌ها را می‌توان به روش‌های مختلفی نمایش داد. ردیف‌های وضعیت در یک برنامه‌ی مبتنی بر متن معمولاً به‌حالت معکوس (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 مجموعه‌های گسترده‌تری از ابزارک‌ها دارند.)

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

  • get_wch() صفحه را بازسازی می‌کند و سپس منتظر می‌ماند تا کاربر دکمه‌ای را بزند، دکمه را نمایش می‌دهد اگر echo() قبلاً فراخوانی شده باشد. شما می‌توانید به‌صورت اختیاری مختصاتی را مشخص کنید که نشانگر باید قبل از توقف به آنجا منتقل شود.

  • getch() کار مشابهی انجام می‌دهد اما کد دکمه را به جای نویسه برمی‌گرداند. با ncurses این یک بایت واحد از کدگذاری دکمه در locale جاری است، بنابراین یک نویسه که با چند بایت کدگذاری شده است چندین فراخوانی می‌گیرد، یک بایت در هر فراخوانی.

  • getkey() همان کار getch() را انجام می‌دهد اما یک رشته برمی‌گرداند: یک دکمه معمولی به عنوان یک رشته ۱-نویسه، و یک دکمه خاص به عنوان نام آن، مانند KEY_UP.

استفاده از متد پنجره nodelay() امکان عدم انتظار برای کاربر را فراهم می‌کند. پس از nodelay(True)، خواندن‌ها برای پنجره غیرمسدودکننده می‌شوند. برای سیگنال دادن اینکه هیچ ورودی آماده نیست، get_wch() و getkey() یک استثنا پرتاب می‌کنند، و getch() -1 برمی‌گرداند. همچنین تابع halfdelay() وجود دارد که می‌تواند (در اثر) یک تایمر روی هر خواندن تنظیم کند؛ اگر هیچ ورودی در تاخیر مشخص شده (بر حسب دهم ثانیه) موجود نشود، خواندن به همان روش شکست می‌خورد.

دکمه‌های خاص مانند Page Up، Home، یا دکمه‌های نشانگر توسط هر سه به عنوان یکی از KEY_* constants برگردانده می‌شوند، که همگی بزرگتر از ۲۵۵ هستند. شما می‌توانید مقدار برگردانده شده را با ثابت‌هایی مانند curses.KEY_PPAGE، curses.KEY_HOME، یا curses.KEY_LEFT مقایسه کنید. حلقه‌ی اصلی برنامه‌ی شما ممکن است چیزی شبیه به این باشد:

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() نویسه‌ی کنترلی متناظر با آرگومان خود را برمی‌گرداند.

همچنین متدی برای بازیابی یک سطر کامل وجود دارد، getstr(). این متد بسیار استفاده نمی‌شود، زیرا کارکرد آن کاملاً محدود است؛ تنها دکمه‌های ویرایش موجود نویسه‌های erase و kill، و دکمه Enter هستند که سطر را خاتمه می‌دهند. این متد یک شیء 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 پشتیبانی نمی‌کند. از وصله‌هایی که پشتیبانی از این موارد را اضافه کنند استقبال می‌شود؛ برای آموختن بیشتر در مورد ارسال وصله‌ها به پایتون، راهنمای توسعه‌دهنده‌ی پایتون را ببینید.