Tuesday, 18 January 2011

Simple countdown progress bar remake

I've been warned on reddit my countdown progress bar implementation explained here is messy and wouldn't work on Windows OS because changing widgets is valid only from the GUI thread and I must admit, a special thread object for the purpose of updating the widget really looks messy, but I really didn't test it to see if it works on Windows. Today I reworked the code anyway, merging all the previous stuff in one object that builds GUI and acts as a timer at the same time. Also I made some minor improvements. Simple countdown timer is now even more simpler:

 1 import time, gtk 
2 from threading import Thread, Event
4 gtk.gdk.threads_init() # initialize threads right away
6 class cdProgressBar(Thread):
7 def __init__(self, time = 10):
8 Thread.__init__(self)
9 self.time = self.tot_time = time
11 self.builder = gtk.Builder()
12 self.builder.add_from_file('progressbar_countdown.glade')
13 self.window = self.builder.get_object('cd_window')
14 self.progressbar = self.builder.get_object('cd_progressbar')
15 self.set_progressbar()
17 self.builder.connect_signals({
18 'on_cd_window_delete_event' : self.quit,
19 'on_cd_startbutton_clicked' : self.startbutton_clicked,
20 'on_cd_pausebutton_clicked' : self.pausebutton_clicked
21 })
23 # threading
24 self.unpause = Event()
25 self.restart = False
26 self.setDaemon(True) # stop the thread on exit
28 def main(self):
29 self.window.show_all()
30 self.start() # start the thread
31 gtk.main()
33 def quit(self, widget, data = None):
34 gtk.main_quit()
36 def startbutton_clicked(self, widget, data = None):
37 if not self.unpause.isSet():
38 self.unpause.set()
39 self.restart = True
41 def pausebutton_clicked(self, widget, data = None):
42 if self.unpause.isSet():
43 self.unpause.clear() # pause the countdown timer
44 else:
45 self.unpause.set()
47 def set_progressbar(self):
48 self.progressbar.set_text(str(self.time))
49 self.progressbar.set_fraction(self.time/float(self.tot_time))
51 def run(self):
52 while True:
53 self.unpause.wait() # wait the self.unpause.isSet()
54 if self.restart:
55 self.time = self.tot_time
56 self.restart = False
57 self.set_progressbar()
58 time.sleep(1)
59 if self.time != 0:
60 self.time -= 1
62 cd_window = cdProgressBar()
63 cd_window.main()

Countdown Progress Bar is designed using glade interface designer like this. Code is tested and it's working under Windows and under Linux as well.

Saturday, 15 January 2011

Simple countdown progress bar using pygtk

Here I will explain how to build a progress bar that behaves like a simple countdown timer. This kind of progress bar at the start will be 100% filled and will empty itself every second until it's empty and reaches zero. Buttons to start and pause the countdown will be implemented as well. The picture below demonstrates the final product:

For this to work, a background countdown timer thread must be implemented. Its also important to implement pause and restart function for the timer. Here is the full code for the countdownThread object (countdownThread.py):

 1 from threading import Thread, Event 
2 import time
4 class countdownThread(Thread):
5 def __init__(self, callback_func, time = 10):
6 Thread.__init__(self)
7 self.__time = self.__fulltime = time
8 self.__unpause = Event()
9 self.__paused = False
10 self.__restart = False
11 self.__callback_func = callback_func
12 self.setDaemon(True)
14 def pause(self):
15 self.__paused = not self.__paused
16 if self.__paused:
17 self.__unpause.clear()
18 else:
19 self.__unpause.set()
21 def get_time(self):
22 return self.__time
24 def restart(self):
25 if not self.isAlive():
26 self.start()
27 else:
28 self.__restart = True
29 if self.__paused:
30 self.pause()
32 def run(self):
33 self.__unpause.set()
34 while True:
35 if self.__time < 0:
36 self.__time = 0
37 self.__unpause.wait()
38 if self.__restart:
39 self.__time = self.__fulltime
40 self.__restart = False
41 self.__callback_func()
42 time.sleep(1)
43 self.__time -= 1

