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