पाठ इंटरफ़ेस, भाग 2 उपयोगकर्ता सहभागिता

तो, दूसरा भाग। यहां मैं प्रकट करूंगा कि उपयोगकर्ता से जानकारी कैसे प्राप्त की जाए, साथ ही साथ इस डेटा में हेरफेर कैसे करें। यहां आप पिछली पोस्ट की टिप्पणियों में उठाए गए प्रश्न पर स्पर्श कर सकते हैं - "यह सब क्यों आवश्यक है?"। 21 वीं शताब्दी में ऐसे इंटरफेस के उपयोग के उदाहरण विभिन्न मशीनों पर लागू होते हैं जो अलग-अलग सेवाओं को लागू करते हैं। सबसे अधिक बार, वे एक न्यूनतम लिनक्स वितरण या बूट करने योग्य कर्नेल + व्यस्त बॉक्स का एक सेट हैं। इस इंटरफ़ेस का उपयोग करके, आप सेवा के लिए एक निश्चित फ्रंट-एंड को लागू कर सकते हैं, जो आपको एक नज़र में मुख्य नोड्स की स्थिति निर्धारित करने या उपयोगकर्ता के लिए सुविधाजनक रूप में कुछ संचालन करने की अनुमति देता है। एक उदाहरण VMware ESXi (vDirector, vCenter आदि), Citrix Xen के लिए समान है, जो वेब इंटरफेस और TUI की शक्ति को बैकअप इंटरफ़ेस और / या कॉन्फ़िगरेशन / डायग्नोस्टिक इंटरफ़ेस के रूप में संयोजित करता है। दर्जनों मशीनों के माध्यम से स्विच करना, आप एक नज़र में देख सकते हैं कि क्या सब कुछ क्रम में है या जल्दी से आईपी पते का पता लगा सकते हैं, उपयोगकर्ता की पहुंच को पूरी तरह से कंसोल पर रोकते हुए, उसे केवल वही दिखाते हैं जो उसे जानने की जरूरत है (मूर्खतापूर्ण)।



अंतिम पोस्ट को न देखने के लिए, मैं आपको याद दिलाता हूं कि मैं सभी "विगेट्स" को एक संरचना के माध्यम से कार्यान्वित करता हूं जो परतों की समानता बनाता है, जिनमें से प्रत्येक अपने स्वयं के कुछ (छाया, डिजाइन, शीर्ष परत, उपयोगकर्ता को बदलने के लिए उपलब्ध) के लिए जिम्मेदार है। और यह सब एक पैनल में संयुक्त है जो आपको एक पूरे के रूप में सभी परतों को स्थानांतरित करने, छिपाने और हटाने की अनुमति देता है।

struct _cursed_window { WINDOW *background; WINDOW *decoration; WINDOW *overlay; PANEL *panel; }; typedef struct _cursed_window cursed; 


स्क्रीनशॉट को देखते हुए, आप देख सकते हैं कि उनमें से कुछ पर रंग कैसे भिन्न होते हैं। हालांकि कोड समान है, रंग प्रदर्शन टर्मिनल एमुलेटर के विशेष कार्यान्वयन पर निर्भर करता है। मैंने इसे एक उदाहरण के रूप में छोड़ा।

मैं getch (), wgetch (), scanw, और कामरेड जैसे कार्यों के साथ काम करने का उल्लेख नहीं करूंगा। ये कुछ जोड़ के साथ C से संबंधित कार्यों के अनिवार्य रूप से एनालॉग हैं।

मेन्यू


मेनू को सरल और स्पष्ट रूप से लागू किया गया है। इंजन नेस्टेड सहित मेनू विकल्पों की एक विस्तृत विविधता का समर्थन करता है। कोई भी मेनू आइटम सक्रिय हो सकता है या नहीं। स्क्रॉलिंग के लिए समर्थन भी है, संयोजन और अन्य सुखों द्वारा खोज, बहुत जटिल और परिष्कृत मेनू के निर्माण को सरल बनाना।
हम एक फ़ंक्शन लिखते हैं जो उपयोगकर्ता की पसंद के अनुरूप मूल्य लौटाएगा।

 int32_t tui_make_menu (const char **choices, size_t choices_num) 