Countdown timer object is inherited from Thread object, and more functionality is added. Its constructor accepts 2 arguments: callback_func (a callable object) and time (integer). They are used to initialize timer's member variables. The self.__time is total time to start the countdown from, and it should decrement every second by 1; variable self.__fulltime is used to restart the counter.
Furthermore an Event object self.__unpause is used internally for pausing/unpausing the counter. Callback function passed to constructor is function that should execute every one second till time hits zero. In this case it's used to update the progress bar in GUI.
Timer is set to daemon thread using self.setDaemon(True) which means the main thread doesn't need to wait for the timer to quit.
Pause function on line 14 changes the self.__paused, and sets/clears self.__unpause Event object. When this object calls its set() function, it sets its internal flag, and any thread waiting for this event to happen continues its work until the clear() function 'unsets' the internal flag again. In this case. CountdownTimer repeatedly checks the self.__unpause event by calling self.__unpause.wait(). If internal flag is set timer continues the loop, and if it's not it waits for the Event to 'unset' the flag so it can continue the work.
Restart function at line 24 is used to start the thread or restart the counter if the thread is already running. Run function is the timer itself, at the beginning it calls self.__unpause.set(), meaning timer is unpaused at the start. While loop loops till the program exits, performs a couple of checks: sets time to full again if self.__restart is true and sets time to 0 if it tries to go negative. Finally calls the callback function, sleeps a second and decrements time by one.

Next, it is time to build a GUI. I did it using Glade Interface Designer program. Just try to build something similar to the pic above using GtkButton, GtkProgressBar. GtkHBox, and GtkVBox. Important thing is to set the handler names under 'Signals' properties of each button so they can be connected to appropriate callback functions. It should look like something like this.
And here is the code for the gui (gui.py):

 1 import gtk
2 from gtk import gdk
4 from gtkCountdown.countdownThread import countdownThread
6 class cdProgressBar:
7 def __init__(self, time = 10):
8 self.tot_time = time
9 self.cd_thread = countdownThread(self.__countdown_cb, time)
11 self.builder = gtk.Builder()
12 self.builder.add_from_file('progress_countdown.glade')
13 self.window = self.builder.get_object('cd_window')
15 self.progressbar = self.builder.get_object('cd_progressbar')
17 handlers_dict = {
18 'on_cd_window_delete_event' : self.quit,
19 'on_cd_startbutton_clicked' : self.startbutton_clicked,
20 'on_cd_pausebutton_clicked' : self.pausebutton_clicked,
21 }
23 self.builder.connect_signals(handlers_dict)
26 def main(self):
27 self.window.show_all()
28 gdk.threads_init()
29 gtk.main()
31 def startbutton_clicked(self, widget, data=None):
32 self.cd_thread.restart()
34 def pausebutton_clicked(self, widget, data=None):
35 if self.cd_thread.isAlive():
36 self.cd_thread.pause()
38 def quit(self, widget, data=None):
39 gtk.main_quit()
41 def __countdown_cb(self):
42 curr_time = self.cd_thread.get_time()
43 self.progressbar.set_text(str(curr_time))
44 self.progressbar.set_fraction(curr_time/float(self.tot_time))
46 cd_window = cdProgressBar(time = 30)
47 cd_window.main()

Notice I put those files in the same python package called gtkCountdown.
GUI construction needs only one parameter and it's time, which will be used along with self.__countdown_cb callable object to construct a countdown timer thread self.cd_thread. GUI is build and appropriate signals are connected in lines from 11 to 23. Function main() is used to start the gtk main loop. Important thing is to initialize the threads with gdk.threads_init() before gtk.main() is called.
Clicking on the 'start' button, restart() function of the countdown time thread is called, and by clicking on 'pause' button timer is paused. Function __countdown_cb as said before, is used by the timer and executes every second. It gets current time from self.cd_thread, prints its value onto progress bar and updates it. GUI is initialized and ran in last two lines of code.

Notice that this code shouldn't be used as reference. Changing widget from thread outside the GUI thread is probably bad idea.

See the improved countdown progress bar code here

Thursday, 5 August 2010

Python: 'Inhibiting' the gnome-screensaver while watching videos

