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:
after 1 min 24 seconds:
Questions:
Is the above code looking good enough, are there any mistakes that could be eliminated?
How to make the clock update more smoothly; without "blinking" on every second?
If I use the commented functions
redraw ()
withinincrement_second () / minute () / hour ()
the clock shows, within the specified window, only once after the loops withinrun_clock ()
are over. On the other hand, in the current implementation, i.e. using functionsdraw ()
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.
1 Answer 1
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.
-
\$\begingroup\$ If I've understood right, the first function
timer_callback()
is some kind of wrapper for therepeat_timeout()
, which is a "recursive" function calling its wrapper after it makes one second pause. Functionadd_timeout ()
starts the "recursive" process, right ? \$\endgroup\$Ziezi– Ziezi2015年11月18日 15:33:20 +00:00Commented 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\$Ziezi– Ziezi2015年11月19日 11:40:27 +00:00Commented Nov 19, 2015 at 11:40