इनपुट पर, हमारे पास विकल्पों की पंक्तियों और इन विकल्पों की संख्या के साथ एक सरणी है। यहाँ एक सूची है जिसके बारे में हमारे पास है।

 const char *choices[] = { "Restore", "Quick backup", "Adv. backup", "Add machine", "Exit", NULL, }; 


और हम इसे एक फंक्शन में पास करेंगे
 if(tui_make_menu(choices,(sizeof choices/sizeof choices[0]))==4) do_something(); 


और फ़ंक्शन में ही, हम अब स्ट्रिंग के इस सरणी के आधार पर एक मेनू बनाएंगे।

 ITEM **my_items = (ITEM **)calloc(choices_num, sizeof(ITEM *)); for(int32_t i = 0; i < choices_num; ++i) my_items[i] = new_item(choices[i], ""); MENU *my_menu = new_menu((ITEM **)my_items); 


New_item के लिए कॉल में, मैंने दूसरी तर्क के रूप में एक खाली रेखा को इंगित किया, यह मेनू आइटम के लिए पोस्टफिक्स है, यह एक टिप्पणी के रूप में दाईं ओर प्रदर्शित होगा (उदाहरण के लिए, संबंधित गर्म कुंजी को इंगित करने के लिए)।
मेनू के लिए विंडो सेट करें
 set_menu_win(my_menu, new->overlay); 

और एक प्रतीक भी सेट करें जो चयनित मेनू आइटम का एक मार्कर होगा।
 set_menu_mark(my_menu, " * "); 


खैर, चलो हमारे मेनू को रंगीन करते हैं।
 set_menu_fore(my_menu, COLOR_PAIR(1) | A_REVERSE); set_menu_back(my_menu, COLOR_PAIR(2)); set_menu_grey(my_menu, COLOR_PAIR(3)); 


और इसे प्रकाशित करें
 post_menu(my_menu); 




हमेशा की तरह, हम एक मैनुअल, लेकिन विजुअल मोड में महत्वपूर्ण कार्यों के साथ इंजन प्रदान करते हैं।

 do { switch(user_key) { case KEY_MOUSE: menu_driver(my_menu, KEY_MOUSE); touchwin(panel_window(new->panel)); update_panels(); doupdate(); sleep(1); goto mouse_end; case KEY_DOWN: menu_driver(my_menu, REQ_DOWN_ITEM); break; case KEY_UP: menu_driver(my_menu, REQ_UP_ITEM); break; } touchwin(panel_window(new->panel)); update_panels(); doupdate(); } while((user_key = wgetch(new->overlay)) != 0xD); 


हाँ, हाँ। आइए हम माउस के साथ काम करने के बारे में बात करते हैं।

एक चूहा


Ncurses हमें उन कार्रवाइयों की नकल करने की अनुमति देता है जो हमने पहले ही माउस के साथ बहुत जटिलता या कोड परिवर्तन के बिना किए हैं। यह एक साधारण मास्किंग के साथ माउस घटनाओं की प्रतिक्रिया को सक्षम करने के लिए पर्याप्त है।
 mousemask(ALL_MOUSE_EVENTS, NULL); 

इस स्थिति में, सभी घटनाओं की अनुमति है, लेकिन आप स्वाभाविक रूप से केवल सही बटन BUTTON2_DOUBLE_CLICKED के साथ डबल-क्लिक करने के लिए या केवल बाएं बटन BUTTON1_RELEASED , आदि जारी करने के लिए प्रतिक्रिया करना चाहते हैं हो सकता है।