As user Styelz noted in the last blog post about this, the script with "gnome-screensaver-command -p" uses 100% CPU while running.
So I made a new script for this purpose that uses the "gnome-screensaver-command -i" call. The -i or --inhibit command argument stops/inhibits gnome screensaver and gnome power manager at the same time. Here is the full script:

import subprocess

#run inhibition
ss_inhibit = subprocess.Popen(["gnome-screensaver-command", "-i", "-n",
"mplayer", "-r", "video"])

#run gmplayer
player = subprocess.Popen("gmplayer")

#wait for mplayer to exit

#kill the inhibition

Inhibition command is:

gnome-screensaver-command -i -n mplayer -r video

Options used here are:
-i -> inhibit the screensaver from activating.
-n mplayer -> the calling application that is inhibiting the screensaver is mplayer.
-r video -> reason for inhibiting the screensaver - video

Wednesday, 3 September 2008

Python hacks: stopping gnome-screensaver while watching videos

I got really tired of pressing keys while watching a movie when the screen goes black so today I made those two python scripts.

First one simply shuts down gnome-screensaver daemon, runs gmplayer command, and than turns the screensaver back on after gmplayer exits. Here is the first:


import subprocess

# close gnome-screensaver
subprocess.Popen(["gnome-screensaver-command", "--exit"])

# run gmplayer (put command which runs your favorite video player instead "gmplayer"
player = subprocess.Popen("gmplayer")

# wait till mplayer exits

# start gnome-screensaver again

Module subprocess contains neat Popen object that can run Linux commands within python code. This scripts simple calls gnome-screensaver-commad --exit, gmplayer and gnome-screensaver command sequence like you would type it in the terminal.
But this script has one flaw. It doesn't stop gnome-power-manager so it will put display to sleep according to preferences. More lines can be added here that handles shutting the power manager and than turning it back on... you can do that.

Note: As pointed out by user Styelz, the script below uses 100% CPU cause it constantly polls the subprocess to complete. Lately I made the new script that uses the --inhibit to stop the screensaver and powermanager at the same time. Read about it here

But the smarter idea came to my mind exploring the gnome-screensaver-command. There is a way to notify gnome-screensaver of user activity with the -p (or --poke) option. Next script opens up a player and runs "gnome-screensaver-command -p" in intervals till the video player closes:


import time, subprocess

# set interval in which screensaver should be poked
poke_interval = 540

# which command to use for starting a player
player_command = "gmplayer"

# run player
player = subprocess.Popen(player_command)
except OSError:
print "Could not open player"

# run "gnome-screensaver-command -p" in intervals
t = time.time()

while player.poll() == None:
if t + poke_interval < time.time():
poke = subprocess.Popen(["gnome-screensaver-command","-p"])

Note that this two hacks works only for gnome-screensaver and MPlayer, but you can customize it for your own setup, you just need to figure out the right commands and replace them.

Monday, 18 August 2008

C/C++: Get the CD/DVD volume name

ISO 9660 is a international standard that defines a filesystem for CD/DVD media. To get the volume name from such system in Linux we'll be dealing with the its device path once again. On my system the device path of the CD/DVD device is /dev/scd0.
Also, it is important to know where is this information stored on the CD. Surfing and google-ing around I found the site that explains ISO 9660 in detail: types, modes, sectors etc.
Field of interest (called Volume ID) is located in the sector 16 called Primary Volume Descriptor on the 32808. byte of the CD, and its length is 32 bytes.

Those include directives are important for the compilation:

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include <fcntl.h>

  4. #include <string.h>

  5. #include <unistd.h>

Next, function that can read chunk of data from the filesystem at specific byte is needed:

  1. int fs_read_data ( char * fs, int seek, int len, char * data)

  2. {

  3. unsigned int fd;

  4. int result = -1;

  5. if ( fs != NULL)

  6. {

  7. if ( ( fd = open (fs, O_RDONLY)) != -1)

  8. {

  9. if ( lseek (fd, seek, SEEK_SET) != -1)

  10. {

  11. if ( read ( fd, data, len) != -1)

  12. {

  13. data[len] = '\0';

  14. result = 0;

  15. }

  16. }

  17. }

  18. close ( fd);

  19. }

  20. return result;

  21. }

