updated on Thu Jan 26 16:09:46 UTC 2012
[aur-mirror.git] / knubbler / knubbler.py
blobabfebe8571933f8fa46301fb198db4a289657d78
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 # Part of knubbler
5 # License: CC-BY-NC-SA-3.0
6 # http://creativecommons.org/licenses/by-nc-sa/3.0/de/
7 # http://creativecommons.org/licenses/by-nc-sa/3.0/de/deed.en
8 # Johann Klähn <kljohann@gmail.com>
10 import sys
11 import gtk
12 import re
14 from optparse import OptionParser
16 def float_to_percent(flt):
17 return (("%.2f" % flt).replace('.', '').lstrip("0") or "0")+"%"
20 class Scroller(object):
21 # defaults that will be overridden by instances
22 exitstatus = 0
24 def __init__(self, fraction=0.5, title=None, factor=20):
25 # Interface {{{
26 self._window = gtk.Window()
27 self._window.set_title('Knubbler')
29 # Cancel when closed by window-manager
30 self._window.connect('delete-event', lambda *a, **k: self.exit(1))
32 # Put ProgressBar in EventBox to capture Scroll & Mouse-Click
33 eventbox = gtk.EventBox()
34 self._progress = gtk.ProgressBar()
35 self._progress.set_property('show-text', True)
37 # Add widgets to window
38 eventbox.add(self._progress)
40 if title:
41 # Display text above progressbar
42 label = gtk.Label(title)
43 label.set_alignment(0.5, 0.5)
44 label.set_justify(gtk.JUSTIFY_CENTER)
45 label.set_use_markup(True)
46 label.set_line_wrap(True)
48 vbox = gtk.VBox()
49 vbox.pack_start(label, expand=False, padding=5)
50 vbox.pack_end(eventbox)
52 self._window.add(vbox)
53 else:
54 self._window.add(eventbox)
55 # }}}
57 # Atach event-handlers:
58 self._progress.connect("realize",
59 lambda w: w.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CIRCLE)))
60 eventbox.connect("button-press-event", self.on_button_press_event)
61 eventbox.connect("motion-notify-event", self.on_motion_notify_event)
62 eventbox.connect('scroll-event', self.on_scroll_event)
63 self._window.connect('key-press-event', self.on_key_press_event)
65 # Set value of progressbar, initialize everything else
66 # (most of this is done by properties/set_x, see below)
67 self._factor = float(factor)
68 self.fraction = fraction
69 self.orientation = gtk.PROGRESS_BOTTOM_TO_TOP
71 def window(self, width=150, height=250):
72 self._window.unfullscreen()
73 self._window.set_size_request(width, height)
74 self._window.set_position(gtk.WIN_POS_CENTER)
76 def fullscreen(self):
77 self._window.fullscreen()
79 # Properties {{{
80 def set_orientation(self, orientation):
81 assert orientation in [gtk.PROGRESS_LEFT_TO_RIGHT,
82 gtk.PROGRESS_BOTTOM_TO_TOP], "Only LTR/BTT"
83 self._progress.set_orientation(orientation)
84 self._orientation = orientation
86 def get_orientation(self):
87 return self._orientation
89 orientation = property(lambda s: s.get_orientation(),
90 lambda s, v: s.set_orientation(v))
92 def set_fraction(self, value):
93 value = max(0, min(1, value))
94 # round to steps of 1/factor
95 self._fraction = round(value * self._factor)/self._factor
96 self._progress.set_fraction(self._fraction)
98 def get_fraction(self):
99 return self._fraction
101 fraction = property(lambda s: s.get_fraction(),
102 lambda s, v: s.set_fraction(v))
104 def tick_up(self):
105 self.fraction += 1.0/self._factor
107 def tick_down(self):
108 self.fraction -= 1.0/self._factor
109 # }}}
111 def exit(self, code=0):
112 self.exitstatus = code
113 self._window.hide()
114 gtk.main_quit()
116 def call(self):
117 self._window.show_all()
118 gtk.main()
119 return self.exitstatus or 0
121 # Event-Handlers {{{
122 def on_scroll_event(self, widget, event):
123 # Modify progessbar according to scrolling
124 if self.orientation == gtk.PROGRESS_LEFT_TO_RIGHT:
125 # Horizontal scrolling
126 if event.direction == gtk.gdk.SCROLL_RIGHT:
127 self.tick_up()
128 elif event.direction == gtk.gdk.SCROLL_LEFT:
129 self.tick_down()
130 elif self.orientation == gtk.PROGRESS_BOTTOM_TO_TOP:
131 # Vertical scrolling
132 if event.direction == gtk.gdk.SCROLL_UP:
133 self.tick_up()
134 elif event.direction == gtk.gdk.SCROLL_DOWN:
135 self.tick_down()
137 def on_button_press_event(self, widget, event):
138 if event.button == 3:
139 # Set distinct value by right-clicking
140 if self.orientation == gtk.PROGRESS_LEFT_TO_RIGHT:
141 total = self._progress.allocation.width
142 self.fraction = max(0, min(1,
143 event.get_coords()[0] / float(total)))
144 elif self.orientation == gtk.PROGRESS_BOTTOM_TO_TOP:
145 total = self._progress.allocation.height
146 self.fraction = 1 - max(0, min(1,
147 event.get_coords()[1] / float(total)))
148 elif event.button == 1:
149 # accept current value on mouseclick
150 self.exit(0)
152 def on_motion_notify_event(self, widget, event):
153 if event.get_state() == gtk.gdk.BUTTON3_MASK:
154 # Right click-and-hold to modify value
155 if self.orientation == gtk.PROGRESS_LEFT_TO_RIGHT:
156 total = self._progress.allocation.width
157 self.fraction = max(0, min(1,
158 event.x / float(total)))
159 elif self.orientation == gtk.PROGRESS_BOTTOM_TO_TOP:
160 total = self._progress.allocation.height
161 self.fraction = 1 - max(0, min(1,
162 event.y / float(total)))
164 def on_key_press_event(self, widget, event):
165 #Return or Space is pressed -> Accept current value
166 if event.keyval in [gtk.keysyms.Return, gtk.keysyms.space]:
167 self.exit(0)
168 #Escape is pressed -> Cancel
169 elif event.keyval == gtk.keysyms.Escape:
170 self.exit(1)
171 #Number key is pressed -> set value of progressbar
172 elif event.keyval >= 48 and event.keyval <= 57:
173 self.fraction = float(event.keyval - 48)/10.0
174 # }}}
176 def main(args):
177 parser = OptionParser()
178 parser.add_option('-i', '--stdin', dest='stdin',
179 action="store_true", help='Use first percentage found on stdin')
180 parser.add_option('-p', '--percent', dest='percent',
181 action="store_true", help='Output XX% instead of 0.XX')
182 parser.add_option('-w', '--window', dest='window', metavar="DIMENSIONS",
183 help='Display window with given dimensions. '
184 +'DIMENSIONS has the format 150x250 (WIDTHxHEIGHT)')
185 parser.add_option('-t', '--title', dest='title', metavar="TITLE",
186 help='Display a short help at the top of the window.'
187 +'You can use Pango-Markup.')
188 parser.add_option('-f', '--factor', dest='factor', metavar="FACTOR",
189 help='Sets the accuracy of the progressbar to steps of 100/FACTOR percent.')
190 parser.add_option('-l', '--left-to-right', dest='ltr',
191 action="store_true", help='Display progressbar from left to right')
192 parser.usage = "%prog [options] [-i|FRACTION]"
194 parser.set_defaults(title=None, ltr=False, factor=20)
196 (options, args) = parser.parse_args(args)
198 # Find first "xx%"
199 re_percent = re.compile("(?:\D|^)(0|100|[1-9][0-9]?)\%(?:\D|$)").search
200 #re_fractal = re.compile("\D0.[0-9]+\D").search
202 # determine fraction to start with
203 frac = 0.5
204 if options.stdin:
205 # Look for percentage on stdin.
206 text = sys.stdin.read()
207 match = re_percent(text)
208 if match:
209 frac = max(0, min(1, float(match.group(1))/100.0))
210 else:
211 # Was a fraction passed as first argument? (0,xx or 0.xx)
212 try:
213 frac = max(0, min(1, float(args[1].replace(',', '.'))))
214 except (IndexError, ValueError):
215 pass
217 scr = Scroller(fraction=frac, title=options.title, factor=options.factor)
218 if options.ltr:
219 scr.orientation = gtk.PROGRESS_LEFT_TO_RIGHT
220 if options.window:
221 try:
222 # read dimensions (WIDTHxHEIGHT)
223 dimensions = [int(x) for x in options.window.lower().split("x")]
224 scr.window(*dimensions)
225 except (IndexError, ValueError):
226 # Use standard values
227 scr.window()
228 else:
229 scr.fullscreen()
231 scr.call()
233 # only print value on success/accept
234 if scr.exitstatus == 0:
235 if options.percent:
236 print float_to_percent(scr.fraction)
237 else:
238 print "%.2f" % scr.fraction
239 return scr.exitstatus
241 if __name__ == '__main__':
242 sys.exit(main(sys.argv))