x
בניית אתרים בחינם
הפוך לדף הבית
הוסף למועדפים
שלח לחבר
 

האתר סגור

   1    2    3    4    5    6
   7    8    9    10    11    12
   13    15    16    17    18    19
   20    21    22    23        
                       
                       
                       
                       
                       
                       
   
 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    15
    16
    17
    18
    19
    20
    21
    22
    23
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   

איך בונים תוכנה - חלק ז'

 
עידו גנדל

הללויה! הגענו לתכל'ס: כתיבת הקוד. אז בואו נתחיל דווקא מהזזת חלונות. נראה פשוט, נכון? אז זהו, שלא כל כך • לפרק את הבייט

כמעט בכל טור הדגשנו אתהחשיבות של אינטואיטיביות השימוש. ככל שהעבודה עם התוכנה שלכם מובנת מאליה ופשוטה יותר, כך יעילות העבודה תשתפר, והחוויה הסובייקטיבית של המשתמש תהיה חיובית יותר. למעשה, בכל תוכנה טובה יש כמה מאפיינים קטנים שמקלים עלינו מאד את החיים, אבל הם כל כך אינטואיטיביים שאנחנו לא שמים אליהם לב בכלל. אם מערכת ההפעלה שלכם היא Windows בגירסה כלשהי, פתחו את פנקס הרשימות (Notepad). הזיזו אותו לפינת המסך, וסגרו אותו. כעת פתחו אותו שוב. איפה הוא נמצא? במקום האחרון שבו שמתם אותו. מובן מאליו, נכון? אך האם הקדשתם לזה מחשבה אפילו פעם אחת?
 