Function fs_read_data takes four arguments. String fs is a device path of a iso9660 filesystem, variable seek is a byte number at which we start reading data, len is number of bytes to read and data is a character buffer that stores the read data.
If the fs string is NULL, function returns -1 as a result, otherwise the device file is opened and data is being read from it.

Function open (read more) tries to open the device file representing our filesystem (/dev/scd0). Upon successful completion, the function shall open the file and return a non-negative integer representing the lowest numbered unused file descriptor; otherwise, -1 shall be returned. Second argument of the open function says to open the file for rading only.

Function lseek (read more) at line 9 sets the number of bytes to skip for the current file descriptor. In this case the number 32808 will be passed.

Finally, data is being read with the function read (read more) at line 11. It reads the len number of bytes from the file associated with the fd file descriptor and puts that chunk of bytes into data buffer. In our case the len will be 32.

If all the conditions of the if statements are true the function fs_read_data will return 0 and data will be filled. Character '\0' marks the end of the string.

The main function is now constructed like this:

  1. int main() {

  2. char * fs = "/dev/scd0";

  3. int seek =32808;

  4. int len = 32;

  5. char * buff = NULL;

  6. char * volume_name = NULL;

  7. buff = malloc( (sizeof (buff)) * (len+1));

  8. if ( fs_read_data ( fs, seek, len, buff) != -1)

  9. {

  10. if ( (strncmp ( buff, "NO NAME", 7) == 0) || ( strncmp (buff, " ",1) == 0))

  11. {

  12. free ( buff);

  13. buff=NULL;

  14. volume_name = "None";

  15. }

  16. else

  17. {

  18. volume_name = strdup ( buff);

  19. }

  20. }

  21. printf("%s", volume_name);

  22. free (buff);

  23. buff=NULL;

  24. return 0;

  25. }

All important variables are declared in first 7 lines. Notice that malloc function is used to allocate 32 bytes in the buffer buff.
The if statement at line 9 tries to read wanted Volume ID field from the /dev/scd0 device file representing my CD/DVD device. Next, strcmp function is used to check if the string on that field is empty string ("") or it says "NO NAME". If either of that is true the volume name of the ISO 9660 filesystem isn't set, the buff is freed and "None" is assigned to the volume_name char array. Otherwise, the content of the buff is duplicated into volume_name. At the end volume name is printed on the screen.

Friday, 15 August 2008

Convert C++ to syntax colored HTML code with line numbers

