Created
April 6, 2013 19:26
-
-
Save sahib/5327302 to your computer and use it in GitHub Desktop.
Two Cairo Widgets usable for Gtk. Ported from FreyaMPD.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cairo | |
from math import pi | |
from gi.repository import Gtk, Gdk | |
class CairoGtkWidget(Gtk.DrawingArea): | |
def __init__(self): | |
Gtk.DrawingArea.__init__(self) | |
# Theming Information (so cairo widgets look natural) | |
self._style_context = self.get_style_context() | |
# Progress from 0 - allocation.width | |
self._position = 0 | |
# True when user drags on the widget with the mouse | |
self._drag_mode = False | |
# Enable the receival of the appropiate signals: | |
self.add_events(self.get_events() | | |
Gdk.EventMask.BUTTON_PRESS_MASK | | |
Gdk.EventMask.BUTTON_RELEASE_MASK | | |
Gdk.EventMask.POINTER_MOTION_MASK | | |
Gdk.EventMask.SCROLL_MASK | |
) | |
# Signals used to know when redrawing is desired | |
self.connect('scroll-event', self.on_scroll_event) | |
self.connect('button-press-event', self.on_button_press_event) | |
self.connect('button-release-event', self.on_button_release_event) | |
self.connect('motion-notify-event', self.on_motion_notify) | |
self.connect('draw', self.on_draw) | |
@property | |
def theme_active_color(self): | |
return self._style_context.get_background_color(Gtk.StateFlags.SELECTED) | |
@property | |
def theme_inactive_color(self): | |
return self._style_context.get_color(Gtk.StateFlags.NORMAL) | |
@property | |
def percent(self): | |
return min(max(float(self._position) / self.get_allocation().width, 0.0), 1.0) | |
@percent.setter | |
def percent(self, value): | |
clamped = min(max(value, 0.0), 1.0) | |
self._position = clamped * self.get_allocation().width | |
#################### | |
# Widget Signals # | |
#################### | |
def on_button_press_event(self, widget, event): | |
self._position = event.x | |
self.queue_draw() | |
self._drag_mode = True | |
return True | |
def on_button_release_event(self, widget, event): | |
self._drag_mode = False | |
return True | |
def on_scroll_event(self, widget, event): | |
if event.direction == Gdk.ScrollDirection.UP: | |
offset = +10 | |
else: | |
offset = -10 | |
self._position = self._position + offset | |
self.queue_draw() | |
return True | |
def on_motion_notify(self, widget, event): | |
if self._drag_mode: | |
self._position = event.x | |
self.queue_draw() | |
def on_draw(self, widget, ctx): | |
# To filled by subclass | |
pass | |
class CairoProgressSlider(CairoGtkWidget): | |
def __init__(self, line_width=2): | |
# Needs to be first. | |
CairoGtkWidget.__init__(self) | |
# If False the colored part is not drawn | |
self._draw_full_line = True | |
# Some const attributes | |
self._line_width = line_width | |
self._thickness = (self._line_width + 2) / 2 | |
@property | |
def draw_full_line(self, value): | |
'If True, color is drawn, if False only the bar is drawn' | |
self._draw_full_line = value | |
@property | |
def draw_full_line(self): | |
'Check if we shall draw a full line' | |
return self._draw_full_line | |
def on_draw(self, area, ctx): | |
'Draw a nice rounded bar with cairo, colored to show the progress' | |
# Get the space the widget allocates | |
alloc = self.get_allocation() | |
width, height = alloc.width, alloc.height | |
# inactive and active theming colors | |
i_color = self.theme_inactive_color | |
a_color = self.theme_active_color | |
# Precalculate often used values | |
radius = height / 2 - self._thickness | |
mid = self._thickness + radius | |
midx2 = mid * 2 | |
length = width - midx2 | |
pid2 = pi / 2 | |
# Configure the context | |
ctx.set_line_cap(cairo.LINE_CAP_SQUARE) | |
ctx.set_source_rgb(a_color.red, a_color.green, a_color.blue) | |
ctx.set_line_width(self._line_width) | |
# Left Rounded Corner | |
ctx.arc_negative(mid, mid, radius, -pid2, pid2) | |
# Upper line | |
ctx.move_to(mid, self._thickness) | |
ctx.line_to(mid + length, self._thickness) | |
# Right Rounded Corner | |
ctx.arc(mid + length, mid, radius, -pid2, pid2) | |
# Down line | |
ctx.move_to(mid, midx2 - self._thickness) | |
ctx.line_to(mid + length, midx2 - self._thickness) | |
# If we shall draw the full thing: paint a colored rectangle and clip | |
# it in. Otherwise, we just use the clip mask and draw it. | |
if self._draw_full_line is True: | |
ctx.rectangle(mid, self._thickness, length, midx2 - 2 * self._thickness) | |
ctx.clip() | |
ctx.paint() | |
else: | |
ctx.stroke() | |
if self._draw_full_line is True: | |
for col, x, y, w, h in [(a_color, 0, 0, self._position, height), | |
(i_color, self._position, 0, width - self._position, height)]: | |
ctx.set_source_rgb(col.red, col.green, col.blue) | |
ctx.rectangle(x, y, w, h) | |
ctx.fill() | |
ctx.stroke() | |
# Call no other draw functions | |
return True | |
class CairoBarSlider(CairoGtkWidget): | |
def __init__(self, line_width=2, line_gap=1): | |
CairoGtkWidget.__init__(self) | |
self._line_width = line_width | |
self._line_gap = line_gap | |
def on_draw(self, area, ctx): | |
alloc = self.get_allocation() | |
width, height = alloc.width, alloc.height | |
# Theming support | |
a_color = self.theme_active_color | |
i_color = self.theme_inactive_color | |
# True when bars after self._position are drawn | |
color_turned = False | |
ratio = height / float(width) | |
extra_gap = self._line_width + self._line_gap | |
# Configure the context to use filling lines and a nice color | |
ctx.set_line_width(self._line_width) | |
ctx.set_source_rgb(a_color.red, a_color.green, a_color.blue) | |
for i in range(0, width, extra_gap): | |
# Height of this line | |
lim = height - (i * ratio) - 2 | |
# Draw a straight line up | |
ctx.move_to(i, height) | |
ctx.line_to(i, lim) | |
# No sense in drawing any further | |
if lim < 1: | |
break | |
else: | |
if color_turned is False and i >= self._position: | |
# Draw everything till now | |
ctx.stroke() | |
# Change the color for the next bars | |
ctx.set_source_rgb(i_color.red, i_color.green, i_color.blue) | |
color_turned = True | |
# Draw the rest | |
ctx.stroke() | |
return True | |
if __name__ == '__main__': | |
box = Gtk.Box() | |
box.pack_start(CairoBarSlider(), True, True, 2) | |
box.pack_start(CairoProgressSlider(), True, True, 2) | |
win = Gtk.Window() | |
win.connect("delete-event", Gtk.main_quit) | |
win.add(box) | |
win.show_all() | |
Gtk.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment