failing like never before

11Sep/094

Python Animated Progress Bar

I wanted to be able to have an animated progress bar to be displayed in a terminal for my current Python project. There are already various implementations on the Internet of something like what I wanted, but I couldn't find one that was animated. So for the sake of practice, I decided to write my own. In order to be able to have the bar animated, and still allow the program to get other work done, I had to create a seperate thread to manage the status bar.

Code for the bar, (and also sample code to create a demo) are below.

import time
import sys
import os
import threading

"""
Display an animated statusbar, with progress and percentage
( items-completed/items-total )
displayed below the statusbar. Seperate thread is used to
display the spinning "icon." In order to stop the statusbar thread
early, calling thread can use join() 

example output created by StatusBar thread:

[===============\--------------]
30/60  50%

Written by chi (aka chi42) from 42gems.com-- 11 Sept, 2009

Copyright (C) 2009 chi (from 42gems.com)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

Please see http://www.gnu.org/licenses/ for a copy of the license.
"""

class StatusBar(threading.Thread):

    # class variables:
    # max:  number of total items to be completed
    # pos:  number of completed items
    # inc:  amount of items to increment completed 'pos' by
    #           (shared resource)
    # comp: amount of '=' to display in the progress bar
    # running: whether or not the statusbar is running
    def __init__(self, pos=0, max=100):
        threading.Thread.__init__(self)
        self.pos = pos
        self.max = max
        self.busy_char = '|'
        self.running = 0
        self.inc =  0
        self.__getsize()
        self.comp = int(float(self.pos) / self.max * self.columns)
        self.inc_lock = threading.Lock() 

    # find number of columns in terminal
    def __getsize(self):
        rows, columns = os.popen('stty size', 'r').read().split()
        if int(columns) > 80:
            self.columns = 80 - 2
        else:
            self.columns = int(columns) - 2
        return

    # redraw progress bar and all numerial values
    def __print(self):
        self.__getsize()

        sys.stdout.write('\x1b[1G')
        sys.stdout.write('[' + '=' * self.comp + self.busy_char + \
            '-'*(self.columns - self.comp - 1) + ']'   )
        sys.stdout.write('\n\x1b[0K' + str(self.pos) + \
            '/' + str(self.max) + '\t' + \
            str( round(float(self.pos) / self.max * 100, 2)) + '%')
        sys.stdout.write('\x1b[1A\x1b[' + \
            str(self.comp + 2) + 'G')
        sys.stdout.flush()
        return

    # run the thread
    def run(self):
        global busy_chars, inteval
        busy_chars = ['|','/','-','\\']
        interval = 0.3

        self.running = 1 

        self.__print()
        while 1:
            # loop and display the busy spinning icon
            for c in busy_chars:
                self.busy_char = c
                sys.stdout.write(c + '\x1b[1D')
                sys.stdout.flush()
                time.sleep(interval)

                self.inc_lock.acquire()
                if self.inc:
                    if (self.pos + self.inc) >= self.max:
                        self.inc_lock.release()
                        self.pos = self.max
                        self.comp = self.columns
                        self.busy_char = ''
                        self.__print()
                        sys.stdout.write('\n\n')
                        self.running = 0
                        return 0
                    else:
                        self.pos += self.inc
                        self.inc = 0
                        self.inc_lock.release()
                        self.comp = int(float(self.pos) / self.max \
                            * self.columns)
                        self.__print()
                else:
                    self.inc_lock.release()
        return 1

    # increment number of completed items used by calling thread
    def increment(self):
        if self.running:
            self.inc_lock.acquire()
            self.inc += 1
            self.inc_lock.release()
            return 0
        else:
            return 1

Annnd the demo code:

#!/usr/bin/python

import statusbar
import time
import os

print '\n'

min, max = 0, 80
inc_sleep = 3 

bar = statusbar.StatusBar(min, max)

rows, columns = os.popen('stty size', 'r').read().split()
print 'columns in screen: ', columns 

bar.start()
while 1:
    time.sleep(inc_sleep)
    if bar.increment():
        break
print 'done!'
10Mar/090

Muxing the Clock

My final lab project for my digital design class is basically a free-for-all, design your own project. The project that my lab partner and I have chosen includes displaying a countdown timer on four seven-segment displays. We wanted to have the displayed value decrementing once every second, and we also wanted to make it so that the user could pause the counter and change the displayed value with push-buttons. The only problem with this scenario is that since the counters are connected to a 1Hz clock, the speed at which a user can set the displayed values is also 1Hz. This is bad, as we quickly found out, because it is unbearably slow, especially when you want to set a value of 100. So I came up with what I thought was a great idea: mux the clock input on the counters and then use the "set" button as the mux selector. If one of the mux inputs was then a 1Hz signal and the other a 4Hz clock, we'd be able to switch back and forth between the clock signals at will. Like most things I come up with, I thought it was a brilliant idea. And like most of my ideas, it died in the implementation phase.

The results of muxing the clock signal resulted in a buggy counter that would often spaz out whenever the "set" button was pressed. The clock would settle back into a regular pattern after one or two cycles, but the damage would have already been done: nobody wants to have a clock timer that explodes in a mad counting rush every time you press a button. I believe the reason for the bugginess is due to the fact that the rising and falling edges of the 1Hz and 4Hz clock do not coincide in time perfectly, and so when the mux switches clocks the counters see a clock signal that fluctuates at a weird rate for a brief interval.

And one more thing that I've learned in digital design lab: Xilinx software is total crap. Project files get regularly corrupted, the program crashes at odd times, compiling takes so long that I could probably wire the design by hand faster, and the auto-wirer was designed by a disgruntled employee on crack. The quiet murmuring that fills the digital design lab room is frequently broken by loud exclaimations of "I hate this software," "I will find the engineer that designed this auto-wirer and kill him,"  "#$(@*$&(@!***#&!!" and other such phrases (generally not by me). I could maybe understand the total crappiness if Xilinx's ISE was a beta version and avaliable for free, but its not. We're running version 9.2 (somewhere around there) and the school paid buttloads for Xilinx's POS software. Please don't even bother with Xilinx.

10Jan/080

42gems Explained

I looked at my site's URL today and wondered why I chose the domain name “42gems,” and for the life of me I couldn't seem to remember why. Before I go any further, I'd like to describe how and why this site came about.

During the summer of '06, before I started my senior year in high school, I was an intern at Intel Corporation. I worked in Technology Automations and Manageability Services, a subgroup of IT. Essentially, my job was to design web applications. This of course, required that I know some sort of server-side scripting language. My manager gave me two choices, Microsoft .NET or Ruby on Rails. During that time, I knew very little about web programming, my programming experience consisted solely of c/c++, Javascript, and some HTML and CSS (of course, neither HTML or CSS are really programming languages, but I digress). However, even when I was sixteen I still had a dislike for Windows, so I chose Rails over .NET.