Problem
This is in response to Nitro7's post titled "Countdown Timer".
In Part 1, I've presented a solution using Gtk::timeout_add()
.
However, I find that sometimes when the application or system is too busy, the timeout event doesn't always trigger at exactly one second interval. (Remember all the events always need to line up in an events queue.) As a result, the timer is not too accurate after running for a while.
In this Part 2, I present a more accurate version of the countdown timer as shown below:
Solution
- We make use of the same technique as described in Part 1.
- The only difference is that when the timer is first started, we take note of the initial time.
- Thereafter, in the signal handler of the timeout event, we calculate the exact time elapsed since the initial time.
- Note that in the example below, I still maintain the variable
$time_left
we had used in Part 1. Take a look at the command window. You can see the difference between the actual time elapsed and$time_left
. You have to have a busy system, and you have to let it run for some time, before you can see any noticeable difference.
Sample Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 72 73 76 77 78 79 80 81 82 83 84 87 88 89 90 91 92 94 95 96 97 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 115 116 117 118 119 120 121 122 | <?php $window = new GtkWindow(); $window->set_title($argv[0]); $window->set_size_request(400, 175); $window->connect_simple('destroy', array('Gtk','main_quit')); $window->add($vbox = new GtkVBox()); // display title $title = new GtkLabel(" Set up a countdown timer\n". "Part 2 - A more accurate timer"); $title->modify_font(new PangoFontDescription("Times New Roman Italic 10")); $title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff")); $title->set_size_request(-1, 40); $vbox->pack_start($title, 0, 0); $vbox->pack_start(new GtkLabel(), 0, 0); $vbox->pack_start(new GtkLabel('Click the button to start the timer'), 0, 0); $vbox->pack_start(new GtkLabel('When the time is up, an alert message is displayed.'), 0, 0); $vbox->pack_start(new GtkLabel(), 0, 0); $vbox->pack_start($hbox = new GtkHBox(), 0, 0); $hbox->pack_start($button = new GtkButton('Start timer'), 1, 0); $vbox->pack_start(new GtkLabel()); $button->connect('clicked', 'start_timer'); $window->show_all(); Gtk::main(); // display an alert dialog box function alert($msg) { $dialog = new GtkDialog('Alert', null, Gtk::DIALOG_MODAL); $dialog->set_position(Gtk::WIN_POS_CENTER_ALWAYS); $top_area = $dialog->vbox; $top_area->pack_start($hbox = new GtkHBox()); $stock = GtkImage::new_from_stock(Gtk::STOCK_DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG); $hbox->pack_start($stock, 0, 0); $hbox->pack_start(new GtkLabel($msg)); $dialog->add_button(Gtk::STOCK_OK, Gtk::RESPONSE_OK); $dialog->set_has_separator(false); $dialog->show_all(); $dialog->run(); $dialog->destroy(); } function start_timer($button) { $do_long_task = new DoLongTask(); $do_long_task->total_time = 1.5 * 60; // note 1 $do_long_task->time_left = 1.5 * 60; // note 2 $do_long_task->process_task(); $do_long_task->timeout_ID = Gtk::timeout_add(1000, array(&$do_long_task, 'process_task')); } class DoLongTask { var $progress; var $dialog; var $time_left; // note 2 var $subtask_count = 0; var $max_task_count = 10; var $timeout_ID; var $start_time; function DoLongTask() { // setup a dialog containing progress bar $dialog = new GtkDialog('Timer', null, Gtk::DIALOG_MODAL); // create a new dialog $top_area = $dialog->vbox; $dialog->set_size_request(200, 60); $this->progress = new GtkLabel(); $this->progress->modify_font(new PangoFontDescription("Times New Roman Italic 20")); $top_area->pack_start($this->progress); $dialog->set_has_separator(false); $dialog->show_all(); // show the dialog $this->dialog = $dialog; // keep a copy of the dialog ID $dialog->connect('delete-event', array( &$this, "on_delete_event")); $this->start_time = microtime(1); // note 3 } // this is where you process your task function process_task() { $elapsed_time = microtime(1)-$this->start_time; // note 4 $remaining_time = $this->total_time - $elapsed_time; $this->progress->set_text(date('i:s', round($remaining_time))); echo "remaining_time = $remaining_time ($this->time_left)\n"; --$this->time_left; while (Gtk::events_pending()) {Gtk::main_iteration();} if ($remaining_time>=0) { return true; // not yet! } else { alert("time's up!"); $this->dialog->destroy(); // yes, all done. close the dialog Gtk::timeout_remove($this->timeout_ID); return false; } } // function that is called when user closes the timer function on_delete_event($widget, $event) { $this->dialog->destroy(); Gtk::idle_remove($this->timeout_ID); // any other clean-up that you may want to do return true; } } ?> |
Output
As shown above.Explanation
The above code is based on How to set up a countdown timer - Part 1?
What's new here:
- Set the time in seconds. Here I set it at 1.5 minutes, or 90 seconds.
- We maintain these variables we have used in Part 1 so that you can see a difference between the two version. (Note: Please see the command window where it shows the differencces.)
- Take note of the initial time.
- Calculate the actual time elapsed.
Related Links
- How to set up a countdown timer - Part 1?
- How to set up a countdown timer - Part 3 - accurate to hundredth of a second?
- How to set up a stopwatch accurate to hundredth of a second?
- How to display progress bar while processing long task - Part 2 using_idle_add?
- How to display progress bar while processing long task - Part 3 - auto begin processing when program starts?
- How to display a popup alert for required fields - Part 1?
Read more...