diff options
Diffstat (limited to 'grc/gui/PropsDialog.py')
-rw-r--r-- | grc/gui/PropsDialog.py | 239 |
1 files changed, 115 insertions, 124 deletions
diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index a5b46cbbac..ca1e3c5296 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -17,116 +17,91 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -import pygtk -pygtk.require('2.0') -import gtk +from __future__ import absolute_import +from gi.repository import Gtk, Gdk, GObject, Pango -import Actions -from Dialogs import SimpleTextDisplay -from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT, FONT_SIZE -import Utils -import pango +from . import Actions, Utils, Constants +from .Dialogs import SimpleTextDisplay +import six -TAB_LABEL_MARKUP_TMPL="""\ -#set $foreground = $valid and 'black' or 'red' -<span foreground="$foreground">$encode($tab)</span>""" - -def get_title_label(title): - """ - Get a title label for the params window. - The title will be bold, underlined, and left justified. - - Args: - title: the text of the title - - Returns: - a gtk object - """ - label = gtk.Label() - label.set_markup('\n<b><span underline="low">%s</span>:</b>\n'%title) - hbox = gtk.HBox() - hbox.pack_start(label, False, False, padding=11) - return hbox - - -class PropsDialog(gtk.Dialog): +class PropsDialog(Gtk.Dialog): """ A dialog to set block parameters, view errors, and view documentation. """ - def __init__(self, block): + def __init__(self, parent, block): """ Properties dialog constructor. - Args: + Args:% block: a block instance """ - self._hash = 0 - gtk.Dialog.__init__( + Gtk.Dialog.__init__( self, - title='Properties: %s' % block.get_name(), - buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, - gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_APPLY, gtk.RESPONSE_APPLY) + title='Properties: ' + block.name, + transient_for=parent, + modal=True, + destroy_with_parent=True, + ) + self.add_buttons( + Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT, + Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT, + Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY, ) - self.set_response_sensitive(gtk.RESPONSE_APPLY, False) + self.set_response_sensitive(Gtk.ResponseType.APPLY, False) self.set_size_request(*Utils.scale( - (MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT) + (Constants.MIN_DIALOG_WIDTH, Constants.MIN_DIALOG_HEIGHT) )) + self._block = block + self._hash = 0 - vpaned = gtk.VPaned() - self.vbox.pack_start(vpaned) + vpaned = Gtk.VPaned() + self.vbox.pack_start(vpaned, True, True, 0) # Notebook to hold param boxes - notebook = gtk.Notebook() + notebook = self.notebook = Gtk.Notebook() notebook.set_show_border(False) notebook.set_scrollable(True) # scroll arrows for page tabs - notebook.set_tab_pos(gtk.POS_TOP) + notebook.set_tab_pos(Gtk.PositionType.TOP) vpaned.pack1(notebook, True) # Params boxes for block parameters - self._params_boxes = list() - for tab in block.get_param_tab_labels(): - label = gtk.Label() - vbox = gtk.VBox() - scroll_box = gtk.ScrolledWindow() - scroll_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scroll_box.add_with_viewport(vbox) - notebook.append_page(scroll_box, label) - self._params_boxes.append((tab, label, vbox)) + self._params_boxes = [] + self._build_param_tab_boxes(block.params) # Docs for the block self._docs_text_display = doc_view = SimpleTextDisplay() - doc_view.get_buffer().create_tag('b', weight=pango.WEIGHT_BOLD) - self._docs_box = gtk.ScrolledWindow() - self._docs_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self._docs_box.add_with_viewport(self._docs_text_display) - notebook.append_page(self._docs_box, gtk.Label("Documentation")) + doc_view.get_buffer().create_tag('b', weight=Pango.Weight.BOLD) + self._docs_box = Gtk.ScrolledWindow() + self._docs_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self._docs_box.add(self._docs_text_display) + notebook.append_page(self._docs_box, Gtk.Label(label="Documentation")) # Generated code for the block if Actions.TOGGLE_SHOW_CODE_PREVIEW_TAB.get_active(): self._code_text_display = code_view = SimpleTextDisplay() - code_view.set_wrap_mode(gtk.WRAP_NONE) - code_view.get_buffer().create_tag('b', weight=pango.WEIGHT_BOLD) - code_view.modify_font(pango.FontDescription( - 'monospace %d' % FONT_SIZE)) - code_box = gtk.ScrolledWindow() - code_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - code_box.add_with_viewport(self._code_text_display) - notebook.append_page(code_box, gtk.Label("Generated Code")) + code_view.set_wrap_mode(Gtk.WrapMode.NONE) + code_view.get_buffer().create_tag('b', weight=Pango.Weight.BOLD) + code_view.set_monospace(True) + # todo: set font size in non-deprecated way + # code_view.override_font(Pango.FontDescription('monospace %d' % Constants.FONT_SIZE)) + code_box = Gtk.ScrolledWindow() + code_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + code_box.add(self._code_text_display) + notebook.append_page(code_box, Gtk.Label(label="Generated Code")) else: self._code_text_display = None # Error Messages for the block self._error_messages_text_display = SimpleTextDisplay() - self._error_box = gtk.ScrolledWindow() - self._error_box.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self._error_box.add_with_viewport(self._error_messages_text_display) + self._error_box = Gtk.ScrolledWindow() + self._error_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self._error_box.add(self._error_messages_text_display) vpaned.pack2(self._error_box) - vpaned.set_position(int(0.65 * MIN_DIALOG_HEIGHT)) + vpaned.set_position(int(0.65 * Constants.MIN_DIALOG_HEIGHT)) # Connect events self.connect('key-press-event', self._handle_key_press) @@ -134,6 +109,27 @@ class PropsDialog(gtk.Dialog): self.connect('response', self._handle_response) self.show_all() # show all (performs initial gui update) + def _build_param_tab_boxes(self, params): + tab_labels = (p.tab_label for p in self._block.params.values()) + + def unique_tab_labels(): + seen = {Constants.DEFAULT_PARAM_TAB} + yield Constants.DEFAULT_PARAM_TAB + for tab_label in tab_labels: + if tab_label in seen: + continue + yield tab_label + seen.add(tab_label) + + for tab in unique_tab_labels(): + label = Gtk.Label() + vbox = Gtk.VBox() + scroll_box = Gtk.ScrolledWindow() + scroll_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scroll_box.add(vbox) + self.notebook.append_page(scroll_box, label) + self._params_boxes.append((tab, label, vbox)) + def _params_changed(self): """ Have the params in this dialog changed? @@ -146,25 +142,23 @@ class PropsDialog(gtk.Dialog): true if changed """ old_hash = self._hash - # create a tuple of things from each param that affects the params box - self._hash = hash(tuple([( - hash(param), param.get_name(), param.get_type(), - param.get_hide() == 'all', - ) for param in self._block.get_params()])) - return self._hash != old_hash + new_hash = self._hash = hash(tuple( + (hash(param), param.name, param.get_type(), param.get_hide() == 'all',) + for param in self._block.params.values() + )) + return new_hash != old_hash def _handle_changed(self, *args): """ A change occurred within a param: Rewrite/validate the block and update the gui. """ - # update for the block self._block.rewrite() self._block.validate() self.update_gui() def _activate_apply(self, *args): - self.set_response_sensitive(gtk.RESPONSE_APPLY, True) + self.set_response_sensitive(Gtk.ResponseType.APPLY, True) def update_gui(self, widget=None, force=False): """ @@ -175,45 +169,48 @@ class PropsDialog(gtk.Dialog): Update the documentation block. Hide the box if there are no docs. """ - # update the params box if force or self._params_changed(): # hide params box before changing for tab, label, vbox in self._params_boxes: - vbox.hide_all() + vbox.hide() # empty the params box for child in vbox.get_children(): vbox.remove(child) - child.destroy() + # child.destroy() # disabled because it throws errors... # repopulate the params box box_all_valid = True - for param in filter(lambda p: p.get_tab_label() == tab, self._block.get_params()): - if param.get_hide() == 'all': + for param in self._block.params.values(): + # fixme: why do we even rebuild instead of really hiding params? + if param.get_tab_label() != tab or param.get_hide() == 'all': continue box_all_valid = box_all_valid and param.is_valid() + input_widget = param.get_input(self._handle_changed, self._activate_apply) - vbox.pack_start(input_widget, input_widget.expand) - label.set_markup(Utils.parse_template(TAB_LABEL_MARKUP_TMPL, valid=box_all_valid, tab=tab)) - # show params box with new params - vbox.show_all() - # update the errors box + input_widget.show_all() + vbox.pack_start(input_widget, input_widget.expand, True, 1) + + label.set_markup('<span foreground="{color}">{name}</span>'.format( + color='black' if box_all_valid else 'red', name=Utils.encode(tab) + )) + vbox.show() # show params box with new params + if self._block.is_valid(): self._error_box.hide() else: self._error_box.show() messages = '\n\n'.join(self._block.get_error_messages()) self._error_messages_text_display.set_text(messages) - # update the docs box + self._update_docs_page() - # update the generated code self._update_generated_code_page() def _update_docs_page(self): """Show documentation from XML and try to display best matching docstring""" - buffer = self._docs_text_display.get_buffer() - buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) - pos = buffer.get_end_iter() + buf = self._docs_text_display.get_buffer() + buf.delete(buf.get_start_iter(), buf.get_end_iter()) + pos = buf.get_end_iter() - docstrings = self._block.get_doc() + docstrings = self._block.documentation if not docstrings: return @@ -221,11 +218,11 @@ class PropsDialog(gtk.Dialog): from_xml = docstrings.pop('', '') for line in from_xml.splitlines(): if line.lstrip() == line and line.endswith(':'): - buffer.insert_with_tags_by_name(pos, line + '\n', 'b') + buf.insert_with_tags_by_name(pos, line + '\n', 'b') else: - buffer.insert(pos, line + '\n') + buf.insert(pos, line + '\n') if from_xml: - buffer.insert(pos, '\n') + buf.insert(pos, '\n') # if given the current parameters an exact match can be made block_constructor = self._block.get_make().rsplit('.', 2)[-1] @@ -234,19 +231,19 @@ class PropsDialog(gtk.Dialog): docstrings = {block_class: docstrings[block_class]} # show docstring(s) extracted from python sources - for cls_name, docstring in docstrings.iteritems(): - buffer.insert_with_tags_by_name(pos, cls_name + '\n', 'b') - buffer.insert(pos, docstring + '\n\n') + for cls_name, docstring in six.iteritems(docstrings): + buf.insert_with_tags_by_name(pos, cls_name + '\n', 'b') + buf.insert(pos, docstring + '\n\n') pos.backward_chars(2) - buffer.delete(pos, buffer.get_end_iter()) + buf.delete(pos, buf.get_end_iter()) def _update_generated_code_page(self): if not self._code_text_display: return # user disabled code preview - buffer = self._code_text_display.get_buffer() + buf = self._code_text_display.get_buffer() block = self._block - key = block.get_key() + key = block.key if key == 'epy_block': src = block.get_param('_source_code').get_value() @@ -258,40 +255,34 @@ class PropsDialog(gtk.Dialog): def insert(header, text): if not text: return - buffer.insert_with_tags_by_name(buffer.get_end_iter(), header, 'b') - buffer.insert(buffer.get_end_iter(), text) + buf.insert_with_tags_by_name(buf.get_end_iter(), header, 'b') + buf.insert(buf.get_end_iter(), text) - buffer.delete(buffer.get_start_iter(), buffer.get_end_iter()) + buf.delete(buf.get_start_iter(), buf.get_end_iter()) insert('# Imports\n', '\n'.join(block.get_imports())) - if key.startswith('variable'): + if block.is_variable: insert('\n\n# Variables\n', block.get_var_make()) insert('\n\n# Blocks\n', block.get_make()) if src: insert('\n\n# External Code ({}.py)\n'.format(block.get_id()), src) def _handle_key_press(self, widget, event): - """ - Handle key presses from the keyboard. - Call the ok response when enter is pressed. - - Returns: - false to forward the keypress - """ - if (event.keyval == gtk.keysyms.Return and - event.state & gtk.gdk.CONTROL_MASK == 0 and - not isinstance(widget.get_focus(), gtk.TextView) - ): - self.response(gtk.RESPONSE_ACCEPT) + close_dialog = ( + event.keyval == Gdk.KEY_Return and + event.get_state() & Gdk.ModifierType.CONTROL_MASK == 0 and + not isinstance(widget.get_focus(), Gtk.TextView) + ) + if close_dialog: + self.response(Gtk.ResponseType.ACCEPT) return True # handled here + return False # forward the keypress def _handle_response(self, widget, response): - if response in (gtk.RESPONSE_APPLY, gtk.RESPONSE_ACCEPT): + if response in (Gtk.ResponseType.APPLY, Gtk.ResponseType.ACCEPT): for tab, label, vbox in self._params_boxes: for child in vbox.get_children(): child.apply_pending_changes() - self.set_response_sensitive(gtk.RESPONSE_APPLY, False) + self.set_response_sensitive(Gtk.ResponseType.APPLY, False) return True return False - - |