उसके बाद, हमारे मानक इनपुट प्रसंस्करण चक्र में, आपको केस KEY_MOUSE जोड़ना होगा और फिर प्राप्त कार्यक्रम को मेनू इंजन में पास करना होगा या कुछ और करना होगा। पहले मामले में, मेनू इंजन स्वतंत्र रूप से निर्धारित करेगा कि मेनू मार्कर को कैसे स्थानांतरित किया जाए।
यह बहुत महत्वपूर्ण है अगर काम हमेशा पर्याप्त माउस ऑपरेशन के साथ वीएनसी के माध्यम से जाता है।

अब जब उपयोगकर्ता ने एक मेनू आइटम का निर्णय लिया और चुना है, तो हम उसकी पसंद का पता लगा सकते हैं

 mouse_end: int32_t user_selection = item_index(current_item(my_menu)); 


हम कचरा हटाते हैं

 unpost_menu(my_menu); free_menu(my_menu); for(int32_t i = 0; i < choices_num; ++i) free_item(my_items[i]); tui_del_win(new); 


और मान वापस करें

 return (user_selection); 


फार्म और फील्ड्स


Ncurses में इनपुट फ़ील्ड में एक बहुत शक्तिशाली कार्यक्षमता है जो आपको उपयोगकर्ता द्वारा विस्तृत श्रृंखला में दर्ज किए गए पाठ में हेरफेर करने की अनुमति देता है। लेकिन पहले, हम एक छोटा फ़ंक्शन लिखेंगे जो उपयोगकर्ता से हमें पाठ लौटाएगा और इसके उदाहरण पर हम इस क्षेत्र के साथ कैसे और क्या करना है, इस पर विचार करेंगे।

 char * tui_make_input(const char *info, int32_t max_len); 

अर्थात्, हमें उपयोगकर्ता से क्या प्राप्त करना है, यह स्पष्ट करते हुए अधिकतम क्षेत्र की लंबाई और एक निश्चित सूचना संदेश निर्धारित करना चाहिए।

हमारे इनपुट क्षेत्र के साथ एक बफर बनाएं। इस स्थिति में, फ़ील्ड का आकार निश्चित है, लेकिन फ़ील्ड स्वचालित रूप से विस्तारित हो सकती है,
यह आरंभीकरण शैली की आवश्यकता है।
 FIELD *field[2]; field[0] = new_field(1, max_len, 0, 0, 0, 0); field[1] = NULL; 


 FORM *my_form; set_field_back(field[0], A_UNDERLINE); 


अधिकतम_लेन वर्णों में फ़ील्ड 1 पंक्ति ऊँची है। हमने फॉर्म के लिए एक फ़ील्ड बनाया है, लेकिन आप उन्हें बना सकते हैं जैसे आप चाहते हैं, उदाहरण के लिए, यदि आपके पास लॉगिन / पासवर्ड इनपुट फ़ॉर्म है। फिर इन फ़ील्ड्स को एक साथ उपयोग किया जा सकता है, उदाहरण के लिए, जब आप एक निश्चित कुंजी दबाते हैं और / या फॉर्म भरते समय एक नए फ़ील्ड में जाते हैं (उदाहरण के लिए, यदि यह सीरियल नंबर दर्ज करने के लिए फॉर्म है)।
और अंडरस्कोर के साथ इनपुट फ़ील्ड को हाइलाइट करने का विकल्प भी शामिल था।

प्रकार

प्रत्येक फ़ील्ड में एक या अधिक प्रकार हो सकते हैं, जिन्हें निम्नानुसार परिभाषित किया गया है:
 set_field_type(field[0],TYPE_ALPHA); 

