Chapter 27 Exercises: Introduction to Lazarus
These exercises reinforce the concepts introduced in Chapter 27. Part A covers conceptual understanding, Part B focuses on exploration and analysis, and Part C provides coding challenges.
Part A: Conceptual Understanding
A.1. Explain the difference between sequential programming and event-driven programming. Give a real-world analogy (not the restaurant analogy from the chapter) that illustrates the difference.
Guidance
Sequential programming is like following a recipe step by step: do step 1, then step 2, then step 3. Event-driven programming is like being a receptionist: you sit at your desk and respond to whatever happens next — a phone rings, a visitor arrives, an email notification appears. You cannot predict the order of events, and you must handle each one appropriately when it occurs. The key insight is the inversion of control: in sequential programming, the program decides what happens next; in event-driven programming, the user (or the environment) decides.A.2. What is the message loop? Why is it important that event handlers complete quickly? Describe what happens to the user experience if an event handler takes 5 seconds to complete.
Guidance
The message loop is the central while-loop that retrieves messages (events) from the operating system's message queue, translates them, and dispatches them to the appropriate component handlers. While an event handler is running, the message loop is blocked — it cannot process any other messages. If a handler takes 5 seconds, the application cannot repaint its window (it appears frozen), cannot respond to mouse clicks or keyboard input, and the operating system may display a "Not Responding" message. The window may turn white or gray because paint messages are not being processed.A.3. Explain the Property-Method-Event (PME) model using a TButton as your example. Name at least three properties, two methods, and three events of a button.
Guidance
Properties: Caption (the text on the button), Enabled (whether it can be clicked), Visible (whether it appears on the form), Width, Height, Font. Methods: SetFocus (gives the button keyboard focus), Hide (makes it invisible), Show (makes it visible). Events: OnClick (fires when clicked), OnMouseEnter (fires when the mouse moves over it), OnKeyPress (fires when a key is pressed while the button has focus). The PME model means every component has state (properties), behavior (methods), and responsiveness (events).A.4. What is a widgetset in the LCL architecture? Why does the LCL use widgetsets instead of drawing its own controls?
Guidance
A widgetset is the platform-specific layer that translates LCL component calls into native operating system widgets. The win32 widgetset creates Windows API controls, the cocoa widgetset creates macOS Cocoa controls, and the gtk2/qt5 widgetsets create GTK or Qt controls on Linux. The LCL uses widgetsets instead of drawing its own controls because native controls look and behave exactly as users expect on each platform. A native macOS button has the correct appearance, animation, and accessibility behavior. A custom-drawn button would look foreign and miss platform-specific behaviors.A.5. List all the file types in a Lazarus project and explain the purpose of each. Which files should be committed to version control, and which should be ignored?
Guidance
.lpr (project file — main program entry point), .pas (unit source files — your code), .lfm (form design files — visual layout), .lpi (project information — compiler options, XML format), .lps (session file — editor state, breakpoints), .compiled (compilation tracking), lib/ directory (compiled object files). Commit: .lpr, .pas, .lfm, .lpi. Ignore: .lps, .compiled, lib/, backup/ directories. The .lpi file is debatable — some teams commit it for consistent build settings, others regenerate it.Part B: Exploration and Analysis
B.1. Open Lazarus and create a new Application project. Without writing any code, explore the Object Inspector for the main form (Form1). List at least 10 properties you can find, and for each one, change it and observe the effect. Document what you found.
Guidance
Properties to explore: Caption (changes title bar text), Color (changes form background), Width/Height (changes form size), Position (poDesktopCenter centers the form), BorderStyle (bsSingle removes resize handles, bsNone removes the title bar entirely), AlphaBlend + AlphaBlendValue (makes the form semi-transparent), Font (changes default font for all child controls), ShowInTaskbar (controls taskbar visibility), WindowState (wsMaximized starts maximized), Constraints.MinWidth/MinHeight (prevents the form from being resized too small).B.2. Create a form with three buttons. Assign the same OnClick event handler to all three buttons. Inside the handler, use the Sender parameter to display a message that identifies which button was clicked. Test with Sender = Button1, Sender = Button2, etc. Then try using (Sender as TButton).Caption to display the button's caption dynamically.
Guidance
Create the shared handler by writing it for Button1, then in the Object Inspector for Button2 and Button3, select the Events tab, click the OnClick dropdown, and choose the existing handler (e.g., Button1Click). In the handler:
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('You clicked: ' + (Sender as TButton).Caption);
end;
The as operator performs a safe typecast. This demonstrates that the Sender parameter enables reusable event handlers.
B.3. Open the .lfm file of your project (right-click the form, choose "View Source (.lfm)"). Study the format. Add a new TLabel by editing the .lfm file directly (add the object block by hand), then save and switch back to the form designer. Did the label appear? What did you learn about the relationship between the .lfm file and the visual designer?
Guidance
You can add a label by inserting a block like:object Label2: TLabel Left = 50 Height = 20 Top = 50 Width = 100 Caption = 'Added by hand!' endAfter saving, you also need to add
Label2: TLabel; to the form class declaration in the .pas file. This exercise demonstrates that the .lfm file is the authoritative description of the form layout and that the IDE designer is just a visual editor for this text file. It also demonstrates that the .pas and .lfm files must stay in sync.
Part C: Coding Challenges
C.1. Click Counter (Easy) Build a Lazarus application with a single button and a label. The label displays a number starting at 0. Each time the button is clicked, the number increments by 1. Add a second button that resets the counter to 0.
Hints
Use a private fieldFCount: Integer in the form class to track the count. In the increment handler: Inc(FCount); Label1.Caption := IntToStr(FCount);. In the reset handler: FCount := 0; Label1.Caption := '0';. Initialize FCount := 0 in the FormCreate handler.
C.2. Temperature Converter (Easy) Build an application with a TEdit for entering a temperature, a TComboBox for selecting the conversion direction (Celsius→Fahrenheit or Fahrenheit→Celsius), a TButton labeled "Convert," and a TLabel that displays the result. Include validation that shows an error message if the input is not a valid number.
Hints
UseTryStrToFloat for validation. The formulas are: F = C × 9/5 + 32 and C = (F - 32) × 5/9. Use Format('%.1f', [Result]) for clean output. Add the two options to the combo box in FormCreate and set ItemIndex := 0 for a default selection.
C.3. Color Mixer (Intermediate) Build an application with three TTrackBar controls (sliders) representing Red, Green, and Blue values (0–255). As the user moves any slider, a TPanel should update its background color to reflect the current RGB combination. Display the hex color code (e.g., #FF8040) in a TLabel. Add a "Copy to Clipboard" button.
Hints
Use theOnChange event of each TTrackBar. The color formula is: Panel1.Color := RGBToColor(TrackBarR.Position, TrackBarG.Position, TrackBarB.Position);. For the hex code: Format('#%.2X%.2X%.2X', [R, G, B]). For clipboard: uses Clipbrd; then Clipboard.AsText := HexCode;.
C.4. Stopwatch (Intermediate) Build a stopwatch application using a TTimer component (set its Interval to 100 for 10th-of-a-second precision). Display elapsed time as MM:SS.T (minutes, seconds, tenths). Include Start, Stop, and Reset buttons. The Start and Stop buttons should toggle (when running, Start is disabled and Stop is enabled, and vice versa).
Hints
Track elapsed time as an integer counting tenths of seconds. In the timer handler:Inc(FTenths) and update the label with Format('%.2d:%.2d.%d', [FTenths div 600, (FTenths div 10) mod 60, FTenths mod 10]). Enable/disable buttons by setting their Enabled property. Start the timer with Timer1.Enabled := True.
C.5. PennyWise Enhancement: Running Balance (Intermediate) Extend the PennyWise GUI v1 from the chapter checkpoint by adding a fifth column to the grid: "Balance." After each expense is added, this column should show the cumulative total up to that row. Also add a TComboBox filter that lets the user select a category, and when a category is selected, highlight (change the background color of) all rows matching that category.
Hints
For the running balance, calculate it inbtnAddClick: sum all amounts from row 1 to FNextRow. For category highlighting, use the OnDrawCell event of TStringGrid: if the cell's row has the selected category, set sgExpenses.Canvas.Brush.Color := clYellow before calling sgExpenses.DefaultDrawCell. Call sgExpenses.Invalidate when the filter combo box changes to trigger a repaint.
Part M: Metacognitive Exercises
M.1. The transition from sequential to event-driven programming is a threshold concept. On a scale of 1 to 5, how comfortable are you with the idea that your code does not run "top to bottom" but instead responds to events? What specific aspect is most confusing?
M.2. When you built your first GUI application, what surprised you most? Was it easier or harder than you expected? If you have used a different GUI framework before, how does the Lazarus experience compare?
M.3. The chapter emphasized separating business logic from UI code. Look at the PennyWise checkpoint code. Where is the business logic? Where is the UI code? If you wanted to change the UI (e.g., replace the grid with a list view), what would you need to change, and what could stay the same?