FLTKを使用したシンプルなアナログ時計


2

主に 2 FLTK機能と関数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 ();
}

結果:

33秒後:

enter image description here

1分24秒後:

enter image description here

質問:

  1. 上記のコードは問題ないように見えますが、排除できる間違いはありますか?

  2. 時計をよりスムーズに更新する方法。毎秒「点滅」していませんか?

  3. increment_second () / minute () / hour ()内のコメント付き関数redraw ()を使用すると、指定したウィンドウ内で、run_clock ()内のループが終了した後に一度だけクロックが表示されます。一方、現在の実装では、つまり関数draw ()を使用すると、クロックは指定されたウィンドウ内にないことで毎秒更新され、開いている残りのウィンドウと干渉します。


1。演習6 B. Stroustrupの第16章:「C ++プログラミング言語:原則と実践」

2。コンパイルに必要なすべてのファイルはhereです。 FLTK hereにあります。

2

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.