public
Last active

A custom GTK widget; horizontal slider with stars for rating an item

  • Download Gist
starhscale.py
Python
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
#!/usr/bin/env python
 
"""
 
StarHScale a Horizontal slider that uses stars
Copyright (C) 2006 Mark Mruss <selsine@gmail.com>
 
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
 
This library 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
Lesser General Public License for more details.
 
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
If you find any bugs or have any suggestions email: selsine@gmail.coim
 
Modified from
http://www.pygtk.org/articles/writing-a-custom-widget-using-pygtk/writing-a-custom-widget-using-pygtk.htm
"""
 
try:
import gtk
from gtk import gdk
except:
raise SystemExit
 
if gtk.pygtk_version < (2, 0):
print "PyGtk 2.0 or later required for this widget"
raise SystemExit
 
allocHeight = 1
allocWidth = 1
 
#BORDER_WIDTH = 5
#PIXMAP_SIZE = 22
#STAR_PIXMAP = ["22 22 77 1",
 
BORDER_WIDTH = 0
PIXMAP_SIZE = 22
STAR_PIXMAP = ["22 22 77 1",
" c None",
". c #626260",
"+ c #5E5F5C",
"@ c #636461",
"# c #949492",
"$ c #62625F",
"% c #6E6E6B",
"& c #AEAEAC",
"* c #757673",
"= c #61625F",
"- c #9C9C9B",
"; c #ACACAB",
"> c #9F9F9E",
", c #61635F",
"' c #656663",
") c #A5A5A4",
"! c #ADADAB",
"~ c #646562",
"{ c #61615F",
"] c #6C6D6A",
"^ c #797977",
"/ c #868684",
"( c #A0A19E",
"_ c #AAAAA8",
": c #A3A3A2",
"< c #AAAAA7",
"[ c #9F9F9F",
"} c #888887",
"| c #7E7E7C",
"1 c #6C6C69",
"2 c #626360",
"3 c #A5A5A3",
"4 c #ABABAA",
"5 c #A9A9A7",
"6 c #A2A2A1",
"7 c #A3A3A1",
"8 c #A7A7A6",
"9 c #A8A8A6",
"0 c #686866",
"a c #A4A4A2",
"b c #A4A4A3",
"c c #A1A19F",
"d c #9D9D9C",
"e c #9D9D9B",
"f c #A7A7A5",
"g c #666664",
"h c #A1A1A0",
"i c #9E9E9D",
"j c #646461",
"k c #A6A6A4",
"l c #A0A09F",
"m c #9F9F9D",
"n c #A9A9A8",
"o c #A0A09E",
"p c #9B9B9A",
"q c #ACACAA",
"r c #60615E",
"s c #ADADAC",
"t c #A2A2A0",
"u c #A8A8A7",
"v c #6E6F6C",
"w c #787976",
"x c #969695",
"y c #8B8B8A",
"z c #91918F",
"A c #71716E",
"B c #636360",
"C c #686966",
"D c #999997",
"E c #71716F",
"F c #61615E",
"G c #6C6C6A",
"H c #616260",
"I c #5F605E",
"J c #5D5E5B",
"K c #565654",
"L c #5F5F5D",
" ",
" ",
" . ",
" + ",
" @#$ ",
" %&* ",
" =-;>, ",
" ';)!' ",
" ~{{]^/(_:<[}|*1@, ",
" 23&4_5367895&80 ",
" 2a4b:7c>def)g ",
" 2c4:h>id56j ",
" {k8lmeln2 ",
" j8bmoppqr ",
" {stusnd4v ",
" ws;x@yq;/ ",
" zfAB {CmD{ ",
" rE{ FGH ",
" IJ KL ",
" ",
" ",
" "]
 
class StarHScale(gtk.Widget):
"""A horizontal Scale Widget that attempts to mimic the star
rating scheme used in iTunes"""
 
 
__gtype_name__ = 'StarHScale'
 
def __init__(self, max_stars=5, stars=0):
"""Initialization, numstars is the total number
of stars that may be visible, and stars is the current
number of stars to draw"""
 
#Initialize the Widget
gtk.Widget.__init__(self)
 
self.max_stars = max_stars
self.stars = stars
 
# Init the list to blank
self.sizes = []
for count in range(0,self.max_stars):
self.sizes.append((count * PIXMAP_SIZE) + BORDER_WIDTH)
 
def do_realize(self):
"""Called when the widget should create all of its
windowing resources. We will create our gtk.gdk.Window
and load our star pixmap."""
 
# First set an internal flag showing that we're realized
self.set_flags(self.flags() | gtk.REALIZED)
 
# Create a new gdk.Window which we can draw on.
# Also say that we want to receive exposure events
# and button click and button press events
 
self.window = gtk.gdk.Window(
self.get_parent_window(),
width=self.allocation.width,
height=self.allocation.height,
window_type=gdk.WINDOW_CHILD,
wclass=gdk.INPUT_OUTPUT,
event_mask=self.get_events() | gtk.gdk.EXPOSURE_MASK
| gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.POINTER_MOTION_MASK
| gtk.gdk.POINTER_MOTION_HINT_MASK)
 
