We are going to make a digital clock that has LCD Screen looking digits. Not only that. It can also speak the time!
Clock is an essential part of our daily lives. Wherever the new technology taking us, we’re rediscovering the clock but can’t avoid it. We now have smartwatches that shows time, takes photos, receives messages and more.
Okey, all those new technology apart, today we’ll build a small digital clock that sits on your screen and shows time in an LCD Display-like digits. Sounds difficult but it’s not.
For the talking part, we will use TTS (Text to Speech), so no tension there either. If you are interested on how to make the computer speak, check out this tutorial.
So, how can we show LCD looking displays. The answer is there are many digit-looking fonts out there. We can use them. But using those fonts would mean that the font should be installed in the system that will run the program. That seems an extra burden. Especially if you consider cross-platform scenerio.
So I thought of a different approach. I would use an image to store digits. That way it wouldn’t need to install the font and it is possible to use hundreds of images of different fonts without installing them in users’ computer.
The Image
I have come up with this image:
(You can right click and select “Save Image as…” to download and use the image in your project.)
I have downloaded a font that looks like LCD digits, appropriately named “Let’s Go Digital”, from here. Opened photoshop and positioned the digits. Each digit has an imaginery area of 30px x 50px. (Pixels are written as “px” as a short version and generally written as [width] x [height].)
The image has 15 characters/digits/states of that dimension in the image side by side. So the whole image is 450px x 50px. Depending on the time we’ll take certain parts of the image and draw it out in our form. That’s our plan. Simple!
You can download thousands of fonts from the internet and place the digits like the above image. So feel free to try any other! Just remember to keep the characters within the dimensions.
I have saved the image as digits.bmp in an empty folder that I made. We’ll use the folder as the project folder. There is the Photoshop psd file in the project download available with this article, just in case you want to play with it.
The project
Go ahead and start Lazarus.
Create a new Application Project (Project -> New Project -> Applicaiton -> OK).
Now, we would have to save the project. Save the project to the empty directory that you just made. Why are we saving our project that early? Because, we are using the digits.bmp from that folder. If we don’t save the project it would run from the Temp directory and that image would not be found.
Coding a bit
First, define the TBitmap
s that will hold the digits.bmp (digitsBitmap
) and the final clock image (clockBitmap
).
1 | var |
Now, add information about the single digits. Our every digit is 30px x 50px. So we store that value in variables:
1 | var |
Now, we’ll define what’s in our digits.bmp
1 | var |
digitCount
contains the total number of digits in the bmp file. digitPositions
array contains 1 to 15 string items containing each digits’ real string. Now, we know all the information of the charaters in the bmp file.
Double click on the form and enter:
1 | procedure TForm1.FormCreate(Sender: TObject); |
We prepare both the TBitmap
s. We create clockBitmap
with the dimensions of our form. We will draw this exact TBitmap
in the form on its OnPaint
event.
We prepare digitsBitmap
and load the digits.bmp
on it.
We also use drawDigits
, which we didn’t declare yet. We use the procedure to draw an initial text '--:-- '
on the form.
Now select the form and enter the following code in its OnPaint event (Object Inspector -> Events -> OnPaint -> […]):
1 | procedure TForm1.FormPaint(Sender: TObject); |
We draw the clockBitmap
everytime the form is painted. If you don’t know, the form is “painted” everytime the form is resized, gets focus, releaved some part while moving, the form is painted. If we don’t draw the bitmap, the revealing part would be blank!
We use Assigned()
to see if the bitmap is Create
-d (with TBitmap.Create
). If the bitmap is not Create
-d and we try to draw it then it will raise a SIGSEGV exception. So we check it everytime we use it.
Now enter the following code on the form’s OnClose
event:
1 | procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); |
Again, we check with Assigned()
then we use .Free
to free the TBitmap
s we created. (It’s important that we .Free
everything that we .Create
-d. If we fail to do that a memory leak might be created.)
Add the following procedure (before the “end.
“ line, under the implementation
clause):
1 | procedure TForm1.drawDigit(digit:string; X, Y: Integer); |
Now, place your cursor inside the procedure and press Ctrl+Shift+C. That will create a forward declaration under the TForm1
class.
This procedure draws a single digit, given its position, which can be 1
through 15
, at a given position (with X and Y). This procedure is only half of the drawing we would do. We would use it in our drawDigits()
procedure which will use this procedure to draw each single digit.
1 | digitNumber:=15; // the default digit position |
First we loop through every digit position to find the character in the digits.bmp.
Then we prepare 2 TRect
s for CopyRect
. We copy pixels from the appropriate position from the bmp and draw it in our clockBitmap
, to be eventually drawn on the form.
Now add the following procedure:
1 | procedure TForm1.drawDigits(digits:String); |
Like the above procedure, take your cursor inside the procedure and press Ctrl+Shift+C.
Now draw a TTimer
(from System tab) in the form. The Interval
property is set to 1000
. 1000 miliseconds = 1 second. Now double click it and enter:
1 | procedure TForm1.Timer1Timer(Sender: TObject); |
Now Run the project (F9 ot Run -> Run) to see how it looks.
You will see that the form is bigger than the clock. Also, the form is resizable. Set the properties like the following:
1 | Width = 180 |
Now Run the project again.
This time it looks more perfect.
Let it talk!
It would be cool if our little digital clock could talk the time! It is possible, thanks to TTS (Text to Speech). Here is a good wiki about TTS in Lazarus/FPC. We can either use Microsoft Speech API (SAPI) or eSpeak. SAPI is Windows-only. We are going to use SAPI here to keep it simple. You can implement eSpeak with the code very easily.
Add comobj
in your uses
clause:
1 | uses |
We have used compiler directive {$IFDEF MSWINDOWS}
because comobj
is only supported in Windows. So if the program is compiled in a platform other than Windows, it will not be used.
On the Form’s OnClick
event enter:
1 | procedure TForm1.FormClick(Sender: TObject); |
Now Run your project (F9 or Run -> Run).
Click on the form to listen to the time being spoken.
Optionally, add the following 2 lines to make the clock appear at the bottom right part of the screen:
1 | procedure TForm1.FormCreate(Sender: TObject); |
You can also set the FormStyle
of the form to fsStaysOnTop
to set it as a “Always on Top” mode (it won’t disappear even if you focus on other windows).
Now run it and enjoy!
Further experiments
Enhancing a source code is a great way of learning, the fun way. Try these:
- Make the clock borderless and make it movable. Create a right click menu to minimize, always on top etc.
- Implement an alarm feature. That’d be cool.
- Give the user the option to choose “skin” image (a.k.a. digits.bmp). So that they can change the appearance of the clock as they wish. You can also change the values of digitWidth and digitHeight according to the new image, so that users can use skins of different sizes.
- Implement eSpeak for cross-platform compatibility. Hint: you can use something like RunCommand()
and {$ELSE}
to implement it.
Downloads
You can download the source code for the tutorial project and executable/EXE files from the links below: