3
\$\begingroup\$

I'm implementing a graphical representation of an "analog clock" with moving hands (seconds, minutes and hours), using mainly2 FLTK facilities and the function Sleep(miliseconds):

main.cpp:

#include "iostream"
#include "GUI.h"
#include "Window.h"
using namespace Graph_lib;
#include "AnalogClock.h"
int main () 
try {
 Analog_clock ac (Point (700,30), "Analog clock"); 
 return gui_main ();
} catch (exception &e) {
 cerr << e.what () << endl;
 getchar();
}

AnalogClock.h:

#pragma once
#define PI 3.14159265
/*
class Analog_clock
It creates a GUI representing an analog clock
with three indicators: hour, minute, second 
and a fancy dial.
*/
class Analog_clock: public Window {
public:
 Analog_clock (Point xy, const string &label);
private:
 // background image
 Image clock_dial;
 // data representing second, minute and hour
 Line* second_indicator;
 Line* minute_indicator;
 Line* hour_indicator;
 // helper functions
 Point rotate (Point initial, Point pivot, double angle);
 void set_clock ();
 void run_clock ();
 // action functions
 void increment_second ();
 void increment_minute ();
 void increment_hour ();
 // callback functions
 /*
 typedef void* Address;
 template<class W> W& reference_to (Address pw) {
 return *static_cast<W*>(pw);
 }
 */
 static void cb_seconds (Address, Address pw) { reference_to<Analog_clock>(pw).increment_second (); }
 static void cb_minutes (Address, Address pw) { reference_to<Analog_clock>(pw).increment_minute (); }
 static void cb_hours (Address, Address pw) { reference_to<Analog_clock>(pw).increment_hour (); }
};
//------------------------------------------------------------------------------------------------------------------------
// class member implementations
/*
class constructor: Analog_clock()
It initializes a window containing
an image (dial) and three lines
(indicators), together with a 
function that runs the clock utilizing
the machine clock.
*/ 
Analog_clock::Analog_clock (Point xy, const string &label)
 : Window (xy, 480, 460 , label),
 clock_dial (Point (0, 0), "Chapter16Exercise6.gif"),
 second_indicator (nullptr),
 minute_indicator (nullptr),
 hour_indicator (nullptr)
{
 attach (clock_dial);
 set_clock ();
 run_clock ();
}
// helper function
/*
Member function: rotate ();
Use: -
It is used to rotate the clock indicators
around the center point at an angle 
corresponding to second, minute and hour.
*/
Point Analog_clock::rotate (Point initial, Point pivot, double angle) {
 return Point((cos(angle) * (initial.x - pivot.x)) - (sin(angle) * (initial.y - pivot.y)) + pivot.x,
 (sin(angle) * (initial.x - pivot.x)) + (cos(angle) * (initial.y - pivot.y)) + pivot.y);
}
/*
Member function: set_clock ()
Use: -
It initializes the data members
representing clock indicators to
initial value: pointing at 12 o'clock.
*/
void Analog_clock::set_clock () {
 Point clock_center (x_max () / 2. - 2, y_max () / 2.);
 // set seconds
 const int second_indicator_length = 150;
 Point twelve_o_clock_s (x_max () / 2. - 2, y_max () / 2. - second_indicator_length);
 second_indicator = new Line (clock_center, twelve_o_clock_s); 
 second_indicator->set_style (Line_style (Line_style::solid, 2));
 second_indicator->set_color (Color::red);
 // set minutes
 const int minute_indicator_length = 150;
 Point twelve_o_clock_m (x_max () / 2. - 2, y_max() / 2. - minute_indicator_length);
 minute_indicator = new Line (clock_center, twelve_o_clock_m);
 minute_indicator->set_style (Line_style (Line_style::solid, 8));
 // set hours
 const int hour_indicator_length = 50;
 Point twelve_o_clock (x_max () / 2. - 2, y_max () / 2. - hour_indicator_length);
 hour_indicator = new Line (clock_center, twelve_o_clock);
 hour_indicator->set_style (Line_style (Line_style::solid, 8));
 // attach in the right order
 attach (*minute_indicator);
 attach (*hour_indicator);
 attach (*second_indicator);
}
/*
Member function: run_clock ()
Use: -
It updates the clock time by
invoking the functions responsible
for the rotation of the indicators
at a specific interval defined with
the help of the functions: clock ()
and sleep ().
*/
void Analog_clock::run_clock () {
 // get real time and set the clock
 // ...
 // run the clock
 while (true) {
 for (auto i = 0; i < 60; i++) {
 for (auto i = 0; i < 60; i++) {
 cb_seconds (0, this); 
 Sleep (1000); 
 }
 cb_minutes (0, this); 
 }
 cb_hours (0, this);
 }
}
// action functions
/*
Member function: increment_second ()
Use: -
It increments the second indicator by
rotating the line that represents it
by an angle of 6 degrees.
*/
void Analog_clock::increment_second () {
 Point center = second_indicator->point (0);
 Point old_time = second_indicator->point (1);
 // rotate 6 degrees (6 degrees x 60 seconds = 360 one rotation)
 double angle_radians = ((6.) * PI) / 180.;
 Point new_time = rotate (old_time, center, angle_radians);
 // delete old indicator, create new one and attach it
 detach (*second_indicator);
 second_indicator = new Line (center, new_time);
 second_indicator->set_style (Line_style (Line_style::solid, 2));
 second_indicator->set_color (Color::red);
 attach (*second_indicator);
 // redraw ();
 draw();
}
/*
Member function: increment_minute ()
Use: -
It increments the minute indicator by
rotating the line that represents it
by an angle of 6 degrees.
*/
void Analog_clock::increment_minute () {
 Point center = minute_indicator->point (0);
 Point old_time = minute_indicator->point (1);
 // rotate 6 degrees (6 degrees x 60 seconds = 360 one rotation)
 double angle_radians = ((6.) * PI) / 180.;
 Point new_time = rotate (old_time, center, angle_radians);
 // delete old indicator, create new one and attach it
 detach (*minute_indicator);
 minute_indicator = new Line (center, new_time);
 minute_indicator->set_style (Line_style (Line_style::solid, 8));
 attach (*minute_indicator);
 // redraw ();
 draw();
}
/*
Member function: increment_hour ()
Use: -
It increments the hour indicator by
rotating the line that represents it
by an angle of 30 degrees.
*/
void Analog_clock::increment_hour () {
 Point center = hour_indicator->point (0);
 Point old_time = hour_indicator->point (1);
 // rotate 6 degrees (6 degrees x 60 seconds = 360 one rotation)
 double angle_radians = ((30.) * PI) / 180.;
 Point new_time = rotate (old_time, center, angle_radians);
 // delete old indicator, create new one and attach it
 detach (*hour_indicator);
 hour_indicator = new Line (center, new_time);
 hour_indicator->set_style (Line_style (Line_style::solid, 8));
 attach (*hour_indicator);
 // redraw ();
 draw ();
}

