summaryrefslogtreecommitdiff
path: root/plugins/python/cglimpse/panel.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/python/cglimpse/panel.py')
-rw-r--r--plugins/python/cglimpse/panel.py323
1 files changed, 323 insertions, 0 deletions
diff --git a/plugins/python/cglimpse/panel.py b/plugins/python/cglimpse/panel.py
new file mode 100644
index 0000000..2fd3c7f
--- /dev/null
+++ b/plugins/python/cglimpse/panel.py
@@ -0,0 +1,323 @@
+
+import os
+from gi.repository import Gtk
+from pychrysalide import core
+from pychrysalide.gtkext import BuiltNamedWidget
+from pychrysalide.gtkext import EasyGtk
+from pychrysalide.gui import PanelItem
+from pychrysalide.gui.panels import UpdatablePanel
+
+
+from .distro import ByteDistribution
+from .entropy import ShannonEntropy
+
+
+class CGlimpsePanel(PanelItem, UpdatablePanel):
+
+ _key = 'cglimpse'
+
+ _path = 'Ms'
+
+ _working_group_id = core.setup_tiny_global_work_group()
+
+
+ def __init__(self):
+ """Initialize the GUI panel."""
+
+ directory = os.path.dirname(os.path.realpath(__file__))
+ filename = os.path.join(directory, 'panel.ui')
+
+ widget = BuiltNamedWidget('Content glimpse', 'Binary content glimpse', filename)
+
+ super(CGlimpsePanel, self).__init__(widget)
+
+ self._builder = self.named_widget.builder
+
+ self._builder.connect_signals(self)
+
+ self._content = None
+
+ combo = self._builder.get_object('method_sel')
+ self._on_method_changed(combo)
+
+
+ def _change_content(self, old, new):
+ """Get notified about a LoadedContent change."""
+
+ self._content = new.content
+
+ combo = self._builder.get_object('method_sel')
+ self._on_method_changed(combo)
+
+
+ def _setup(self, uid):
+ """Prepare an update process for a panel."""
+
+ assert(uid == 0)
+
+ return ( 1, {}, 'Computing data for content glimpse' )
+
+
+ def _introduce(self, uid, data):
+ """Introduce the update process and switch display."""
+
+ assert(uid == 0)
+
+ self.switch_to_updating_mask()
+
+
+ def _process(self, uid, status, id, data):
+ """Perform the computing of data to render."""
+
+ assert(uid == 0)
+
+ self._current.update(self._content.data)
+
+ area = self._builder.get_object('content')
+ area.queue_draw()
+
+
+ def _conclude(self, uid, data):
+ """Conclude the update process and display the computed data."""
+
+ assert(uid == 0)
+
+ self.switch_to_updated_content()
+
+
+ def _clean_data(self, uid, data):
+ """Delete dynamically generated objects for the panel update."""
+
+ # Not really useful here...
+
+ assert(uid == 0)
+
+
+ def _on_method_changed(self, combo):
+ """React on method selection change."""
+
+ tree_iter = combo.get_active_iter()
+
+ if tree_iter:
+
+ model = combo.get_model()
+ key = model[tree_iter][1]
+
+ if key == 'shanon':
+ self._current = ShannonEntropy(self._builder)
+
+ else:
+ assert(key == 'distrib')
+ self._current = ByteDistribution(self._builder)
+
+ self._current.switch()
+
+ if self._content:
+
+ self.run_update(0)
+
+
+ def _on_options_toggled(self, button):
+ """React on options display/hide order."""
+
+ lbl = self._builder.get_object('options_label')
+
+ common = self._builder.get_object('common_options')
+
+ if button.get_active():
+
+ button.get_parent().child_set_property(button, 'expand', False)
+ lbl.set_angle(0)
+
+ common.show()
+
+ else:
+
+ button.get_parent().child_set_property(button, 'expand', True)
+ lbl.set_angle(90)
+
+ common.hide()
+
+
+ def _render_grid(self, widget, cr):
+ """Draw a basic empty grid."""
+
+ # Colors
+
+ color = EasyGtk.get_color_from_style('view', True)
+ bg_color = [ color.red, color.green, color.blue, color.alpha ]
+
+ color = EasyGtk.get_color_from_style('background', False)
+ line_color = [ color.red, color.green, color.blue, color.alpha ]
+
+ # Background
+
+ w = widget.get_allocation().width
+ h = widget.get_allocation().height
+
+ cr.set_source_rgba(*bg_color)
+
+ cr.rectangle(0, 0, w, h)
+ cr.fill()
+
+ # Area definitions
+
+ x_range, y_range = self._current.setup_rendering()
+
+ margin_left = 0
+ margin_bottom = 0
+
+ y_count = int((y_range[2] - y_range[0]) / y_range[1])
+
+ for i in range(y_count + 1):
+
+ text = self._current.format_legend(y_range[0] + i * y_range[1], True)
+
+ (_, _, width, height, _, _) = cr.text_extents(text)
+
+ if width > margin_left:
+
+ margin_left = width
+ margin_bottom = height
+
+ bar_padding = 5
+ bar_tick = 3
+ arrow_size = 4
+
+ graph_left = bar_padding + margin_left + bar_tick * 3
+ graph_right = w - 2 * bar_padding
+
+ graph_bottom = h - bar_padding - margin_bottom - bar_tick * 3
+
+ data_left = graph_left + 2 * bar_padding
+ data_right = graph_right - 2 * bar_padding
+
+ data_top = 5 * bar_padding
+ data_bottom = graph_bottom - 2 * bar_padding
+
+ data_height = data_bottom - data_top
+ data_width = data_right - data_left
+
+ data_area = [ data_left, data_top, data_width, data_height ]
+
+ # Grid content #1
+
+ cr.set_source_rgba(*line_color)
+
+ cr.set_line_width(1)
+
+ cr.move_to(graph_left, 2 * bar_padding)
+ cr.line_to(graph_left, graph_bottom)
+ cr.line_to(graph_right, graph_bottom)
+
+ cr.stroke()
+
+ cr.move_to(graph_right, graph_bottom)
+ cr.line_to(graph_right - arrow_size, graph_bottom - arrow_size)
+ cr.line_to(graph_right - arrow_size, graph_bottom + arrow_size)
+
+ cr.fill()
+
+ cr.move_to(graph_left, 2 * bar_padding)
+ cr.line_to(graph_left - arrow_size, 2 * bar_padding + arrow_size)
+ cr.line_to(graph_left + arrow_size, 2 * bar_padding + arrow_size)
+
+ cr.fill()
+
+ cr.set_source_rgba(0, 0, 0, 0.2)
+
+ cr.rectangle(*data_area)
+ cr.fill()
+
+ # Grid content #2
+
+ y_count = int((y_range[2] - y_range[0]) / y_range[1])
+
+ for i in range(y_count + 1):
+
+ y = data_bottom - (i * data_height) / y_count
+
+ # Line
+
+ cr.save()
+
+ cr.set_source_rgba(*line_color[:3], line_color[3] * 0.4)
+ cr.set_dash([ 2 * bar_tick, 6 * bar_tick ])
+
+ cr.move_to(graph_left + 6 * bar_tick, y)
+ cr.line_to(data_right, y)
+
+ cr.stroke()
+
+ cr.restore()
+
+ # Tick
+
+ cr.set_source_rgba(*line_color)
+
+ cr.move_to(graph_left - bar_tick, y)
+ cr.line_to(graph_left + bar_tick, y)
+
+ cr.stroke()
+
+ # Text
+
+ text = self._current.format_legend(y_range[0] + i * y_range[1], True)
+
+ _, _, tw, th, _, _ = cr.text_extents(text)
+
+ x = graph_left - 3 * bar_tick - tw
+
+ cr.move_to(x, y + th / 2)
+ cr.show_text(text)
+
+ x_count = int((x_range[2] - x_range[0]) / x_range[1])
+
+ for i in range(x_count + 1):
+
+ x = data_left + (i * data_width) / x_count
+
+ # Line
+
+ cr.save()
+
+ cr.set_source_rgba(*line_color[:3], line_color[3] * 0.4)
+ cr.set_dash([ 2 * bar_tick, 6 * bar_tick ])
+
+ cr.move_to(x, data_top)
+ cr.line_to(x, data_bottom)
+
+ cr.stroke()
+
+ cr.restore()
+
+ # Tick
+
+ cr.set_source_rgba(*line_color)
+
+ cr.move_to(x, graph_bottom - bar_tick)
+ cr.line_to(x, graph_bottom + bar_tick)
+
+ cr.stroke()
+
+ # Text
+
+ text = self._current.format_legend(x_range[0] + i * x_range[1], False)
+
+ _, _, tw, th, _, _ = cr.text_extents(text)
+
+ y = graph_bottom + 3 * bar_tick + th
+
+ cr.move_to(x - tw / 2, y)
+ cr.show_text(text)
+
+ return data_area
+
+
+ def _render_content_glimpse(self, widget, cr):
+ """Draw the selected content view."""
+
+ data_area = self._render_grid(widget, cr)
+
+ if self._content:
+ self._current.render(cr, data_area)