Rodrigo Bistolfi avatar Rodrigo Bistolfi committed 399a72a

PyGtk disk widget

Comments (0)

Files changed (1)

vinstall/extensions/disk_widget.py

+# coding: utf8
+
+
+import gtk
+from gtk import gdk
+import gobject
+
+
+class BlocksWidget(gtk.DrawingArea):
+    """A widget that behaves much like the gparted representation of a disk
+
+    """
+    __gsignals__ = {
+            "active_block_changed": (gobject.SIGNAL_RUN_FIRST,
+                gobject.TYPE_NONE,
+                (object,)),
+            "block_added": (gobject.SIGNAL_RUN_FIRST,
+                gobject.TYPE_NONE,
+                (object,)),
+            "block_removed": (gobject.SIGNAL_RUN_FIRST,
+                gobject.TYPE_NONE,
+                (object,)),
+            "block_resized": (gobject.SIGNAL_RUN_FIRST,
+                gobject.TYPE_NONE,
+                (object,)),
+            }
+
+    neutral_color_fill = (0.8, 0.8, 0.8)
+    neutral_color_border = (0.6, 0.6, 0.6)
+    even_color_fill = (0.5, 0.9, 0.55)
+    even_color_border = (0.7, 1, 0.75)
+    odd_color_fill = (0.9, 0.5, 0.55)
+    odd_color_border = (1, 0.7, 0.75)
+    stroke_width = 4.0
+
+    def __init__(self):
+        super(BlocksWidget, self).__init__()
+
+        self.connect("expose_event", self.expose)
+        self.connect("button_press_event", self.button_press)
+        self.connect("button_release_event", self.button_release)
+        self.connect("key_release_event", self.key_release)
+
+        self.add_events(gdk.BUTTON_PRESS_MASK |
+                        gdk.BUTTON_RELEASE_MASK |
+                        gdk.POINTER_MOTION_MASK)
+
+        self.set_flags(self.flags() | gtk.CAN_FOCUS)
+        self.set_size_request(600, 50)
+
+        self._dragging = False
+        self.blocks = []
+
+    def add_block(self, size=None, index=None, label=None,
+            neutral_color=False, user_data=None):
+        "Add block"
+        block = Block()
+        if size is None:
+            size = 1 - sum(i.size for i in self.blocks)
+        block.size = size
+        block.user_data = user_data
+        block.label = label
+        if neutral_color:
+            color = self.neutral_color_fill
+            border_color = self.neutral_color_border
+            block.neutral = True
+        else:
+            if len(self.blocks) % 2:
+                color = self.odd_color_fill
+                border_color = self.odd_color_border
+            else:
+                color = self.even_color_fill
+                border_color = self.even_color_border
+        block.color = color
+        block.border_color = border_color
+        if index is not None:
+            self.blocks.insert(index, block)
+        else:
+            self.blocks.append(block)
+        self.emit_block_added(block)
+
+    def insert_block(self, index, size=None, label=None, neutral_color=False,
+            user_data=None):
+        "Insert block at index"
+        self.add_block(index=index, size=size, label=label,
+                neutral_color=neutral_color, user_data=user_data)
+
+    def remove_block(self, block):
+        "Remove block"
+        self.blocks.remove(block)
+        self.emit_block_removed(block)
+
+    def get_focus_block(self):
+        "Return the active block"
+        for i, b in enumerate(self.blocks):
+            if b.is_active:
+                return i
+
+    def set_focus_block(self, index):
+        "Set the active block"
+        for i, block in enumerate(self.blocks):
+            if index == i:
+                block.is_active = True
+                theblock = block
+            else:
+                block.is_active = False
+        self.emit_active_block_changed(theblock)
+
+    def resize_block(self, index, newsize):
+        """Resize block. If the block is not the last one, the next block is
+        also resized. When the active block grows, the next block shrinks and
+        viceversa.
+
+        """
+        if index is None:
+            return
+        block = self.blocks[index]
+        if block is self.blocks[-1]:
+            others = sum(i.size for i in self.blocks if i is not block)
+            if others + newsize > 1:
+                return
+        oldsize = block.size
+        block.size = newsize
+        try:
+            next = self.blocks[index+1]
+        except IndexError:
+            pass
+        else:
+            diff = newsize - oldsize
+            next.size -= diff
+            self.emit_block_resized(next)
+        self.emit_block_resized(block)
+
+    def expose(self, widget, event):
+        "Create a context and draw the widget"
+        context = widget.window.cairo_create()
+        area = self.get_allocation()
+        context.rectangle(area.x, area.y, area.width,
+                          area.height)
+        context.clip()
+        self.draw(context)
+        return False
+
+    def button_press(self, widget, event):
+        "Handle the button press event"
+        self._dragging = True
+        return False
+
+    def button_release(self, widget, event):
+        "Change active block when clicked"
+        if not self._dragging:
+            return False
+        for index, block in enumerate(self.blocks):
+            if event in block:
+                self.set_focus_block(index)
+        return False
+
+    def key_release(self, widget, event):
+        """Key has been released. Change focus to next or previous block
+        If there is no active block yet (the widget never got focus)
+        set focus to the first block.
+
+        """
+        LEFT = "Left"
+        RIGHT = "Right"
+        left_bound = 0
+        right_bound = len(self.blocks) - 1
+        keyval = gdk.keyval_name(event.keyval)
+        index = self.get_focus_block()
+        if index is None:
+            self.set_focus_block(0)
+        elif keyval == LEFT and index != left_bound:
+            self.set_focus_block(index - 1)
+        elif keyval == RIGHT and index != right_bound:
+            self.set_focus_block(index + 1)
+        else:
+            pass
+        return False
+
+    def emit_active_block_changed(self, block):
+        "Emit signal"
+        self.redraw_canvas()
+        self.emit("active_block_changed", block)
+
+    def emit_block_added(self, block):
+        "Emit signal"
+        self.redraw_canvas()
+        self.emit("block_added", block)
+
+    def emit_block_removed(self, block):
+        "Emit signal"
+        self.redraw_canvas()
+        self.emit("block_removed", block)
+
+    def emit_block_resized(self, block):
+        "Emit signal"
+        self.redraw_canvas()
+        self.emit("block_resized", block)
+
+    def draw(self, context):
+        "Draw widget"
+        rect = self.get_allocation()
+        context.set_line_width(self.stroke_width)
+        x = rect.x
+        y = rect.y
+        for index, block in enumerate(self.blocks):
+            width = rect.width * block.size
+            block.width = width
+            block.height = rect.height
+            block.context = context
+            block.x = x
+            block.y = y
+            block.draw(context)
+            x += width
+
+    def redraw_canvas(self):
+        "Update the view"
+        if self.window:
+            alloc = self.get_allocation()
+            self.queue_draw_area(alloc.x, alloc.y, alloc.width, alloc.height)
+            self.window.process_updates(True)
+
+    def get_allocation(self):
+        alloc = super(BlocksWidget, self).get_allocation()
+        alloc.x = self.stroke_width / 2
+        alloc.y = self.stroke_width / 2
+        alloc.width = alloc.width - (self.stroke_width * 2)
+        return alloc
+
+
+class Block(object):
+    "A Block represents an item in the BlocksWidget"
+    border_color = (0.3, 0.3, 0.3)
+    active_border_color = (1, 1, 0)
+    color = (0.8, 0.8, 0.8)
+    stroke_width = 10.0
+
+    def __init__(self, label=None, user_data=None):
+        self.label = label
+        self.x = 0
+        self.y = 0
+        self.size = 0.0
+        self.width = 0
+        self.height = 0
+        self.is_active = False
+        self.user_data = user_data
+        self.neutral = False
+
+    def draw(self, context):
+        "Draw self as a rectangle"
+        x = self.x + (self.stroke_width / 2.0)
+        y = self.y + (self.stroke_width / 2.0)
+        width = self.width - (self.stroke_width / 2)
+        context.rectangle(x, y, width, self.height - 10)
+        context.set_source_rgb(*self.color)
+        context.fill_preserve()
+        if self.is_active:
+            context.set_source_rgb(*self.active_border_color)
+        else:
+            context.set_source_rgb(*self.border_color)
+        context.stroke()
+        if self.label:
+            self.draw_label(context)
+
+    def draw_label(self, context):
+        "Draw the text label"
+        font_size = min(self.height / 5, 12)
+        x = self.x + self.stroke_width * 2
+        y = self.y + ((self.height-10) / 2.0) + (self.stroke_width / 2.0) + \
+                (font_size / 4.0)
+        context.select_font_face('Sans')
+        context.set_font_size(font_size)
+        context.move_to(x, y)
+        context.set_source_rgb(0, 0, 0)
+        context.show_text(self.label)
+
+    def __contains__(self, point):
+        "A point is contained by self when it belongs to the rectangle area"
+        if point.x > self.x and  point.x < self.x + self.width:
+            return True
+        return False
+
+
+if __name__ == "__main__":
+
+    def value_changed(adj):
+        "Resize active block when adjustment changes"
+        block = disk.get_focus_block()
+        disk.resize_block(block, adj.value/100.0)
+
+    def active_block_changed(widget, block):
+        "when a block is focused, set the adjustment value to its size"
+        value = block.size * 100
+        if block is widget.blocks[-1]:
+            upper = 1 - sum(i.size for i in widget.blocks if i is not block)
+        else:
+            index = widget.blocks.index(block)
+            upper = block.size + widget.blocks[index+1].size
+        adjustment.upper = (upper * 100)
+        adjustment.set_value(value)
+
+    window = gtk.Window()
+    window.connect("destroy", gtk.main_quit)
+    #window.set_border_width(10)
+    vbox = gtk.VBox()
+    window.add(vbox)
+
+    disk = BlocksWidget()
+    disk.add_block(0.2, label="/dev/sda1 - 10GB")
+    disk.add_block(0.6, label="Microsoft Windows - 40GB")
+    disk.add_block(neutral_color=True, label="Free space - 10GB")
+
+    adjustment = gtk.Adjustment(value=0, lower=0, upper=100, step_incr=1,
+            page_incr=10)
+    adjustment.connect("value_changed", value_changed)
+
+    slider = gtk.HScale(adjustment=adjustment)
+    slider.set_sensitive(False)
+
+    disk.connect("active_block_changed", lambda *_:
+            slider.set_sensitive(True))
+    disk.connect("active_block_changed", active_block_changed)
+
+    vbox.pack_start(disk, True, True, 0)
+    vbox.pack_start(slider, True, True, 0)
+
+    window.show_all()
+
+    gtk.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.