इस स्थिति में, अक्षरों के लिए फ़िल्टर चालू होता है, और उपयोगकर्ता या अन्य वर्णों द्वारा दर्ज किए गए नंबर वर्णमाला से संबंधित नहीं होते हैं। स्क्रीनशॉट में, TYPE_IPV4 फ़िल्टर के साथ इस तरह के इनपुट क्षेत्र का एक उदाहरण सक्षम है। दुर्भाग्य से, वह केवल संख्याओं और अवधियों की उपस्थिति के लिए जाँच करता है, लेकिन स्वयं पते की शुद्धता के लिए नहीं। लेकिन यहां तक ​​कि आप प्रत्येक कुंजी दबाए गए (फ़िल्टर को अतीत में) की जांच कर सकते हैं और, जाँच कर सकते हैं, यह तय करें कि इसे प्रदर्शित करना है या नहीं या चेतावनी प्रदर्शित करना है।



अब हम अपने खेतों को एक रूप में जोड़ते हैं

 my_form = new_form(field); set_form_sub(my_form, new->overlay); post_form(my_form); 


और हमारे निमंत्रण को भी प्रिंट करें।
 mvwprintw(new->decoration,1,1, info); 


इसके बाद, हमें यह सुनिश्चित करना होगा कि फॉर्म

 uint32_t user_key=0; do { switch(user_key) { case 0xD: form_driver(my_form, REQ_VALIDATION); goto check; default: form_driver(my_form, user_key); break; } touchwin(panel_window(new->panel)); update_panels(); doupdate(); } while((user_key = getch()) != KEY_F(12)); 


हम कीस्ट्रोक के लिए उपयोगकर्ता से लूप में प्रतीक्षा करते हैं, यदि यह एंटर नहीं है, तो हम फ़ील्ड ड्राइवर की कुंजी को पास करते हैं, जो पहले से ही फ़िल्टर लागू करता है और यदि आवश्यक हो, तो इसे प्रदर्शित करता है। यदि यह ENTER कुंजी है, तो हम एक फ़ील्ड चेक और लूप से बाहर निकलने का अनुरोध करते हैं।

अब यह आपके ऊपर है कि उपयोगकर्ता क्या दर्ज करे:
 check: char *result=0; asprintf(&result, "%s", field_buffer(field[0], 0)); 


अगला, हम इसे साफ करते हैं और परिणाम वापस करते हैं।

 unpost_form(my_form); free_field(field[0]); free_form(my_form); tui_del_win(new); return result; 


बेशक, आप इनपुट फ़ील्ड के लिए एक प्रारंभिक मूल्य निर्दिष्ट कर सकते हैं, जिसका उपयोग संकेत या डिफ़ॉल्ट मान के रूप में किया जा सकता है:
 set_field_buffer(field[0], 0, «Default»); 

खैर, वास्तव में अन्य विकल्पों में से एक बहुत। मैं ध्यान देना चाहता हूं कि पात्रों या पूरे शब्दों को हटाने / आंदोलन में हेरफेर उपयोगकर्ता के साथ निहित है। एक ही डू-टाइम लूप में, आवश्यक कुंजियों के कीस्ट्रोक्स को पकड़ना आवश्यक है और, form_driver के माध्यम से, वर्ण को हटाने के लिए अनुरोध करें, अक्षरों या शब्दों द्वारा कूदें। रूपों का उपयोग करके, आप आसानी से एक साधारण पाठ संपादक को लागू कर सकते हैं, पाठ के साथ शेर का हिस्सा जिसमें ncurses इंजन है। आखिरकार, यहां तक ​​कि फ़ील्ड के साथ पाठ को संरेखित करना, पूरे शब्दों और अन्य खुशियों को दूर करना समर्थित है।

बटन


चूंकि ncurses एक तैयार-निर्मित कार्यान्वयन प्रदान नहीं करते हैं, हम इसे स्वयं करेंगे। एक बटन के लिए सबसे सरल विकल्प एक छाया के साथ एक खिड़की है जिसे दबाए जाने पर, इसकी छाया में गिर जाता है। पिछली पोस्ट में, मैंने आपको बताया था कि सबसे सरल मामले में, छाया नीचे की खिड़की पर गिरेगी। यह इस तथ्य के कारण है कि समान छाया खींचने के लिए, हमें एक परत बनाना होगा, और यह परत आयताकार है। इसका अप्रयुक्त क्षेत्र अभी भी मौजूद है और नीचे स्क्रीनशॉट के रूप में होगा