Result:

after 33 seconds:

enter image description here

after 1 min 24 seconds:

enter image description here

Questions:

  1. Is the above code looking good enough, are there any mistakes that could be eliminated?

  2. How to make the clock update more smoothly; without "blinking" on every second?

  3. If I use the commented functions redraw () within increment_second () / minute () / hour () the clock shows, within the specified window, only once after the loops within run_clock () are over. On the other hand, in the current implementation, i.e. using functions draw () the clock updates every second by not in the specified window and it interferes with the rest of the open windows.


1. Exercise 6 Chapter 16 from B. Stroustrup's: "C++ Programming Language: Principles and Practice."

2. All the needed files for compilation are here. The FLTK can be found here.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Nov 18, 2015 at 12:34
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

you shouldn't be using an infinite loop and sleep in a gui, instead use the timer functionality of FLTK using Fl::add_timeout and Fl::repeat_timeout

void timer_callback(void* window){
 reinterpret_cast<Analog_clock*>(window)->increment_second();
 Fl::repeat_timeout(1, timer_callback, window);
}
void Analog_clock::run_clock () {
 Fl::add_timeout(1, timer_callback, this);
}
Analog_clock::increment_second(){
 //will also handle seconds overflow so minute and hour hand can be updated
}

Many gui libraries use a single thread and callbacks to manage events and draws, if you block in one of the callbacks then it can't handle any other event while it is blocking. This was the reason redraw didn't work.

repeat_timeout is like add_timeout except that it adds t (the time until callback should happen) to the time the current callback (should have) happened instead of now(). This way the time between timer_callback getting called and it calling repeat_timeout is not a factor.

answered Nov 18, 2015 at 12:45
\$\endgroup\$
2
  • \$\begingroup\$ If I've understood right, the first function timer_callback() is some kind of wrapper for the repeat_timeout(), which is a "recursive" function calling its wrapper after it makes one second pause. Function add_timeout () starts the "recursive" process, right ? \$\endgroup\$ Commented Nov 18, 2015 at 15:33
  • \$\begingroup\$ It worked nicely, I just made void timer_callback(void* window), static and integrated it into the class. \$\endgroup\$ Commented Nov 19, 2015 at 11:40

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.