Today I made a small C++ program which converts C/C++ code to syntax highlighted HTML code. Changes on the code snippets on previous post are already been made. You can also see the program in action in this post. Like it? :)
This code is not so small so I will not explain it in detail. For the code compilation the Boost C++ and its regular expression libraries must be installed on a system. Boost can be either built from source or installed via some package manager; your choice. When compiling program that uses boost regex (like this one) use the -lboost_regex flag when linking with GCC C++ linker.
OK, here's the code:

  1. #include <iostream>

  2. #include <fstream>

  3. #include <string>

  4. using namespace std;

  5. #include <boost/regex.hpp>

  6. const string help =

  7. "Usage:"

  8. "\n\tccode2html [input file] [output file]"

  9. "\n\tIf output filename is omitted it will be saved as [input file].html\n";

  10. const string pre_expression = "(>)|(<)|(&)";

  11. const string pre_format = "(?1\\&gt;)(?2\\&lt;)(?3\\&amp;)";

  12. const string line_expression = "^.*?$";

  13. const string line_format = "<li>$&</li>";

  14. const string whole_code_expression = "^.*$";

  15. const string whole_code_format = "<pre><ol>$&</ol></pre>";

  16. const string expressions =

  17. // single line comments

  18. "(//.*?(?=</li>))|"

  19. // multi-line comments

  20. "(/\\*.*?\\*/)|"

  21. // string literals

  22. "(\"(?:[^\\\\\"]|\\\\.)*\"|'(?:[^\\\\']|\\\\.)*')|"

  23. // precompile directives

  24. "(#.*?(?=</li>))|"

  25. // floating point numbers

  26. "(\\<[[:digit:]]+\\.[[:digit:]]+)|"

  27. // integer numbers

  28. "(\\<[[:digit:]]+\\>)|"

  29. // boolean literals

  30. "((?:\\<true\\>)|(?:\\<false\\>))|"

  31. // keywords

  32. "(\\<(__asm|__cdecl|__declspec|__export|__far16|__fastcall|__fortran|__import"

  33. "|__pascal|__rtti|__stdcall|_asm|_cdecl|__except|_export|_far16|_fastcall"

  34. "|__finally|_fortran|_import|_pascal|_stdcall|__thread|__try|asm|auto|bool"

  35. "|break|case|catch|cdecl|char|class|const|const_cast|continue|default|delete"

  36. "|do|double|dynamic_cast|else|enum|explicit|extern|false|float|for|friend|goto"

  37. "|if|inline|int|long|mutable|namespace|new|operator|pascal|private|protected"

  38. "|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_cast"

  39. "|struct|switch|template|this|throw|true|try|typedef|typeid|typename|union|unsigned"

  40. "|using|virtual|void|volatile|wchar_t|while|NULL)\\>)";

  41. const string formats =

  42. "(?1<font color = \"#999999\"><i>$&</i></font>)"

  43. "(?2<font color = \"#D3D3D3\">$&</font>)"

  44. "(?3<font color = \"#009900\">$&</font>)"

  45. "(?4<font color = \"#006699\">$&</font>)"

  46. "(?5<font color = \"#996600\">$&</font>)"

  47. "(?6<font color = \"#993366\">$&</font>)"

  48. "(?7<font color = \"#990000\"><b>$&</b></font>)"

  49. "(?8<font color = \"#003399\"><b>$&</b></font>)";

  50. int main (int argc, char* argv[]) {

  51. string input_filenm;

  52. string output_filenm;

  53. if ( argc > 3 || argc == 1 ) {

  54. cout << help;

  55. exit (-1);

  56. }

  57. else if ( argc == 2) {

  58. input_filenm = argv[1];

  59. output_filenm = input_filenm + ".html";

  60. }

  61. else {

  62. string input_filenm = argv[1];

  63. string output_filenm = argv[2];

  64. }

  65. ifstream in ( input_filenm.c_str() );

  66. if (!in.is_open()) {

  67. cout << "Failed to open: " << input_filenm << '\n';

  68. }

  69. else {

  70. cout << input_filenm << " opened successfully\nProcessing...\n";

  71. }

  72. ofstream out ( output_filenm.c_str() );

  73. string in_string;

  74. char c;

  75. while (in.get(c)) {

  76. in_string.append(1,c);

  77. }

  78. boost::regex reg;

  79. // replace <, > and & signs with appropriate html escape characters

  80. reg.assign(pre_expression);

  81. in_string = boost::regex_replace(in_string, reg, pre_format, boost::match_default | boost::format_all);

  82. // add <li> ... </li> tags on each line

  83. reg.assign(line_expression);

  84. in_string = boost::regex_replace(in_string, reg, line_format, boost::match_default |

  85. boost::format_all | boost::match_not_dot_newline);

  86. // format and color code syntax

  87. reg.assign(expressions);

  88. in_string = boost::regex_replace(in_string, reg, formats, boost::match_default | boost::match_default |

  89. boost::format_all);

  90. // add <pre><ol> on start and </ol></pre> at the end of the file

  91. reg.assign(whole_code_expression);

  92. in_string = boost::regex_replace(in_string, reg, whole_code_format);

  93. in.close();

  94. out << in_string;

  95. out.close();

  96. return 0;

  97. }

First three lines include important header files for the program. Second include is required for file i/o operations on lines 72, 73 and 79. On the 6. line the file boost/regex.hpp is included. That file is required for regular expression functions and objects. I used only regex class and regex_replace functions in this code. String help is declared from lines 8 to 11 and it explains the usage of the program if zero or more than two command-line arguments are passed when program is ran.

The heart of the program is in the lines 13-55 and 85-103. See the Boost Regex Documentation for more information about regular expressions used in C++. Many regular expression tutorials and explanations can be found here also. There is even a book dedicated to that topic called: Mastering Regular Expressions which is strongly suggested.

