תכנות מונחה-אירועים שיעור מס' 11: תכנות ממשקי משתמש תכנות סדרתי סדר ביצוע הפקודות נקבע ע"י קוד התוכנית. קלט מן הסביבה נעשה באופן יזום ע"י התוכנית. במקרים בהם מבקשים קלט ממשתמש, התוכנית עוצרת את הביצוע עד לקבלת הקלט. )כפי שמתבצע בשיעור 9 דוגמא.)d9curve_select 4 המעבד פעיל כל זמן שהתוכנית מתבצעת )גם כאשר התוכנית עוצרת לקבלת קלט מן המשתמש או כל עצירה אחרת, למשל פקודת.)pause תלות מהלך התוכנית במשתנים סביבתיים )למשל סוגים שונים של קלט( נקבע מראש ע"י התוכנית, בד"כ באמצעות פקודות התנייה. תכנות מונחה-אירועים סדר ביצוע הפקודות נקבע ע"י אירועים בסביבה )למשל פעולות של המשתמש(. הסביבה יוזמת אירועים שמועברים למחשב באמצעות פסיקה.)interrupt( לכל סוג פסיקה משויכת תוכנית שמופעלת כאשר מגיעה פסיקה מסוג זה. המעבד פעיל רק כאשר מתבצעת תוכנית-פסיקה. משתנים סביבתיים משפיעים על מהלך הטיפול באירועים. ממשק-משתמש הוא מקרה פרטי של תכנות מונחה-אירועים. האירועים הם הקשה על אחד מכפתורי העכבר או המקלדת, כאשר הסמן נמצא על עצם גרפי מסוים. בדרך כלל תוכנית לממשק משתמש בנויה באופן הבא: התוכנית הראשית כוללת פקודות היוצרות את העצמים המרכיבים את הממשק, ובהתאם לצורך גם מאתחלת פרמטרים או ערכי ברירת מחדל. התוכנית הראשית מסתיימת, ובמטלב הבקרה חוזרת לחלון-הפקודות. לכל עצם גרפי ניתן לשייך פונקציה שתתבצע כאשר יקרה אירוע הקשור לעצם. פונקציה זו נקראת CallBack )קריאה-לאחור(. שיוך הפונקציות האלה לעצמים הגרפים בממשק נעשה בשלב האתחול )התוכנית הראשית( ע"י ציון תכונות העצם הרלוונטיות. לאחר שהתוכנית הראשית סיימה, אפשר להשתמש בממשק כל עוד החלון הגרפי פתוח. הקשה על העצמים בחלון שהוגדרו כפעילים תפעיל את פונקציות ה- CallBack שהוגדרו וכך יתבצע האלגוריתם המבוקש. המעבד פעיל רק כאשר מתבצעות פונקציות.CallBack עמ' 1 מתוך 7
דוגמא 1: הפונקציה d11save_plot מיועדת להפעלה כאשר יש כבר חלון גרפי פתוח עם שרטוט קיים. המטרה היא לצפות בשרטוט במספר צורות של הגדלה ו/או הזזה ולשמור חלק מן התצוגות, כך כל אחת מהן בקובץ נפרד בשמות סדרתיים. הפונקציה הראשית משייכת לצירים פונקציית- תגובה להקשה על הצירים, וכן מאתחלת מונה עבור שמות הקבצים ומסתיימת. המשתמש משנה את התצוגה ומקיש עם העכבר על שטח הצירים בכל פעם שרוצה לשמור את התצוגה. פונקציית התגובה שומרת את הפלוט לקובץ ומקדמת את המונה. סוגי אירועים התכונות הבאות משייכות פונקציה לעצם גרפי, כאשר קורים האירועים הבאים: ButtonDownFcn לחיצה על אחד מכפתורי העכבר כאשר הסמן נמצא מעל העצם KeyPressFcn לחיצה על מקש/ים במקלדת כאשר הסמן נמצא מעל העצם KeyReleaseFcn שחרור מקש/ים במקלדת כאשר הסמן נמצא מעל העצם CreateFcn יצירת העצם. )שימושי להגדרת תכונות ברירת-מחדל( DeleteFcn מחיקת העצם. )שימושי לשמירת מידע לפני שהעצם נעלם(. ResizeFcn שינוי ממדי העצם. )שימושי כאשר שינוי ממדים מחייב שינויי עיצוב(. התכונות הבאות רלוונטיות רק לחלון גרפי :)figure( CloseRequestFcn ניסיון לסגור את החלון )למשל ע"י הקשה על צלמית הסגירה(. WindowButtonDownFcn לחיצה על אחד מכפתורי העכבר כאשר הסמן נמצא מעל החלון WindowButtonUpFcn שחרור אחד מכפתורי העכבר כאשר הסמן נמצא מעל החלון WindowButtonMotionFcn תנועה של הסמן כשהוא נמצא מעל החלון WindowScrollWheelFcn שימוש בגלגל הגלילה בעכבר כאשר הסמן נמצא מעל החלון WindowKeyPressFcn לחיצה על מקש/ים במקלדת כאשר הסמן נמצא מעל החלון WindowKeyReleaseFcn שחרור מקש/ים במקלדת כאשר הסמן נמצא מעל החלון אפשר לקבל מידע נוסף על האירוע באמצעות התכונות הבאות: CurrentCharacter שם המקש במקלדת שהוקש לאחרונה כאשר הסמן היה מעל העצם SelectionType שם כפתור העכבר שהוקש כאשר הסמן היה מעל העצם. מקרים נפוצים הם normal עבור כפתור שמאלי, alt עבור כפתור ימני. עמ' 2 מתוך 7
CurrentPoint קואורדינטות הנקודה שבה הוקש על כפתור העכבר כאשר הסמן היה מעל העצם. המספרים ביחידות של העצם )ברירת המחדל היא pixel עבור חלון ו- coordinates data עבור מערכת צירים(. קוד עבור callback הקוד עבור תכונת callback )שיתבצע כשיקרה אירוע( יכול להיות אחד מן הדברים הבאים: 'MATLAB code' א. מחרוזת תווים שמטלב מבצע כאילו הוקלדה בחלון הפקודות. אם המחרוזת כוללת שמות של פונקציה או סקריפט, מטלב מבצע )במידה וזיהה את התוכנית המבוקשת(. אם יש שמות שזוהו כמשתנים במרחב- העבודה הכללי של מטלב, יילקחו הערכים השמורים במשתנים אלה. function-handle ב. מזהה לפונקציה, שמטלב מפעיל עם שני הארגומנטים הבאים: hobject 1. מזהה handle של העצם שבו קרה האירוע. EventStructure 2. מידע על האירוע.)structure( למשל, אירועים הקשורים במקלדת כוללים את השדות הבאים: Character התו כפי שמוצג כתוצאה של כלל המקשים. )למשל.)A=a+SHIFT Key שם המקש )אות קטנה,lowre case או שם עבור מקשים אחרים(. Modifier מערך-תאים עם שמות המקשים הנוספים shift(.)control, alt, function cb(h,ev) לסיכום, שורת הכותרת בפונקציית callback תיראה כך: בדרך כלל המזהה h שימושי, ואילו המידע על האירוע )בארגומנט )ev פחות משמעותי. בכל מקרה יש לשמור על התבנית של שני ארגומנטים. מומלץ להשתמש תמיד בפונקציות )אפשרות ב' למעלה( ולא במנגנון של סקריפט עם משתנים במרחב העבודה הכללי. דוגמא 2: התוכנית d11draw_lines מבצעת ממשק המאפשר למשתמש לציין קואודינטות ולשרטט קווים שבורים בעזרת העכבר. בכל קטע מוצג תחילה קו מרוסק העוקב אחרי תנועת העכבר, ואשר הופך ל"קבוע" לאחר לחיצה על כפתור שמאלי. לחיצה על כפתור ימני מסיימת את הקו השבור כך שלחיצה נוספת על כפתור שמאלי מתחילה קו חדש. הקובץ כולל פונקציה ראשית ושתי פונקציות :callback אחת לתגובה על הקשה על כפתור עכבר ואחת לתגובה על תנועת עכבר. עמ' 1 מתוך 7
עצמים לממשק-משתמש במטלב יש עצמים מוגדרים-מראש לצורך בניית ממשקי משתמש. )אפשר לבנות ממשק גם מעצמים גרפיים פשוטים, אבל כמובן בעבודה רבה יותר(. בניגוד לכלי ממשק שהודגמו עד כה )לדוגמא חלון- תפריט, תיבת דיאלוג או תיבה לבחירת קובץ(, עצמים אלה הם חלק מן החלון הגרפי הקיים )כלומר "ילדים" של ה- figure (. לעצמים אלה יש עיצוב גרפי נתון )אשר ניתן לשלוט על הפרמטרים שלו( וכן התנהגות נתונה. ניתן לשנות במידה מסוימת את התנהגות עצמים אלה, אבל המאפיינים הבסיסיים שלהם נקבעו לצורך שימוש בהתנהגות הנתונה. עצמים לממשק משתמש מתחלקים לשני קבוצות עיקריות: UImenu ו- UIcontrol. עצמים מסוג UImenu הם תפריטים שנמצאים בסרגל התפריטים בראש החלון. בשיעור זה נתאר עצמים מסוג,Uicontrol שיכולים להיות בכל מקום בשטח שמתחת לסרגל התפריטים. להלן כמה סוגים של :Uicontrol Uicontrol type עיצוב העצם והתנהגותו כפתור לחיץ. נלחץ בלחיצה על כפתור עכבר ומשתחרר עם שחרור כפתור העכבר. PushButton לחיצה+שחרור של העכבר מפעילה.callback כפתור-מצבים. הקשה עם העכבר מעבירה אותו בין המצבים "בפנים" ו"בחוץ". כל ToggleButton הקשה מפעילה callback וניתן לבדוק באיזה מצב נמצא. תיבת "אישור". לחיצה עם העכבר מעבירה בין מצב "כן" )בו נמצא סימון בתיבה( CheckBox למצב "לא" )בו התיבה ריקה( וכן מפעילה.callback תפריט "קופץ". במצב לא פעיל, מוצג טקסט שהוא אחת מן האפשרויות. לחיצה PopUpMenu בעכבר מציגה רשימה של האפשרויות שמהן בוחרים. בחירה מפעילה callback ומציגה באופן קבוע את האפשרות שנבחרה. כמו-כן ניתן לבדוק איזו אפשרות מוצגת. Edit תיבת-טקסט. מאפשרת למשתמש לכתוב טקסט. כדי להפעיל,callback יש ללחוץ ENTER או להקיש עם העכבר מחוץ לשטח התיבה. )מומלץ לא להסתמך על כך ולספק גם כפתור הפעלה נפרד(. מתג-הזזה. ניתן להזיזו בעזרת העכבר. מקום המתג "מתורגם" לערך מספרי, כאשר Slider מגדירים מראש גבולות מינימום )צד שמאל( ומקסימום )צד ימין(. שחרור של כפתור העכבר עוצר את תנועת המתג וכן מפעיל,callback וניתן לקבל את הערך המספרי. Text טקסט סטטי. לא ניתן לשינוי ע"י המשתמש ולא מפעיל.callback מיועד להצגת כותרות, משוב וכו'. יצירת UIcontrol יצירת עצם,UIcontrol כאשר ציון סוג hndl=uicontrol('type',type,'p1',v1,'p2',v2) העצם נעשה ע"י תכונת.type Hs=uicontrol('type','slider','min',-3) דוגמא: uicontrol(uihandle) העברת הפוקוס לעצם קיים עם מזהה נתון: עמ' 4 מתוך 7
תכונות רלוונטיות לעצמים מסוג Uicontrol התכונות הבאות משותפות לכל העצמים מסוג UIcontrol אבל משמעותן משתנה בהתאם לסוג העצם: העצם פעיל אם,on לא פעיל אם.off במקרה של off מטלב משנה באופן אוטומטי את חזות העצם )צבעים חוורים לטקסט ורקע(. ערך מספרי. לגבי,slider ערך בהתאם למקום המתג. לגבי,popupmenu מספר האפשרות שנבחרה. לגבי עצמים מסוג כן/לא, מקבל ערך Max אם נבחר וערך Min אם לא נבחר. מחרוזת תווים - בהתאם להקשר: טקסט על כפתור, טקסט המוצג בתיבת עריכה, אפשרויות של תפריט )מערך תאים(. ערכים מספריים. לגבי slider מציין את הערכים המשויכים לגבולות בהם המתג יכול לנוע, לגבי עצמים של כן/לא, כמוסבר למעלה. צעדי-התקדמות של slider )מערך של שני איברים כמוסבר בתיעוד(. צבע טקסט. צבע רקע. Enable Value String Min Max SliderStep ForegroundColor BackgroundColor למידע על סוגי Uicontrol נוספים ותכונות נוספות, יש לחפש בתיעוד.uicontrol propoerties דוגמא 3: התוכנית d11curve_select מבצעת ממשק המאפשר למשתמש לבחור בלחיצת- עכבר עקומות מסוימות מתוך המידע הגולמי. כל עקומה שנבחרת מסומנת בקו עבה. בחירת עקומה אחרת "מסירה את הבחירה" מהעקומה שנבחרה קודם. עם הלחיצה על כפתור Confirm מאושרת בחירת העקומה, והיא מועלמת מהמידע הגולמי )כדי למנוע בחירה חוזרת( ומשורטטת במערכת-הצירים של העקומות שנבחרו. כמו-כן קיימים שלושה :sliders אחד להזזה אופקית, אחד להזזה אנכית, ואחד להתקרבות. שיטות להעברת מידע בין הפונקציות בדרך כלל פונקציית callback משתמשת במידע שאיננו קשור רק לעצם שהפעיל אותה. בנוסף, עצמים שונים קולטים מידע )למשל פרמטרים לחישוב, שמות קבצים לקריאה או לשמירה( שמיועד לשימוש כלל חלקי התוכנית. כאמור למעלה, רצוי להימנע ממשתנים גלובאליים, ולכן יש להשתמש באחת מן השיטות עמ' 5 מתוך 7
הבאות: 1. הגדרת כל הפונקציות המשתפות מידע כפונקציות פנימיות של התוכנית הראשית. באופן זה, משתנים משותפים נגישים לכל הפונקציות שמשתמשות בהם ועדכון באחת מהן משתקף אוטומטית בכל היתר. )כפי שנעשה בדוגמאות 2-1(. 2. שיוך המידע המשותף לתכונת UserData של אחד העצמים, כפי שנעשה בדוגמא 1. 1. שימוש בתכונת :guidata משתנה אחד שניתן לשייכו לחלון גרפי מסוים. 4. שימוש בעצם מסוג application data שניתן לשייכו לכל עצם גרפי )לא רק לחלון(. ניתן לציין סדרת תכונות לפי הגדרת המתכנת, ולתת להן ערכים. set(h,'userdata',mydata) Mydata = get(h,'userdata'); giudata(h,mydata) Mydata = guidata; setappdata(h,'p1',v1) setappdata(h,'p2',v2) v1 = getappdata('p1') strc = getappdata(h) 5. לקבלת מזהים handles של עצמים שנוצרו באתחול, ניתן לציין עבורם תכונת.Tag עקרונית ניתן לאחזר מזהה של כל עצם כזה ע"י חיפוש findobj לפי ערך התג. לנוחיות המתכנת, קיימת במטלב פקודה שמחזירה עבור חלון גרפי את כל המזהים שהוצמדו להם תגים, בתוך :structure שמות התגיות הם שמות השדות, וערכי השדות הם המזהים שלהם משויכים התגים. strc = guihandles דוגמא 4: התוכנית d11curve_guidata מבצעת אותו ממשק משתמש כמו בדוגמא 1. פעולת התוכנית זהה לקודמת, למעט השינויים הבאים: א. כל פונקציות ה- callback הן פונקציות משניות )ולא פנימיות(, ולכן אין להן משתנים משותפים. ב. המידע מועבר בין הפונקציות באמצעות תכונת.guidata ג. מידע על מזהי-העצמים handles( )object מתקבל באמצעות פקודת guihandles לאחר שכל העצמים הרלוונטיים לממשק המשתמש סומנו ע"י תגיות.Tags טיפול בתזמון אירועים מאחר שסדר ביצוע הפונקציות נקבע ע"י פעולות של המשתמש, יכול להיווצר מצב שאירוע קורה כאשר אחד מן ה- callbacks עדיין מתבצע. הסבירות למצב כזה גדלה, כאשר הביצוע כרוך בחישובים ארוכים או בכל פעולה מורכבת אחרת )למשל קליטת נתונים ממערכת חיצונית(. במיוחד יכול להיווצר מצב לא מוגדר, שבו פעולה מתחילה להתבצע ב- callback ובינתים קורה אירוע נוסף המפעיל אותה מחדש. ככלל, כל עוד פונקציית callback מתבצעת באופן שוטף, לא מתאפשרת הפעלה של callback נוסף )גם לא של אותה פונקציה( ואירועים נוספים שקורים בינתים נשמרים ב"תור".)queue( לאחר שהפונקצייה מסיימת, מתבצעות הקריאות ל- callbacks שהושעו, לפי הסדר שבו נכנסו לתור. ניתן להפעיל פונקציה נוספת רק כאשר הפונקציה המתבצעת מגיעה לאחת הפקודות הבאות: drawnow, figure, getframe,.pause, waitfor במקרה זה, מתבצעות הפונקציות שבתור ולאחר שהן מסתיימות חוזרים לבצע את הפונקציה שהופסקה. עמ' 6 מתוך 7
תכונות רלוונטיות לטיפול בתזמון של אירועים המשויכים לעצם גרפי Interruptible BusyAction BeingDeleted אם on אז פונקציות אחרות יכולות להפסיק את ביצוע ה- callback המשויך לעצם )אבל רק אם יש בתוכנית אחת הפקודות שצוינו למעלה(. אם off אז לא ניתן להפסיק את התוכנית. מה לעשות אם קורה אירוע המשויך לעצם ויש פונקציה אחרת שמתבצעת. אם queue אז האירוע נכנס לתור. אם cancel אז האירוע מבוטל. האם העצם נמצא בתהליך מחיקה )אשר יכול להתמשך אם משויך אליו callback שמבצע תהליך ארוך, למשל כתיבה לקובץ(. פקודות רלוונטיות לטיפול בתזמון של אירועים המשויכים לעצם גרפי hndl=gcbo קבלת המזהה handle של העצם שה- callback שלו מתבצע עכשיו. אם אין שמתבצע, התוצאה היא מערך ריק. קבלת המזהה handle של החלון הכולל את העצם שה- callback שלו מתבצע עכשיו. hndl=gcbf דוגמא 5: התוכנית d11neuron_guidata מבצעת ממשק המקבל מן המשתמש פרמטרים לחישוב של פעולת נוירון, ומשרטט את הזרם והמתח כפונקציה של הזמן. כאשר החישוב מתבצע, כל העצמים לקליטת מידע מושבתים, כדי למנוע הפעלה חוזרת של החישוב. עמ' 7 מתוך 7