डिफ़ॉल्ट रूप से कार्यान्वयन में "पारदर्शिता" प्रभाव नहीं है। लेकिन यह कोई फर्क नहीं पड़ता, इसे स्वतंत्र रूप से लागू किया जा सकता है। ज्यादातर मामलों में, हम विंडो के अंदर बटन खींचेंगे, इसलिए हम मान लेंगे कि हमें पता है कि बटन किस विंडो पर स्थित है।
फ़ंक्शन में, हम इसकी विशेषताओं (रंग, मोड) के साथ अंतर्निहित विंडो में पाठ पढ़ते हैं। ASCII कोड + विशेषता एक बाइट पर कब्जा करती है और प्रकार chtype (uint8_t) के एक चर में निहित है। इस प्रकार को प्रदर्शित करने के लिए एक विशेष फ़ंक्शन mvwaddchstr है।

बटन कोड
 cursed *tui_new_button(WINDOW *bwin, int32_t sy, int32_t sx, const char *label, size_t len) { cursed *new = malloc (sizeof *new); int32_t w = len + 4; int32_t h = 5; new->background = newwin(h, w + 1, sy, sx); wbkgd(new->background, COLOR_PAIR(4)); /* Get start coordinates of underlying window*/ int32_t shad_x, shad_y; getbegyx(bwin, shad_y, shad_x); chtype c[len + 7] = {0}; /* Extract underlying text and copy it to the button's shadow layer */ for(int32_t i=0; i < h; i++) { mvwinchnstr(bwin,sy-shad_y + i,sx-shad_x,c, len + 6); mvwaddchstr(new->background,i,0, c); } wattron(new->background, COLOR_PAIR(10)); for (int32_t i = 2; i < w - 1; i++) mvwaddch(new->background, h - 1, i, ' '); for (int32_t i= 2; i < h; i++) mvwprintw(new->background, i, w - 1, " "); wattroff(new->background, COLOR_PAIR(10)); new->decoration = derwin(new->background, h-2, w-2, 1, 1); wbkgd(new->decoration, COLOR_PAIR(1)); box(new->decoration, 0, 0); int32_t x, y; getmaxyx(new->decoration, y, x); new->overlay = derwin(new->decoration, y-2, x-2, 1, 1); wbkgd(new->overlay, COLOR_PAIR(1)); new->panel = new_panel(new->background); wprintw(new->overlay, label); update_panels(); doupdate(); return new; } 





यह बटन क्लिक एनीमेशन बनाने के लिए बनी हुई है। जैसे
"सबविंडो फ़ंक्शंस (सबविन, डेरविन, एमवीडविन, wsyncup, wsyncdown, wcursyncup, सिंकोक) परतदार हैं, अपूर्ण रूप से लागू किए गए हैं, और अच्छी तरह से परीक्षण नहीं किए गए हैं",
तब मुझे ऐसी बैसाखी के माध्यम से एक बटन दबाने के प्रभाव का एहसास हुआ। यही है, मैं बटन के मूल पैनल को छिपाता हूं, छाया के बिना एक डुप्लिकेट बनाता हूं, इसे तिरछे स्थानांतरित करता हूं, क्लोन को हटाता हूं और हटाता हूं। खैर, ज़ाहिर है, फिर से मूल बटन दिखा रहा है।

 void tui_toggle_button (cursed *button) { int32_t x, y; getbegyx(button->background, y, x); hide_panel(button->panel); WINDOW *dup_dec = dupwin(button->decoration); PANEL *duppan = new_panel(dup_dec); move_panel(duppan, y + 2, x + 3); update_panels(); doupdate(); usleep(200000); del_panel(duppan); show_panel(button->panel); } 




बाकी मानक इंटरफ़ेस तत्व हैं।