חלון פתוח (צ': פליקר, Extra Ketchup)
 חלון פתוח (צ': פליקר, Extra Ketchup)   
עבור המתכנתים כמעט שום דבר אינו מובן מאליו. כאשר הקומפיילר הופך את הקוד לתוכנית, פרמטרים כמו גודל ומיקום הטופס נשמרים כפי שהיו בזמן התכנון. בכל פעם שמפעילים את התוכנה מחדש, המשתנים הללו חוזרים לערכם המקורי. התנהגות כזו יכולה להוציא משתמש מדעתו, ומן הראוי לפתור אותה בקוד. אז בסאגת התכנות שלנו מהקל אל הכבד, בואו ונראה איך גורמים לטפסים של התוכנה להיות במקום בו השארנו אותם.

איך זה קורה בפנקס הרשימות?

מעט מאד תוכנות – בעיקר וירוסים, למעשה – נוגעות בקוד שלהן עצמן. ערכים שצריכים לשמור בין הפעלה להפעלה מומלץ מאד לשים בקובץ נפרד. בימים שלפני Windows, השיטה המסורתית לעשות זאת היתה באמצעות קובץ הגדרת תצורה מסוג טקסט רגיל, אך בעל הסיומת ".INI" (מהמילה Initialization, אתחול). קובץ זה נשמר לרוב באותה תיקיה כמו קובץ ה-exe עצמו, ובו נרשמו בפורמט פשוט וקריא כל הפרמטרים הרצויים. כיום, מקובל יותר לרשום נתונים כאלה במערכת הרישום (Registry), ואכן, גם הנתונים של פנקס הרשימות הצנוע מתחבאים שם, כפי שאפשר לראות בתמונה.
 
 
מבחינת תכנות, אופני השימוש בקובץ INI ובמערכת הרישום דומים מאד. בתוכנה שלנו נשתמש דווקא בשיטה הישנה ולא במערכת הרישום, כיוון שכך קל יותר לאתר את הנתונים במקרה הצורך, וזה גם לא מעמיס עוד יותר על מערכת הרישום העמוסה ממילא. לשמחתנו, דלפי מעמידה לרשותנו מספר מחלקות ופקודות פשוטות לניהול של קבצי INI.
 

איך זה קורה בדלפי

מתכנת דלפי יכול לבחור בין שתי מחלקות: TIniFile ו-TMemIniFile, כשההבדל ביניהן הוא שהמחלקה הראשונה רושמת כל שינוי ישירות לקובץ, ואילו השניה טוענת אותו לזיכרון ומבצעת שם את כל השינויים, עד שמפעילים פקודה נוספת שמעדכנת את הקובץ עצמו. כיוון שאנחנו לא מתכוונים לשמור בקובץ הזה נתונים קריטיים, נצמצם את הגישה לדיסק ונשתמש במחלקה TMemIniFile. במקרה הכי גרוע, אם המחשב ייתקע או תהיה הפסקת חשמל, נישאר עם הנתונים ששמרנו בפעם האחרונה בה התוכנה נסגרה כמו שצריך.

לתוכנה יהיה קובץ INI אחד בלבד, והוא רלוונטי מרגע ההתחלה ועד לסגירה. לכן, נציב אותו בתוך מודול התוכנה (Unit) הכללי ביותר, זה שבו מוגדרים כל הפרמטרים הגלובליים והקבועים של התוכנה. בקטע התוכנה הבא, הקובץ ייפתח מייד עם הפעלת ה-exe, והוא יעודכן וייסגר בסיום השימוש בתוכנה. הלוכסן הכפול מסמן הערות.


unit VoronoiGlobals;

interface
uses
SysUtils, IniFiles;

const
IniFileName = 'Voronoi.INI';
IniFileWindowXIdent = 'WindowPosX';
IniFileWindowYIdent = 'WindowPosY';
IniFileWindowWidthIdent = 'WindowPosDX';
IniFileWindowHeightIdent = 'WindowPosDY';

var
IniFile : TMemIniFile;

implementation

initialization

// Assign INI file. If file does not exist,
// it is created automatically.
IniFile := TMemIniFile.Create(IniFileName);

finalization

// Update physical INI file and free object
IniFile.UpdateFile;
FreeAndNil(IniFile);

end.

 
 
כעת יש לנו קובץ INI מוכן, אבל הוא ריק. איך מזינים לתוכו ערכים וקוראים אותם בחזרה? נתחיל בכתיבה. המקום ההגיוני לבצע אותה הוא כאשר טופס נסגר, כי זו נקודת השחזור היחידה שתהיה רלוונטית. אם, למשל, נשמור את הערכים בכל פעם שמזיזים את הטופס, נבצע בעצם הרבה פעולות מיותרות. משתמשים מזיזים טפסים פעמים רבות במהלך השימוש כדי לראות מה קורה מאחוריהם או כדי למצוא את המקום האופטימלי עבורם, ואין צורך לשמור את כל ההתעסקויות הללו.
קובצי INI מחולקים למקטעים (Sections), וכל רישום בהם חייב לציין באיזה מקטע מדובר. הטופס העיקרי של התוכנה נקרא MainForm, ולכן, לצורך הפשטות, נקרא למקטע שלו באותו שם. כאשר טופס נסגר ב-Windows, הוא מקפיץ אירוע (Event) מסוג FormClose. נתעלק על האירוע הזה ונטמין בתוכו את הקוד הדרוש לכתיבת הערכים שלנו. הפקודה הרלוונטית היא WriteInteger, שכותבת ערך מספרי שלם במקטע המבוקש, בתוספת מפתח זיהוי טקסטואלי. אחרי שכתבנו את הפרוצדורה הבאה לניהול אירוע הסגירה (המשתנים Left, Top, Width, Height מוגדרים מראש עבור כל טופס):



Const
MainFormIniFileSection = 'MainForm';

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin

IniFile.WriteInteger(MainFormIniFileSection, IniFileWindowXIdent, Left);
IniFile.WriteInteger(MainFormIniFileSection, IniFileWindowYIdent, Top);
IniFile.WriteInteger(MainFormIniFileSection, IniFileWindowWidthIdent, Width);
IniFile.WriteInteger(MainFormIniFileSection, IniFileWindowHeightIdent, Height);

end;

 
קטעי קוד (צ': פליקר, Nat W)
 קטעי קוד (צ': פליקר, Nat W)   
ואחרי שהפעלנו את התוכנה, הצבנו את החלון פחות או יותר במרכז המסך וסגרנו אותו, התוכן של קובץ ה-INI נראה כך:


[MainForm]
WindowPosX=374
WindowPosDX=577
WindowPosDY=486
WindowPosY=171



כעת, צריך להשתמש בערכים שבו. המקום הנכון לביצוע הקריאה יהיה באירוע הנגדי – אירוע יצירת הטופס. חשוב לבצע זאת מוקדם ככל האפשר, כדי שהמשתמש לא יחוש בהזזת הטופס. לשם כך, ניקח את הפקודה ReadInteger. פקודה זו מקבלת כפרמטר, מלבד שם המקטע והמפתח, גם ערך ברירת מחדל למקרה שהקובץ אינו מכיל את המפתח המבוקש, או שהערך הרשום אינו חוקי. פרוצדורת יצירת הקובץ תיראה כך:


procedure TMainForm.FormCreate(Sender: TObject);
begin

Left := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowXIdent, Left);
Top := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowYIdent, Top);
Width := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowWidthIdent, Width);
Height := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowHeightIdent, Height);

