class ResultList(object): """Result list with toolbar""" def __init__(self, win, builder): self.builder = builder self.widget = self.builder.get_object('editor_results_data') self._setup_widget() self.instance = win self.query = None def _setup_widget(self): self.grid = Grid() self.builder.get_object("sw_grid").add(self.grid) def set_query(self, query): self.query = query self.grid.reset() if self.query.description: try: self.grid.set_result(self.query.rows, self.query.description, self.query.coding_hint) except Exception, err: logging.exception('Failed to display query results') dialogs.error(_(u'Failed to display results'), str(err))
class ResultsView(object): def __init__(self, win, builder): self.instance = win self.app = win.app self.widget = builder.get_object('editor_results') self.builder = builder self._setup_widget() self._setup_connections() def _setup_widget(self): self.grid = ResultList(self.instance, self.builder) self.messages = self.builder.get_object("editor_results_messages") model = gtk.ListStore(str, # 0 stock id str, # 1 message str, # 2 foreground color int, # 3 font weight bool, # 4 is separator row str, # 5 font description ) model.connect("row-inserted", self._msg_model_changed) model.connect("row-deleted", self._msg_model_changed) self.messages.set_model(model) col = gtk.TreeViewColumn() renderer = gtk.CellRendererPixbuf() col.pack_start(renderer, expand=False) col.add_attribute(renderer, 'stock-id', 0) renderer = gtk.CellRendererText() col.pack_start(renderer, expand=True) col.add_attribute(renderer, 'text', 1) col.add_attribute(renderer, 'foreground', 2) col.add_attribute(renderer, 'weight', 3) col.add_attribute(renderer, 'font', 5) self.messages.append_column(col) self.messages.set_row_separator_func(self._set_row_separator) self.explain_results = Grid() sw = self.builder.get_object('sw_explain_results') sw.add(self.explain_results) self.explain_results.show_all() self._update_btn_export_state() def _update_btn_export_state(self): """Update state of export button.""" btn_export = self.builder.get_object('editor_export_data') rows = None if self.grid.query: rows = self.grid.query.rows sensitive = False if rows: sensitive = True exp_plugins = self.app.plugins.get_plugins(PLUGIN_TYPE_EXPORT, True) if not exp_plugins: sensitive = False btn_export.set_sensitive(sensitive) def _msg_model_changed(self, model, *args): tb_clear = self.builder.get_object("tb_messages_clear") tb_copy = self.builder.get_object("tb_messages_copy") sensitive = model.get_iter_first() is not None tb_clear.set_sensitive(sensitive) tb_copy.set_sensitive(sensitive) def _setup_connections(self): self.grid.grid.connect("selection-changed", self.on_grid_selection_changed) self.app.plugins.connect('plugin-added', lambda *a: self._update_btn_export_state()) self.app.plugins.connect('plugin-removed', lambda *a: self._update_btn_export_state()) self.app.plugins.connect('plugin-active', lambda *a: self._update_btn_export_state()) def _set_row_separator(self, model, iter): return model.get_value(iter, 4) def on_copy_data(self, *args): gobject.idle_add(self.copy_data) def on_export_data(self, *args): gobject.idle_add(self.export_data) def on_messages_clear(self, *args): self.messages.get_model().clear() def on_messages_copy(self, *args): model = self.messages.get_model() iter_ = model.get_iter_first() plain = [] while iter_ is not None: if model.get_value(iter_, 4): # Is it a separator? plain.append('-'*20) else: value = model.get_value(iter_, 1) if value is not None: plain.append(value) iter_ = model.iter_next(iter_) clipboard = gtk.clipboard_get() clipboard.set_text('\n'.join(plain)) def on_grid_selection_changed(self, grid, selected_cells): self.builder.get_object("editor_copy_data").set_sensitive(bool(selected_cells)) def assure_visible(self): """Make sure that the result pane is visible.""" pane = self.widget.get_parent() if not pane.get_property('visible'): pane.show() def copy_data(self, clipboard=None): """Copy selected values to clipboard. Columns are terminated by \t, rows by \n. If *clipboard* is ``None``, the default clipboard will be used. """ rows = {} for cell in self.grid.grid.get_selected_cells(): value = self.grid.grid.get_cell_data(cell, repr=True) if rows.has_key(cell[0]): rows[cell[0]] += "\t%s" % value else: rows[cell[0]] = "%s" % value rownums = rows.keys() rownums.sort() txt = "\n".join(rows.get(rownum) for rownum in rownums) if clipboard is None: display = gtk.gdk.display_manager_get().get_default_display() clipboard = gtk.Clipboard(display, "CLIPBOARD") clipboard.set_text(txt) self.instance.statusbar.set_message(_(u"Data copied to clipboard")) def export_data(self): data = self.grid.grid.get_grid_data() description = self.grid.grid.description selected = self.grid.grid.get_selected_rows() statement = self.grid.query.statement gtk.gdk.threads_enter() dlg = DataExportDialog(self.instance.app, self.instance, data, selected, statement, description) if dlg.run() == gtk.RESPONSE_OK: dlg.hide() dlg.export_data() dlg.destroy() gtk.gdk.threads_leave() def clipboard_copy(self, clipboard): """Copies selected cells to clipboard.""" # TODO: Cleanup API. There should be one function to do this. self.copy_data(clipboard) def reset(self): # Explain self.explain_results.reset() # Messages # Only gray out messages from previous runs, don't remove them! model = self.messages.get_model() iter_ = model.get_iter_first() while iter_: model.set_value(iter_, 2, '#cccccc') iter_ = model.iter_next(iter_) self.widget.set_current_page(2) def set_explain_results(self, query): self.explain_results.reset() if query.failed: dialogs.error(_(u'Failed'), '\n'.join(query.errors), parent=self.instance) else: self.explain_results.set_result(query.rows, query.description) def set_query(self, query): self.assure_visible() self.grid.set_query(query) model = self.messages.get_model() for err in query.errors: self.add_error(err.strip(), monospaced=True) for msg in query.messages: self.add_output(msg.strip()) if query.errors: curr_page = 2 elif query.description: curr_page = 0 else: curr_page = 2 self._update_btn_export_state() gobject.idle_add(self.widget.set_current_page, curr_page) def add_message(self, msg, type_=None, path=None, monospaced=False): """Add a message. Args: msg: The message to add. type_: Message type ('info', 'output', 'error', 'warning', 'query', None). """ self.assure_visible() assert type_ in (None, 'info', 'output', 'error', 'warning', 'query') stock_id = None foreground = None if monospaced: font = 'Monospace 10' else: font = None weight = pango.WEIGHT_NORMAL if type_ == 'info': stock_id = 'gtk-info' foreground = '#336699' elif type_ == 'error': stock_id = 'gtk-dialog-error' foreground = '#a40000' weight = pango.WEIGHT_BOLD elif type_ == 'warning': stock_id = 'gtk-dialog-warning' foreground = '#00a400' elif type_ == 'query': stock_id ='gtk-execute' weight = pango.WEIGHT_BOLD elif type_ == 'output': stock_id = 'gtk-go-back' font = 'Monospace' model = self.messages.get_model() if model is None: # we are in shutdown phase return msg = msg.strip() if path is None: itr = model.append([stock_id, msg, foreground, weight, False, font]) else: try: iter_ = model.get_iter(path) except ValueError: return model.set_value(iter_, 0, stock_id) model.set_value(iter_, 1, msg) model.set_value(iter_, 2, foreground) model.set_value(iter_, 3, weight) model.set_value(iter_, 5, font) itr = iter_ # Somehow this works smarter than treeview.scroll_to_cell(path). # See: http://www.mail-archive.com/[email protected]/msg17059.html self.messages.scroll_to_cell(str(len(model)-1)) self.widget.set_current_page(2) return model.get_path(itr) def add_error(self, msg, monospaced=False): return self.add_message(msg, 'error', monospaced=monospaced) def add_info(self, msg): return self.add_message(msg, 'info') def add_warning(self, msg): return self.add_message(msg, 'warning') def add_output(self, msg): return self.add_message(msg, 'output') def add_separator(self): model = self.messages.get_model() if not model.get_iter_first(): return model.append([None, None, None, pango.WEIGHT_NORMAL, True, None])
class ResultsView(object): def __init__(self, win, builder): self.instance = win self.app = win.app self.widget = builder.get_object('editor_results') self.builder = builder self._setup_widget() self._setup_connections() def _setup_widget(self): self.grid = ResultList(self.instance, self.builder) self.messages = self.builder.get_object("editor_results_messages") model = gtk.ListStore( str, # 0 stock id str, # 1 message str, # 2 foreground color int, # 3 font weight bool, # 4 is separator row str, # 5 font description ) model.connect("row-inserted", self._msg_model_changed) model.connect("row-deleted", self._msg_model_changed) self.messages.set_model(model) col = gtk.TreeViewColumn() renderer = gtk.CellRendererPixbuf() col.pack_start(renderer, expand=False) col.add_attribute(renderer, 'stock-id', 0) renderer = gtk.CellRendererText() col.pack_start(renderer, expand=True) col.add_attribute(renderer, 'text', 1) col.add_attribute(renderer, 'foreground', 2) col.add_attribute(renderer, 'weight', 3) col.add_attribute(renderer, 'font', 5) self.messages.append_column(col) self.messages.set_row_separator_func(self._set_row_separator) self.explain_results = Grid() sw = self.builder.get_object('sw_explain_results') sw.add(self.explain_results) self.explain_results.show_all() self._update_btn_export_state() def _update_btn_export_state(self): """Update state of export button.""" btn_export = self.builder.get_object('editor_export_data') rows = None if self.grid.query: rows = self.grid.query.rows sensitive = False if rows: sensitive = True exp_plugins = self.app.plugins.get_plugins(PLUGIN_TYPE_EXPORT, True) if not exp_plugins: sensitive = False btn_export.set_sensitive(sensitive) def _msg_model_changed(self, model, *args): tb_clear = self.builder.get_object("tb_messages_clear") tb_copy = self.builder.get_object("tb_messages_copy") sensitive = model.get_iter_first() is not None tb_clear.set_sensitive(sensitive) tb_copy.set_sensitive(sensitive) def _setup_connections(self): self.grid.grid.connect("selection-changed", self.on_grid_selection_changed) self.app.plugins.connect('plugin-added', lambda *a: self._update_btn_export_state()) self.app.plugins.connect('plugin-removed', lambda *a: self._update_btn_export_state()) self.app.plugins.connect('plugin-active', lambda *a: self._update_btn_export_state()) def _set_row_separator(self, model, iter): return model.get_value(iter, 4) def on_copy_data(self, *args): gobject.idle_add(self.copy_data) def on_export_data(self, *args): gobject.idle_add(self.export_data) def on_messages_clear(self, *args): self.messages.get_model().clear() def on_messages_copy(self, *args): model = self.messages.get_model() iter_ = model.get_iter_first() plain = [] while iter_ is not None: if model.get_value(iter_, 4): # Is it a separator? plain.append('-' * 20) else: value = model.get_value(iter_, 1) if value is not None: plain.append(value) iter_ = model.iter_next(iter_) clipboard = gtk.clipboard_get() clipboard.set_text('\n'.join(plain)) def on_grid_selection_changed(self, grid, selected_cells): self.builder.get_object("editor_copy_data").set_sensitive( bool(selected_cells)) def assure_visible(self): """Make sure that the result pane is visible.""" pane = self.widget.get_parent() if not pane.get_property('visible'): pane.show() def copy_data(self, clipboard=None): """Copy selected values to clipboard. Columns are terminated by \t, rows by \n. If *clipboard* is ``None``, the default clipboard will be used. """ rows = {} for cell in self.grid.grid.get_selected_cells(): value = self.grid.grid.get_cell_data(cell, repr=True) if rows.has_key(cell[0]): rows[cell[0]] += "\t%s" % value else: rows[cell[0]] = "%s" % value rownums = rows.keys() rownums.sort() txt = "\n".join(rows.get(rownum) for rownum in rownums) if clipboard is None: display = gtk.gdk.display_manager_get().get_default_display() clipboard = gtk.Clipboard(display, "CLIPBOARD") clipboard.set_text(txt) self.instance.statusbar.set_message(_(u"Data copied to clipboard")) def export_data(self): data = self.grid.grid.get_grid_data() description = self.grid.grid.description selected = self.grid.grid.get_selected_rows() statement = self.grid.query.statement gtk.gdk.threads_enter() dlg = DataExportDialog(self.instance.app, self.instance, data, selected, statement, description) if dlg.run() == gtk.RESPONSE_OK: dlg.hide() dlg.export_data() dlg.destroy() gtk.gdk.threads_leave() def clipboard_copy(self, clipboard): """Copies selected cells to clipboard.""" # TODO: Cleanup API. There should be one function to do this. self.copy_data(clipboard) def reset(self): # Explain self.explain_results.reset() # Messages # Only gray out messages from previous runs, don't remove them! model = self.messages.get_model() iter_ = model.get_iter_first() while iter_: model.set_value(iter_, 2, '#cccccc') iter_ = model.iter_next(iter_) self.widget.set_current_page(2) def set_explain_results(self, query): self.explain_results.reset() if query.failed: dialogs.error(_(u'Failed'), '\n'.join(query.errors), parent=self.instance) else: self.explain_results.set_result(query.rows, query.description) def set_query(self, query): self.assure_visible() self.grid.set_query(query) model = self.messages.get_model() for err in query.errors: self.add_error(err.strip(), monospaced=True) for msg in query.messages: self.add_output(msg.strip()) if query.errors: curr_page = 2 elif query.description: curr_page = 0 else: curr_page = 2 self._update_btn_export_state() gobject.idle_add(self.widget.set_current_page, curr_page) def add_message(self, msg, type_=None, path=None, monospaced=False): """Add a message. Args: msg: The message to add. type_: Message type ('info', 'output', 'error', 'warning', 'query', None). """ self.assure_visible() assert type_ in (None, 'info', 'output', 'error', 'warning', 'query') stock_id = None foreground = None if monospaced: font = 'Monospace 10' else: font = None weight = pango.WEIGHT_NORMAL if type_ == 'info': stock_id = 'gtk-info' foreground = '#336699' elif type_ == 'error': stock_id = 'gtk-dialog-error' foreground = '#a40000' weight = pango.WEIGHT_BOLD elif type_ == 'warning': stock_id = 'gtk-dialog-warning' foreground = '#00a400' elif type_ == 'query': stock_id = 'gtk-execute' weight = pango.WEIGHT_BOLD elif type_ == 'output': stock_id = 'gtk-go-back' font = 'Monospace' model = self.messages.get_model() if model is None: # we are in shutdown phase return msg = msg.strip() if path is None: itr = model.append( [stock_id, msg, foreground, weight, False, font]) else: try: iter_ = model.get_iter(path) except ValueError: return model.set_value(iter_, 0, stock_id) model.set_value(iter_, 1, msg) model.set_value(iter_, 2, foreground) model.set_value(iter_, 3, weight) model.set_value(iter_, 5, font) itr = iter_ # Somehow this works smarter than treeview.scroll_to_cell(path). # See: http://www.mail-archive.com/[email protected]/msg17059.html self.messages.scroll_to_cell(str(len(model) - 1)) self.widget.set_current_page(2) return model.get_path(itr) def add_error(self, msg, monospaced=False): return self.add_message(msg, 'error', monospaced=monospaced) def add_info(self, msg): return self.add_message(msg, 'info') def add_warning(self, msg): return self.add_message(msg, 'warning') def add_output(self, msg): return self.add_message(msg, 'output') def add_separator(self): model = self.messages.get_model() if not model.get_iter_first(): return model.append([None, None, None, pango.WEIGHT_NORMAL, True, None])