I'll just shortly explain how I used regexes in this program. Two strings are declared at 13. and 14. lines. The first string pre_expression is passed to assign function of the regex class (see 88. line). It describes search pattern, in this case the document is searched for '<','>' and '&' signs that needs to be replaced with the HTML escape sequences. Those escape sequences are passed to the pre_format string inside. The ?1, ?2 and ?3 are representing the sub-expression indexes. After assigning the pre_expression string to reg regex on line 88 the function regex_replace, which takes four arguments, is called.
First argument in_string is the string filled with the whole code from the file. That string is searched with the pre_expression patterns. When a match is found it is replaced with the data in the pre_format string. Last parameter is used for boost specific flags.

First, the '<','>' and '&' signs are replaced with the HTML escape sequence equivalents. The <li> and </li> tags are added at start and end of each line for numbering. Than the code syntax is highlighted with appropriate HTML <font> tags declared in string formats on lines 47-55. Finally <pre> and <ol> and their closing tags are put on the start and the end of the file.

Only problem in this code is regex for multi-line comments. The expression '/\\*.*?\\*/' matches all text between the '/*' and '*/' including <li> and </li> tags inside. Never figured out how to exclude those tags from formating. If some regex professional is reading this please help out!

Wednesday, 13 August 2008

C/C++: Check the filesystem is mounted

I'll write and explain the function that checks the /etc/mtab file for the information if the given device is mounted. For this the default C FILE pointer and the mntent structure will be used. More information about the mntent struct and what elements it holds see this page. Three includes are needed before the coding starts:

  1. #include <stdio.h>

  2. #include <mntent.h>

  3. #include <string.h>

First line doesn't need much explanation; just notice it is needed for the file i/o. Second line includes mntent.h where the mntent structure and functions are declared and the third line includes string.h for string manipulation functions (see the string.h documentation).
Here is the code of the is_mounted function:

  1. int is_mounted (char * dev_path) {

  2. FILE * mtab = NULL;

  3. struct mntent * part = NULL;

  4. int is_mounted = 0;

  5. if ( ( mtab = setmntent ("/etc/mtab", "r") ) != NULL) {

  6. while ( ( part = getmntent ( mtab) ) != NULL) {

  7. if ( ( part->mnt_fsname != NULL )

  8. && ( strcmp ( part->mnt_fsname, dev_path ) ) == 0 ) {

  9. is_mounted = 1;

  10. }

  11. }

  12. endmntent ( mtab);

  13. }

  14. return is_mounted;

  15. }

In first three lines of the is_mounted function file pointer mtab, mntent structure pointer part and integer is_mounted are declared; we assign NULL value to pointers and a zero to the is_mounted integer (assumption is the device isn't mounted). Program would work without assigning NULLs to pointers but it's there for security reasons.

In the fourth line the setmntent function is introduced. This function takes two arguments, first is the path to the mtab file (it's location is /etc/mtab on most Linux systems) and the second is short string determines how the file is opened by the program. In this case file /etc/mtab is trying to be opened for reading only. setmntent function is similar to the fopen function declared in stdio.h.
Next, the getmntent function takes a file pointer as argument, reads a line from the mtab file and fills the mntent structure.
While loop in this code reads the /etc/mtab file line by line and checks if it contains the required information. We are actually comparing given device path char * dev_path with the each of the device paths inside the mtab file (part->mnt_fsname). We are using function strcmp for this. If the mtab contains it that means the filesystem (device) is mounted and we assign number one to our is_mounted variable. Also the mnt_fsname element also must not be NULL.
After the while loop the function endmntent closes the file system description file mtab.
At the end the is_mounted variable is returned.

Now the famous main function can be constructed like this:

  1. int main() {

  2. if (is_mounted("/dev/scd0")) {

  3. printf("CDROM mounted!\n");

  4. }

  5. else {

  6. printf("CDROM not mounted!\n");

  7. }

  8. return 0;

  9. }

The /dev/scd0 is a device path of my cd/dvd device. This should be the same on all Debian based systems. If the device path of the filesystem is unknown this check can still be performed by comparing the mount path with the mnt_dir element of the mntent structure.