diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2020-12-24 16:34:38 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2020-12-24 16:35:33 (GMT) |
commit | 5bd15ad0d14e925d1f42ff12a1bc254de3fbdda8 (patch) | |
tree | b68ecb23966e977bd99a2866b907c4bb7be71c67 /plugins/python/cglimpse/panel.py | |
parent | f3578d35758ac47991806233ceba8d566160260b (diff) |
Created the basics of a new panel for binary content glimpse.
Diffstat (limited to 'plugins/python/cglimpse/panel.py')
-rw-r--r-- | plugins/python/cglimpse/panel.py | 323 |
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) |