# Associate the gdk.Window with ourselves, Gtk+ needs a reference
# between the widget and the gdk window
self.window.set_user_data(self)
 
# Attach the style to the gdk.Window, a style contains colors and
# GC contextes used for drawing
self.style.attach(self.window)
 
# The default color of the background should be what
# the style (theme engine) tells us.
self.style.set_background(self.window, gtk.STATE_NORMAL)
self.window.move_resize(*self.allocation)
 
# load the star xpm
self.pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(
self.window
, self.style.bg[gtk.STATE_NORMAL]
, STAR_PIXMAP)
 
# self.style is a gtk.Style object, self.style.fg_gc is
# an array or graphic contexts used for drawing the forground
# colours
self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
 
self.connect("motion_notify_event", self.motion_notify_event)
 
def do_unrealize(self):
# The do_unrealized method is responsible for freeing the GDK resources
# De-associate the window we created in do_realize with ourselves
self.window.destroy()
 
def do_size_request(self, requisition):
"""From Widget.py: The do_size_request method Gtk+ is calling
on a widget to ask it the widget how large it wishes to be.
It's not guaranteed that gtk+ will actually give this size
to the widget. So we will send gtk+ the size needed for
the maximum amount of stars"""
 
requisition.height = PIXMAP_SIZE
requisition.width = (PIXMAP_SIZE * self.max_stars) + (BORDER_WIDTH * 2)
self.allocHeight = requisition.height
self.allocWidth = requisition.width
 
 
def do_size_allocate(self, allocation):
"""The do_size_allocate is called by when the actual
size is known and the widget is told how much space
could actually be allocated Save the allocated space
self.allocation = allocation. The following code is
identical to the widget.py example"""
 
self.allocation = allocation
 
if self.flags() & gtk.REALIZED:
self.window.move_resize(*allocation)
 
def do_expose_event(self, event):
"""This is where the widget must draw itself."""
 
#Draw the correct number of stars. Each time you draw another star
#move over by 22 pixels. which is the size of the star.
for count in range(0,self.stars):
self.window.draw_drawable(self.gc, self.pixmap, 0, 0
, self.sizes[count]
, 0,-1, -1)
 
def motion_notify_event(self, widget, event):
# if this is a hint, then let's get all the necessary
# information, if not it's all we need.
if event.is_hint:
x, y, state = event.window.get_pointer()
else:
x = event.x
y = event.y
state = event.state
 
new_stars = 0
if (x >= 0 and x <= self.allocWidth ) and (y >= 0 and y <= self.allocHeight):
self.check_for_new_stars(event.x)
#if (state & gtk.gdk.POINTER_MOTION_HINT_MASK): #& gtk.gdk.BUTTON1_MASK &
# loop through the sizes and see if the
# number of stars should change
# self.check_for_new_stars(event.x)
 
def do_button_press_event(self, event):
"""The button press event virtual method"""
 
# make sure it was the first button
if event.button == 1:
#check for new stars
self.check_for_new_stars(event.x)
return True
 
def check_for_new_stars(self, xPos):
"""This function will determin how many stars
will be show based on an x coordinate. If the
number of stars changes the widget will be invalidated
and the new number drawn"""
 
# loop through the sizes and see if the
# number of stars should change
new_stars = 0
for size in self.sizes:
if (xPos < size):
# we've reached the star number
break
new_stars = new_stars + 1
 
#set the new value
self.set_value(new_stars)
 
def set_value(self, value):
"""Sets the current number of stars that will be
drawn. If the number is different then the current
number the widget will be redrawn"""
 
if (value >= 0):
if (self.stars != value):
self.stars = value
#check for the maximum
if (self.stars > self.max_stars):
self.stars = self.max_stars
# redraw the widget
#self.window.invalidate_rect(self.allocation,True)
self.redraw_canvas()
 
 
def redraw_canvas(self):
if self.window:
# alloc = self.get_allocation()
rect = gdk.Rectangle(0, 0, self.allocWidth, self.allocHeight)
self.window.invalidate_rect(rect, True)
self.window.process_updates(True)
 
def get_value(self):
"""Get the current number of stars displayed"""
 
return self.stars
 
def set_max_value(self, max_value):
"""set the maximum number of stars"""
 
if (self.max_stars != max_value):
#Save the old max incase it is less then the
#current number of stars, in which case we will
#have to redraw
 
if (max_value > 0):
self.max_stars = max_value
#reinit the sizes list (should really be a sperate function
self.sizes = []
for count in range(0,self.max_stars):
self.sizes.append((count * PIXMAP_SIZE) + BORDER_WIDTH)
#do we have to change the current number of stars?
if (self.stars > self.max_stars):
self.set_value(self.max_stars)
 
def get_max_value(self):
"""Get the maximum number of stars that can be shown"""
 
return self.max_stars
 
if __name__ == "__main__":
# register the class as a Gtk widget
#gobject.type_register(StarHScale)
 
win = gtk.Window()
win.resize(400,60)
win.connect('delete-event', gtk.main_quit)
 
starScale = StarHScale(5,0)
win.add(starScale)
 
win.show_all()
win.resize(400,60)
gtk.main()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.