end;



ומעתה והלאה, בכל פעם שנפעיל את התוכנה בנוכחות קובץ ה-INI שלנו, הטופס יופיע בדיוק במקום ובממדים בהם היה בפעם האחרונה. אפשר אפילו לערוך את הקובץ ידנית ולשנות את הפרמטרים הללו.
 
 

רגע, חשבנו על הכל?

עד כאן טוב ויפה, ועיקר העבודה היה פחות לחשוב ויותר להכיר את הפקודות ולהשתמש בהן במקום הנכון. אבל מתכנת רציני צריך לחזות מראש גם מקרים קיצוניים ולהתכונן אליהם. איך יתנהג הקוד שלנו אם המשתמש יסגור את התוכנה דווקא כשהיא במצב מקסימלי (מסך מלא) או ממוזערת לשורות הפקודות? בדקנו את הנושא, והתגלה שכאשר התוכנה ממוזערת אין בעיה: הערכים נשמרים כאילו הייתה פתוחה. לעומת זאת, כשהיא מוגדלת למסך מלא (אתם יודעים, הריבוע הזה למעלה ליד ה-X של הסגירה), הערכים אמנם נשמרים אך הטופס ייפתח בפעם הבאה כטופס רגיל, כזה שאפשר לגרור ולמתוח. כך נוצר מצב שבו הטופס חורג מגבולות המסך משמאל ולמעלה וזה נראה רע. חלק מהתוכנות שבדקנו יודעות לשחזר את מצב החלון במדויק, ואילו אחרות (כמו פנקס הרשימות) מתעלמות ממקרים כאלה ומשחזרות את הגודל ה"רגיל" הקודם. לצורך הפשטות בלבד, נאמץ את הפתרון השני ונוסיף תנאי מיד לפני רישום הערכים בקובץ ה-INI. המשתנה WindowState והערך wsMaximized הם אלמנטים מובנים בדלפי.


// Skip INI changes in case of a maximized form
if WindowState = wsMaximized then Exit;

 
יש אנשים שעובדים עם שני מסכים (צ': פליקר, stevelyon )
 יש אנשים שעובדים עם שני מסכים (צ': פליקר, stevelyon )   
אבל זה עוד לא הכל. ישנם, למשל, אנשים שעובדים עם שני מסכים בשולחן עבודה מורחב. נניח שהם העבירו את טופס התוכנה למסך השני, וכעת הם מפעילים אותה שוב כשהמסך ההוא מנותק. מה שיקרה זה שהיא תחרוג מגבולות שולחן העבודה הנוכחי, ולכו תגלו איפה היא ובעזרת אילו קומבינות מחזירים אותה לטווח הראייה. לכן, נוסיף עוד תנאי לקריאת הערכים: אם הם חורגים מגבולות שולחן העבודה הנוכחי, נחזור להשתמש בערכי ברירת המחדל. אירוע היצירה של הטופס ייראה, כעת, כך (Screen הינו מחלקה מובנית שמוגדרת אוטומטית עבור כל יישום בדלפי):


procedure TMainForm.FormCreate(Sender: TObject);
Var
OldLeft, OldTop : Integer;

begin

OldLeft := Left;
Left := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowXIdent, Left);
if Left > Screen.DesktopWidth then Left := OldLeft;

OldTop := Top;
Top := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowYIdent, Top);
if Top > Screen.DesktopHeight then Top := OldTop;

Width := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowWidthIdent, Width);
Height := IniFile.ReadInteger(MainFormIniFileSection, IniFileWindowHeightIdent, Height);

end;

 
 
ודבר אחד אחרון שחשוב לזכור: לא לכל המשתמשים יש מסכים כמו שלכם. לכן, כאשר אתם מעצבים את הטפסים וקובעים את ערכי ברירת המחדל שלהם, השתמשו בערכים המינימליים ביותר שאפשר, כדי להבטיח שמישהו שעובד ברזולוציה נמוכה לא יפתח את התוכנה שלכם בפעם הראשונה וימצא את הכותרת שלה מימין למטה.
שהיה כיףף חיים!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  היכנס לעריכת כותרת תחתונה לשינוי טקסט זה