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 if new else None 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)