फ़ॉर्म और मेनू को मिलाकर, आप आसानी से ऑटो खोज के साथ ड्रॉप-डाउन सूची बना सकते हैं। क्षैतिज रूप से उन्मुख मेनू का उपयोग करके, आप एक रेडियोबॉटन बना सकते हैं।
ऑपरेशन की प्रगति का एक ही संकेतक बस कार्यान्वित किया जाता है:



 void tui_progress_bar(WINDOW *win, double progress) { int32_t height, width; getmaxyx(win, height, width); wattron(win, COLOR_PAIR(8)); for (int32_t i = 0; i < width * progress; i++) { mvwaddch(win, (height / 2), i, ' '); } wattroff(win, COLOR_PAIR(8)); wattron(win, COLOR_PAIR(7)); for (int32_t i = width * progress; i < width; i++) { mvwaddch(win, (height / 2), i, ' '); } wattroff(win, COLOR_PAIR(7)); wattron(win, A_STANDOUT); mvwprintw(win, (height / 2), (width / 2) - 2, "%.0f%%", progress*100); wattroff(win, A_STANDOUT); } 


उसी प्रोग्रामबार का उपयोग विशुद्ध रूप से टेक्स्ट एप्लिकेशन में किया जा सकता है। प्रगति के एक स्पष्ट और सुविधाजनक संकेतक के रूप में, हालांकि प्रिंटफ पर समान रूप से किया जा सकता है, लेकिन अन्य लाइनों के साथ कुछ और अपडेट करना संभव नहीं होगा।

निष्कर्ष


पाठ इंटरफ़ेस सरल है। कई लोग कंसोल उपयोगिताओं को लिखते हैं, कुछ मामलों में यह मुख्य काम करने वाला उपकरण है। क्यों यह धारणा के लिए अधिक दृश्य और अधिक आरामदायक नहीं बनाता है? इस स्क्रीनशॉट में, विंडोज़ और पैनल के बिना 3 आउटपुट फ़ंक्शंस के अलावा कुछ भी उपयोग नहीं किया गया है।

यदि यह स्क्रिप्ट के लिए उपयोग किया जाता है, तो tui को स्टार्टअप तर्क में कुंजी द्वारा चालू / बंद किया जा सकता है। एक तरह से या किसी अन्य, अगर कोई प्रोग्राम ग्राफिक्स का उपयोग नहीं कर सकता है, तो इसका मतलब यह नहीं है कि यह समझ से बाहर रहना चाहिए। सहायता के समान निष्कर्ष को अधिक दृश्य या प्रासंगिक भी बनाया जा सकता है। उसी TAB को पूरा करने वाला ऑटो कार्यान्वित किया जा सकता है और आपके उपयोगकर्ता आपको धन्यवाद देंगे। यहाँ एक अच्छा उदाहरण एक्सईएन उपयोगिता है जो एक्सएन सर्वर बंडल से है। उपयोगकर्ता द्वारा कीवर्ड टाइप करने के बाद, TAB दबाकर कार्यक्रम विकल्प प्रदान करता है। उदाहरण के लिए, uuid = और फिर सिस्टम में पंजीकृत मशीनों के UUID को स्वचालित रूप से प्रतिस्थापित किया जाता है। यह अविश्वसनीय रूप से कार्य को सरल और तेज करता है।

पुनश्च: यदि कोई शाप के साथ काम करने की कोशिश करना चाहता है, तो मैंने यहां सबसे सरल उदाहरण पोस्ट किया
github.com/Pugnator/curses_test
मैं आपको कोड के लिए बहुत डांटने के लिए नहीं कहता हूं - मैं सिर्फ एक शौकिया हूं।
बिन डायरेक्टरी में, बस रन बनाते हैं
यदि आवश्यक हो, स्थापित करें:
sudo apt-get install ncurses-dev (डेबियन आधारित) के लिए

Source: https://habr.com/ru/post/In208582/


All Articles