summaryrefslogtreecommitdiff
path: root/grc
diff options
context:
space:
mode:
authorGlenn Richardson <glenn.richardson@live.com>2016-08-02 22:45:02 +0200
committerSebastian Koslowski <koslowski@kit.edu>2016-08-09 21:02:24 +0200
commitcbe1e43b0f0d1ee0d356b7110700400578855ac6 (patch)
treee72d5fbcf7c343e896dcdb9e145456931ef6d97d /grc
parente682374d9f7eda4fd2c2701092470ee912e6c33b (diff)
grc: gtk3: fixup dialogs
Diffstat (limited to 'grc')
-rw-r--r--grc/gui/ActionHandler.py30
-rw-r--r--grc/gui/Dialogs.py401
-rw-r--r--grc/gui/FileDialogs.py101
-rw-r--r--grc/gui/FlowGraph.py6
-rw-r--r--grc/gui/MainWindow.py8
-rw-r--r--grc/gui/ParamWidgets.py2
-rw-r--r--grc/gui/PropsDialog.py1
7 files changed, 326 insertions, 223 deletions
diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py
index 237dd6c84c..9e57565772 100644
--- a/grc/gui/ActionHandler.py
+++ b/grc/gui/ActionHandler.py
@@ -26,10 +26,7 @@ import subprocess
from gi.repository import Gtk
from gi.repository import GObject
-from . import Dialogs, Preferences, Actions, Executor, Constants
-from .FileDialogs import (OpenFlowGraphFileDialog, SaveFlowGraphFileDialog,
- SaveConsoleFileDialog, SaveScreenShotDialog,
- OpenQSSFileDialog)
+from . import Dialogs, Preferences, Actions, Executor, Constants, FileDialogs
from .MainWindow import MainWindow
from .ParserErrorsDialog import ParserErrorsDialog
from .PropsDialog import PropsDialog
@@ -379,13 +376,13 @@ class ActionHandler:
# Window stuff
##################################################
elif action == Actions.ABOUT_WINDOW_DISPLAY:
- Dialogs.AboutDialog(self.platform.config)
+ Dialogs.show_about(main, self.platform.config)
elif action == Actions.HELP_WINDOW_DISPLAY:
- Dialogs.HelpDialog()
+ Dialogs.show_help(main)
elif action == Actions.TYPES_WINDOW_DISPLAY:
- Dialogs.TypesDialog(self.platform)
+ Dialogs.show_types(main)
elif action == Actions.ERRORS_WINDOW_DISPLAY:
- Dialogs.ErrorsDialog(flow_graph)
+ Dialogs.ErrorsDialog(main, flow_graph).run_and_destroy()
elif action == Actions.TOGGLE_CONSOLE_WINDOW:
main.update_panel_visibility(main.CONSOLE, action.get_active())
action.save_to_preferences()
@@ -401,7 +398,7 @@ class ActionHandler:
elif action == Actions.CLEAR_CONSOLE:
main.text_display.clear()
elif action == Actions.SAVE_CONSOLE:
- file_path = SaveConsoleFileDialog(page.file_path).run()
+ file_path = FileDialogs.SaveConsole(main, page.file_path).run()
if file_path is not None:
main.text_display.save(file_path)
elif action == Actions.TOGGLE_HIDE_DISABLED_BLOCKS:
@@ -437,7 +434,7 @@ class ActionHandler:
# Leave it enabled
action.set_sensitive(True)
action.set_active(True)
- #Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_sensitive(action.get_active())
+ # Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR.set_sensitive(action.get_active())
action.save_to_preferences()
elif action == Actions.TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR:
if self.init:
@@ -511,7 +508,7 @@ class ActionHandler:
flow_graph._options_block.get_param('generate_options').set_value(args[0])
flow_graph_update()
elif action == Actions.FLOW_GRAPH_OPEN:
- file_paths = args if args else OpenFlowGraphFileDialog(page.file_path).run()
+ file_paths = args if args else FileDialogs.OpenFlowGraph(main, page.file_path).run()
if file_paths: # Open a new page for each file, show only the first
for i,file_path in enumerate(file_paths):
main.new_page(file_path, show=(i==0))
@@ -519,8 +516,8 @@ class ActionHandler:
main.tool_bar.refresh_submenus()
main.menu_bar.refresh_submenus()
elif action == Actions.FLOW_GRAPH_OPEN_QSS_THEME:
- file_paths = OpenQSSFileDialog(self.platform.config.install_prefix +
- '/share/gnuradio/themes/').run()
+ file_paths = FileDialogs.OpenQSS(main, self.platform.config.install_prefix +
+ '/share/gnuradio/themes/').run()
if file_paths:
try:
prefs = self.platform.config.prefs
@@ -544,7 +541,7 @@ class ActionHandler:
Messages.send_fail_save(page.file_path)
page.saved = False
elif action == Actions.FLOW_GRAPH_SAVE_AS:
- file_path = SaveFlowGraphFileDialog(page.file_path).run()
+ file_path = FileDialogs.SaveFlowGraph(main, page.file_path).run()
if file_path is not None:
page.file_path = os.path.abspath(file_path)
Actions.FLOW_GRAPH_SAVE()
@@ -552,7 +549,7 @@ class ActionHandler:
main.tool_bar.refresh_submenus()
main.menu_bar.refresh_submenus()
elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE:
- file_path, background_transparent = SaveScreenShotDialog(page.file_path).run()
+ file_path, background_transparent = FileDialogs.SaveScreenShot(main, page.file_path).run()
if file_path is not None:
pixbuf = flow_graph.get_drawing_area().get_screenshot(background_transparent)
pixbuf.save(file_path, Constants.IMAGE_FILE_EXTENSION[1:])
@@ -576,9 +573,10 @@ class ActionHandler:
if not page.process:
Actions.FLOW_GRAPH_GEN()
xterm = self.platform.config.xterm_executable
+ Dialogs.show_missing_xterm(main, xterm)
if Preferences.xterm_missing() != xterm:
if not os.path.exists(xterm):
- Dialogs.MissingXTermDialog(xterm)
+ Dialogs.show_missing_xterm(main, xterm)
Preferences.xterm_missing(xterm)
if page.saved and page.file_path:
Executor.ExecFlowGraphThread(
diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py
index da271fe46e..da4b11a3b6 100644
--- a/grc/gui/Dialogs.py
+++ b/grc/gui/Dialogs.py
@@ -1,38 +1,36 @@
-"""
-Copyright 2008, 2009 Free Software Foundation, Inc.
-This file is part of GNU Radio
-
-GNU Radio Companion is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-GNU Radio Companion is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-"""
+# Copyright 2008, 2009, 2016 Free Software Foundation, Inc.
+# This file is part of GNU Radio
+#
+# GNU Radio Companion is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# GNU Radio Companion is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from __future__ import absolute_import
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import GObject
import sys
+import textwrap
from distutils.spawn import find_executable
+from gi.repository import Gtk
+
from . import Utils, Actions, Constants
from ..core import Messages
class SimpleTextDisplay(Gtk.TextView):
- """A non editable gtk text view."""
+ """
+ A non user-editable gtk text view.
+ """
def __init__(self, text=''):
"""
@@ -41,16 +39,18 @@ class SimpleTextDisplay(Gtk.TextView):
Args:
text: the text to display (string)
"""
- text_buffer = Gtk.TextBuffer()
- text_buffer.set_text(text)
- self.set_text = text_buffer.set_text
- GObject.GObject.__init__(self)
+ Gtk.TextView.__init__(self)
+ self.set_text = self.get_buffer().set_text
+ self.set_text(text)
self.set_editable(False)
self.set_cursor_visible(False)
self.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
class TextDisplay(SimpleTextDisplay):
+ """
+ A non user-editable scrollable text view with popup menu.
+ """
def __init__(self, text=''):
"""
@@ -64,54 +64,77 @@ class TextDisplay(SimpleTextDisplay):
self.connect("populate-popup", self.populate_popup)
def insert(self, line):
- # make backspaces work
+ """
+ Append text after handling backspaces and auto-scroll.
+
+ Args:
+ line: the text to append (string)
+ """
line = self._consume_backspaces(line)
- # add the remaining text to buffer
self.get_buffer().insert(self.get_buffer().get_end_iter(), line)
- # Automatically scroll on insert
self.scroll_to_end()
def _consume_backspaces(self, line):
- """removes text from the buffer if line starts with \b*"""
- if not line: return
+ """
+ Removes text from the buffer if line starts with '\b'
+
+ Args:
+ line: a string which may contain backspaces
+
+ Returns:
+ The string that remains from 'line' with leading '\b's removed.
+ """
+ if not line:
+ return
+
# for each \b delete one char from the buffer
back_count = 0
start_iter = self.get_buffer().get_end_iter()
while line[back_count] == '\b':
# stop at the beginning of a line
- if not start_iter.starts_line(): start_iter.backward_char()
+ if not start_iter.starts_line():
+ start_iter.backward_char()
back_count += 1
- # remove chars
+ # remove chars from buffer
self.get_buffer().delete(start_iter, self.get_buffer().get_end_iter())
- # return remaining text
return line[back_count:]
def scroll_to_end(self):
+ """ Update view's scroll position. """
if self.scroll_lock:
- buffer = self.get_buffer()
- buffer.move_mark(buffer.get_insert(), buffer.get_end_iter())
+ buf = self.get_buffer()
+ buf.move_mark(buf.get_insert(), buf.get_end_iter())
# TODO: Fix later
#self.scroll_to_mark(buffer.get_insert(), 0.0)
def clear(self):
- buffer = self.get_buffer()
- buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
+ """ Clear all text from buffer. """
+ buf = self.get_buffer()
+ buf.delete(buf.get_start_iter(), buf.get_end_iter())
def save(self, file_path):
- console_file = open(file_path, 'w')
- buffer = self.get_buffer()
- console_file.write(buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True))
- console_file.close()
+ """
+ Save context of buffer to the given file.
+
+ Args:
+ file_path: location to save buffer contents
+ """
+ with open(file_path, 'w') as logfile:
+ buf = self.get_buffer()
+ logfile.write(buf.get_text(buf.get_start_iter(),
+ buf.get_end_iter(), True))
- # Callback functions to handle the scrolling lock and clear context menus options
# Action functions are set by the ActionHandler's init function
def clear_cb(self, menu_item, web_view):
+ """ Callback function to clear the text buffer """
Actions.CLEAR_CONSOLE()
def scroll_back_cb(self, menu_item, web_view):
+ """ Callback function to toggle scroll lock """
Actions.TOGGLE_SCROLL_LOCK()
def save_cb(self, menu_item, web_view):
+ """ Callback function to save the buffer """
Actions.SAVE_CONSOLE()
def populate_popup(self, view, menu):
@@ -134,79 +157,151 @@ class TextDisplay(SimpleTextDisplay):
return False
-def MessageDialogHelper(type, buttons, title=None, markup=None, default_response=None, extra_buttons=None):
- """
- Create a modal message dialog and run it.
+class MessageDialogWrapper(Gtk.MessageDialog):
+ """ Run a message dialog. """
- Args:
- type: the type of message: Gtk.MessageType.INFO, Gtk.MessageType.WARNING, Gtk.MessageType.QUESTION or Gtk.MessageType.ERROR
- buttons: the predefined set of buttons to use:
- Gtk.ButtonsType.NONE, Gtk.ButtonsType.OK, Gtk.ButtonsType.CLOSE, Gtk.ButtonsType.CANCEL, Gtk.ButtonsType.YES_NO, Gtk.ButtonsType.OK_CANCEL
-
- Args:
- title: the title of the window (string)
- markup: the message text with pango markup
- default_response: if set, determines which button is highlighted by default
- extra_buttons: a tuple containing pairs of values; each value is the button's text and the button's return value
-
- Returns:
- the gtk response from run()
- """
- message_dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, type, buttons)
- if title: message_dialog.set_title(title)
- if markup: message_dialog.set_markup(markup)
- if extra_buttons: message_dialog.add_buttons(*extra_buttons)
- if default_response: message_dialog.set_default_response(default_response)
- response = message_dialog.run()
- message_dialog.destroy()
- return response
-
-
-def ErrorsDialog(flowgraph):
- MessageDialogHelper(
- type=Gtk.MessageType.ERROR,
- buttons=Gtk.ButtonsType.CLOSE,
- title='Flow Graph Errors',
- markup='\n\n'.join(
- '<b>Error {num}:</b>\n{msg}'.format(num=i, msg=Utils.encode(msg.replace('\t', ' ')))
- for i, msg in enumerate(flowgraph.get_error_messages())
- ),
- )
-
-
-class AboutDialog(Gtk.AboutDialog):
- """A cute little about dialog."""
-
- def __init__(self, config):
- """AboutDialog constructor."""
- GObject.GObject.__init__(self)
- self.set_name(config.name)
- self.set_version(config.version)
- self.set_license(config.license)
- self.set_copyright(config.license.splitlines()[0])
- self.set_website(config.website)
- self.run()
- self.destroy()
-
-
-def HelpDialog(): MessageDialogHelper(
- type=Gtk.MessageType.INFO,
- buttons=Gtk.ButtonsType.CLOSE,
- title='Help',
- markup="""\
-<b>Usage Tips</b>
-
-<u>Add block</u>: drag and drop or double click a block in the block selection window.
-<u>Rotate block</u>: Select a block, press left/right on the keyboard.
-<u>Change type</u>: Select a block, press up/down on the keyboard.
-<u>Edit parameters</u>: double click on a block in the flow graph.
-<u>Make connection</u>: click on the source port of one block, then click on the sink port of another block.
-<u>Remove connection</u>: select the connection and press delete, or drag the connection.
-
-* See the menu for other keyboard shortcuts.""")
+ def __init__(self, parent, message_type, buttons, title=None, markup=None,
+ default_response=None, extra_buttons=None):
+ """
+ Create a modal message dialog.
+ Args:
+ message_type: the type of message may be one of:
+ Gtk.MessageType.INFO
+ Gtk.MessageType.WARNING
+ Gtk.MessageType.QUESTION or Gtk.MessageType.ERROR
+ buttons: the predefined set of buttons to use:
+ Gtk.ButtonsType.NONE
+ Gtk.ButtonsType.OK
+ Gtk.ButtonsType.CLOSE
+ Gtk.ButtonsType.CANCEL
+ Gtk.ButtonsType.YES_NO
+ Gtk.ButtonsType.OK_CANCEL
+ title: the title of the window (string)
+ markup: the message text with pango markup
+ default_response: if set, determines which button is highlighted by default
+ extra_buttons: a tuple containing pairs of values:
+ each value is the button's text and the button's return value
-def TypesDialog(platform):
+ """
+ Gtk.MessageDialog.__init__(
+ self, transient_for=parent, modal=True, destroy_with_parent=True,
+ message_type=message_type, buttons=buttons
+ )
+ if title:
+ self.set_title(title)
+ if markup:
+ self.set_markup(markup)
+ if extra_buttons:
+ self.add_buttons(*extra_buttons)
+ if default_response:
+ self.set_default_response(default_response)
+
+ def run_and_destroy(self):
+ response = self.run()
+ self.hide()
+ return response
+
+
+class ErrorsDialog(Gtk.Dialog):
+ """ Display flowgraph errors. """
+
+ def __init__(self, parent, flowgraph):
+ """Create a listview of errors"""
+ Gtk.Dialog.__init__(
+ self,
+ title='Errors and Warnings',
+ transient_for=parent,
+ modal=True,
+ destroy_with_parent=True,
+ )
+ self.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)
+ self.set_size_request(750, Constants.MIN_DIALOG_HEIGHT)
+ self.set_border_width(10)
+
+ self.store = Gtk.ListStore(str, str, str)
+ self.update(flowgraph)
+
+ self.treeview = Gtk.TreeView(model=self.store)
+ for i, column_title in enumerate(["Block", "Aspect", "Message"]):
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn(column_title, renderer, text=i)
+ column.set_sort_column_id(i) # liststore id matches treeview id
+ column.set_resizable(True)
+ self.treeview.append_column(column)
+
+ self.scrollable = Gtk.ScrolledWindow()
+ self.scrollable.set_vexpand(True)
+ self.scrollable.add(self.treeview)
+
+ self.vbox.pack_start(self.scrollable, True, True, 0)
+ self.show_all()
+
+ def update(self, flowgraph):
+ self.store.clear()
+ for element, message in flowgraph.iter_error_messages():
+ if element.is_block:
+ src, aspect = element.get_id(), ''
+ elif element.is_connection:
+ src = element.source_block.get_id()
+ aspect = "Connection to '{}'".format(element.sink_block.get_id())
+ elif element.is_port:
+ src = element.parent_block.get_id()
+ aspect = "{} '{}'".format('Sink' if element.is_sink else 'Source', element.name)
+ elif element.is_param:
+ src = element.parent_block.get_id()
+ aspect = "Param '{}'".format(element.name)
+ else:
+ src = aspect = ''
+ self.store.append([src, aspect, message])
+
+ def run_and_destroy(self):
+ response = self.run()
+ self.hide()
+ return response
+
+
+def show_about(parent, config):
+ ad = Gtk.AboutDialog(transient_for=parent)
+ ad.set_program_name(config.name)
+ ad.set_name('')
+ ad.set_version(config.version)
+ ad.set_license(config.license)
+
+ try:
+ ad.set_logo(Gtk.IconTheme().load_icon('gnuradio-grc', 64, 0))
+ except:
+ pass
+
+ ad.set_copyright(config.license.splitlines()[0])
+ ad.set_website(config.website)
+
+ ad.connect("response", lambda action, param: action.hide())
+ ad.show()
+
+
+def show_help(parent):
+ """ Display basic usage tips. """
+ markup = textwrap.dedent("""\
+ <b>Usage Tips</b>
+ \n\
+ <u>Add block</u>: drag and drop or double click a block in the block selection window.
+ <u>Rotate block</u>: Select a block, press left/right on the keyboard.
+ <u>Change type</u>: Select a block, press up/down on the keyboard.
+ <u>Edit parameters</u>: double click on a block in the flow graph.
+ <u>Make connection</u>: click on the source port of one block, then click on the sink port of another block.
+ <u>Remove connection</u>: select the connection and press delete, or drag the connection.
+ \n\
+ * See the menu for other keyboard shortcuts.\
+ """)
+
+ MessageDialogWrapper(
+ parent, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, title='Help', markup=markup
+ ).run_and_destroy()
+
+
+def show_types(parent):
+ """ Display information about standard data types. """
colors = [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES]
max_len = 10 + max(len(name) for name, code in colors)
@@ -215,62 +310,65 @@ def TypesDialog(platform):
''.format(color=color, name=Utils.encode(name).center(max_len))
for name, color in colors
)
- MessageDialogHelper(
- type=Gtk.MessageType.INFO,
- buttons=Gtk.ButtonsType.CLOSE,
- title='Types - Color Mapping',
- markup=message
- )
+ MessageDialogWrapper(
+ parent, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, title='Types - Color Mapping', markup=message
+ ).run_and_destroy()
-def MissingXTermDialog(xterm):
- MessageDialogHelper(
- type=Gtk.MessageType.WARNING,
- buttons=Gtk.ButtonsType.OK,
- title='Warning: missing xterm executable',
- markup=("The xterm executable {0!r} is missing.\n\n"
- "You can change this setting in your gnuradio.conf, in "
- "section [grc], 'xterm_executable'.\n"
- "\n"
- "(This message is shown only once)").format(xterm)
- )
+def show_missing_xterm(parent, xterm):
+ markup = textwrap.dedent("""\
+ The xterm executable {0!r} is missing.
+ You can change this setting in your gnurado.conf, in section [grc], 'xterm_executable'.
+ \n\
+ (This message is shown only once)\
+ """).format(xterm)
+
+ MessageDialogWrapper(
+ parent, message_type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK,
+ title='Warning: missing xterm executable', markup=markup
+ ).run_and_destroy()
+
+
+def choose_editor(parent, config):
+ """
+ Give the option to either choose an editor or use the default.
+ """
+ if config.editor and find_executable(config.editor):
+ return config.editor
-def ChooseEditorDialog(config):
- # Give the option to either choose an editor or use the default
- # Always return true/false so the caller knows it was successful
buttons = (
'Choose Editor', Gtk.ResponseType.YES,
'Use Default', Gtk.ResponseType.NO,
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL
)
- response = MessageDialogHelper(
- Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, 'Choose Editor',
- 'Would you like to choose the editor to use?', Gtk.ResponseType.YES, buttons
- )
+ response = MessageDialogWrapper(
+ parent, message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.NONE,
+ title='Choose Editor', markup='Would you like to choose the editor to use?',
+ default_response=Gtk.ResponseType.YES, extra_buttons=buttons
+ ).run_and_destroy()
- # Handle the inital default/choose/cancel response
+ # Handle the initial default/choose/cancel response
# User wants to choose the editor to use
+ editor = ''
if response == Gtk.ResponseType.YES:
file_dialog = Gtk.FileChooserDialog(
'Select an Editor...', None,
Gtk.FileChooserAction.OPEN,
- ('gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK)
+ ('gtk-cancel', Gtk.ResponseType.CANCEL, 'gtk-open', Gtk.ResponseType.OK),
+ transient_for=parent
)
file_dialog.set_select_multiple(False)
file_dialog.set_local_only(True)
file_dialog.set_current_folder('/usr/bin')
try:
if file_dialog.run() == Gtk.ResponseType.OK:
- config.editor = file_path = file_dialog.get_filename()
- file_dialog.destroy()
- return file_path
+ editor = file_dialog.get_filename()
finally:
- file_dialog.destroy()
+ file_dialog.hide()
# Go with the default editor
elif response == Gtk.ResponseType.NO:
- # Determine the platform
try:
process = None
if sys.platform.startswith('linux'):
@@ -280,13 +378,10 @@ def ChooseEditorDialog(config):
if process is None:
raise ValueError("Can't find default editor executable")
# Save
- config.editor = process
- return process
+ editor = config.editor = process
except Exception:
Messages.send('>>> Unable to load the default editor. Please choose an editor.\n')
- # Just reset of the constant and force the user to select an editor the next time
- config.editor = ''
- return
- Messages.send('>>> No editor selected.\n')
- return
+ if editor == '':
+ Messages.send('>>> No editor selected.\n')
+ return editor
diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py
index 23fd7e7900..afd41af58c 100644
--- a/grc/gui/FileDialogs.py
+++ b/grc/gui/FileDialogs.py
@@ -18,18 +18,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
from __future__ import absolute_import
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import GObject
-from .Dialogs import MessageDialogHelper
-from .Constants import \
- DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, TEXT_FILE_EXTENSION, \
- NEW_FLOGRAPH_TITLE
-from . import Preferences
from os import path
-from . import Utils
+
+from gi.repository import Gtk
+
+from . import Constants, Preferences, Utils
+from .Dialogs import MessageDialogWrapper
+
##################################################
# Constants
@@ -52,14 +48,14 @@ def get_flow_graph_files_filter():
def get_text_files_filter():
filter = Gtk.FileFilter()
filter.set_name('Text Files')
- filter.add_pattern('*'+TEXT_FILE_EXTENSION)
+ filter.add_pattern('*' + Constants.TEXT_FILE_EXTENSION)
return filter
def get_image_files_filter():
filter = Gtk.FileFilter()
filter.set_name('Image Files')
- filter.add_pattern('*'+IMAGE_FILE_EXTENSION)
+ filter.add_pattern('*' + Constants.IMAGE_FILE_EXTENSION)
return filter
@@ -84,7 +80,7 @@ class FileDialogHelper(Gtk.FileChooserDialog):
Implement a file chooser dialog with only necessary parameters.
"""
- def __init__(self, action, title):
+ def __init__(self, parent, action, title):
"""
FileDialogHelper contructor.
Create a save or open dialog with cancel and ok buttons.
@@ -94,8 +90,16 @@ class FileDialogHelper(Gtk.FileChooserDialog):
action: Gtk.FileChooserAction.OPEN or Gtk.FileChooserAction.SAVE
title: the title of the dialog (string)
"""
- ok_stock = {Gtk.FileChooserAction.OPEN : 'gtk-open', Gtk.FileChooserAction.SAVE : 'gtk-save'}[action]
- Gtk.FileChooserDialog.__init__(self, title, None, action, ('gtk-cancel', Gtk.ResponseType.CANCEL, ok_stock, Gtk.ResponseType.OK))
+ ok_stock = {
+ Gtk.FileChooserAction.OPEN: 'gtk-open',
+ Gtk.FileChooserAction.SAVE: 'gtk-save'
+ }[action]
+
+ Gtk.FileChooserDialog.__init__(
+ self, title=title, action=action,
+ transient_for=parent
+ )
+ self.add_buttons('gtk-cancel', Gtk.ResponseType.CANCEL, ok_stock, Gtk.ResponseType.OK)
self.set_select_multiple(False)
self.set_local_only(True)
self.add_filter(get_all_files_filter())
@@ -104,37 +108,39 @@ class FileDialogHelper(Gtk.FileChooserDialog):
class FileDialog(FileDialogHelper):
"""A dialog box to save or open flow graph files. This is a base class, do not use."""
- def __init__(self, current_file_path=''):
+ def __init__(self, parent, current_file_path=''):
"""
FileDialog constructor.
Args:
current_file_path: the current directory or path to the open flow graph
"""
- if not current_file_path: current_file_path = path.join(DEFAULT_FILE_PATH, NEW_FLOGRAPH_TITLE + Preferences.file_extension())
+ if not current_file_path:
+ current_file_path = path.join(Constants.DEFAULT_FILE_PATH,
+ Constants.NEW_FLOGRAPH_TITLE + Preferences.file_extension())
if self.type == OPEN_FLOW_GRAPH:
- FileDialogHelper.__init__(self, Gtk.FileChooserAction.OPEN, 'Open a Flow Graph from a File...')
+ FileDialogHelper.__init__(self, parent, Gtk.FileChooserAction.OPEN, 'Open a Flow Graph from a File...')
self.add_and_set_filter(get_flow_graph_files_filter())
self.set_select_multiple(True)
elif self.type == SAVE_FLOW_GRAPH:
- FileDialogHelper.__init__(self, Gtk.FileChooserAction.SAVE, 'Save a Flow Graph to a File...')
+ FileDialogHelper.__init__(self, parent, Gtk.FileChooserAction.SAVE, 'Save a Flow Graph to a File...')
self.add_and_set_filter(get_flow_graph_files_filter())
self.set_current_name(path.basename(current_file_path))
elif self.type == SAVE_CONSOLE:
- FileDialogHelper.__init__(self, Gtk.FileChooserAction.SAVE, 'Save Console to a File...')
+ FileDialogHelper.__init__(self, parent, Gtk.FileChooserAction.SAVE, 'Save Console to a File...')
self.add_and_set_filter(get_text_files_filter())
file_path = path.splitext(path.basename(current_file_path))[0]
- self.set_current_name(file_path) #show the current filename
+ self.set_current_name(file_path) # show the current filename
elif self.type == SAVE_IMAGE:
- FileDialogHelper.__init__(self, Gtk.FileChooserAction.SAVE, 'Save a Flow Graph Screen Shot...')
+ FileDialogHelper.__init__(self, parent, Gtk.FileChooserAction.SAVE, 'Save a Flow Graph Screen Shot...')
self.add_and_set_filter(get_image_files_filter())
- current_file_path = current_file_path + IMAGE_FILE_EXTENSION
- self.set_current_name(path.basename(current_file_path)) #show the current filename
+ current_file_path = current_file_path + Constants.IMAGE_FILE_EXTENSION
+ self.set_current_name(path.basename(current_file_path)) # show the current filename
elif self.type == OPEN_QSS_THEME:
- FileDialogHelper.__init__(self, Gtk.FileChooserAction.OPEN, 'Open a QSS theme...')
+ FileDialogHelper.__init__(self, parent, Gtk.FileChooserAction.OPEN, 'Open a QSS theme...')
self.add_and_set_filter(get_qss_themes_filter())
self.set_select_multiple(False)
- self.set_current_folder(path.dirname(current_file_path)) #current directory
+ self.set_current_folder(path.dirname(current_file_path)) # current directory
def add_and_set_filter(self, filter):
"""
@@ -156,7 +162,7 @@ class FileDialog(FileDialogHelper):
Returns:
the complete file path
"""
- if Gtk.FileChooserDialog.run(self) != Gtk.ResponseType.OK: return None #response was cancel
+ if Gtk.FileChooserDialog.run(self) != Gtk.ResponseType.OK: return None # response was cancel
#############################################
# Handle Save Dialogs
#############################################
@@ -164,18 +170,21 @@ class FileDialog(FileDialogHelper):
filename = self.get_filename()
extension = {
SAVE_FLOW_GRAPH: Preferences.file_extension(),
- SAVE_CONSOLE: TEXT_FILE_EXTENSION,
- SAVE_IMAGE: IMAGE_FILE_EXTENSION,
+ SAVE_CONSOLE: Constants.TEXT_FILE_EXTENSION,
+ SAVE_IMAGE: Constants.IMAGE_FILE_EXTENSION,
}[self.type]
- #append the missing file extension if the filter matches
- if path.splitext(filename)[1].lower() != extension: filename += extension
- self.set_current_name(path.basename(filename)) #show the filename with extension
- if path.exists(filename): #ask the user to confirm overwrite
- if MessageDialogHelper(
+ # append the missing file extension if the filter matches
+ if path.splitext(filename)[1].lower() != extension:
+ filename += extension
+ self.set_current_name(path.basename(filename)) # show the filename with extension
+ if path.exists(filename): # ask the user to confirm overwrite
+ response = MessageDialogWrapper(
Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, 'Confirm Overwrite!',
'File <b>{filename}</b> Exists!\nWould you like to overwrite the existing file?'
''.format(filename=Utils.encode(filename)),
- ) == Gtk.ResponseType.NO: return self.get_rectified_filename()
+ ).run_and_destroy()
+ if response == Gtk.ResponseType.NO:
+ return self.get_rectified_filename()
return filename
#############################################
# Handle Open Dialogs
@@ -183,11 +192,11 @@ class FileDialog(FileDialogHelper):
elif self.type in (OPEN_FLOW_GRAPH, OPEN_QSS_THEME):
filenames = self.get_filenames()
for filename in filenames:
- if not path.exists(filename): #show a warning and re-run
- MessageDialogHelper(
+ if not path.exists(filename): # show a warning and re-run
+ MessageDialogWrapper(
Gtk.MessageType.WARNING, Gtk.ButtonsType.CLOSE, 'Cannot Open!',
'File <b>{filename}</b> Does not Exist!'.format(filename=Utils.encode(filename)),
- )
+ ).run_and_destroy()
return self.get_rectified_filename()
return filenames
@@ -203,19 +212,19 @@ class FileDialog(FileDialogHelper):
return filename
-class OpenFlowGraphFileDialog(FileDialog):
+class OpenFlowGraph(FileDialog):
type = OPEN_FLOW_GRAPH
-class SaveFlowGraphFileDialog(FileDialog):
+class SaveFlowGraph(FileDialog):
type = SAVE_FLOW_GRAPH
-class OpenQSSFileDialog(FileDialog):
+class OpenQSS(FileDialog):
type = OPEN_QSS_THEME
-class SaveConsoleFileDialog(FileDialog):
+class SaveConsole(FileDialog):
type = SAVE_CONSOLE
@@ -223,11 +232,11 @@ class SaveImageFileDialog(FileDialog):
type = SAVE_IMAGE
-class SaveScreenShotDialog(SaveImageFileDialog):
+class SaveScreenShot(SaveImageFileDialog):
- def __init__(self, current_file_path=''):
- SaveImageFileDialog.__init__(self, current_file_path)
- self._button = button = Gtk.CheckButton('_Background transparent')
+ def __init__(self, parent, current_file_path=''):
+ SaveImageFileDialog.__init__(self, parent, current_file_path)
+ self._button = button = Gtk.CheckButton(label='Background transparent')
self._button.set_active(Preferences.screen_shot_background_transparent())
self.set_extra_widget(button)
diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py
index 83796f35fd..7333519a4f 100644
--- a/grc/gui/FlowGraph.py
+++ b/grc/gui/FlowGraph.py
@@ -28,7 +28,7 @@ from itertools import count
import six
from six.moves import filter
-from gi.repository import GObject
+from gi.repository import GLib
from . import Actions, Colors, Utils, Bars, Dialogs
from .Element import Element
@@ -95,7 +95,7 @@ class FlowGraph(CoreFlowgraph, Element):
else:
config = self.parent_platform.config
editor = (find_executable(config.editor) or
- Dialogs.ChooseEditorDialog(config))
+ Dialogs.choose_editor(None, config)) # todo: pass in parent
if not editor:
return
updater = functools.partial(
@@ -103,7 +103,7 @@ class FlowGraph(CoreFlowgraph, Element):
editor = self._external_updaters[target] = ExternalEditor(
editor=editor,
name=target[0], value=param.get_value(),
- callback=functools.partial(GObject.idle_add, updater)
+ callback=functools.partial(GLib.idle_add, updater)
)
editor.start()
try:
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 97f9033974..efa8573c3b 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -33,7 +33,7 @@ from .BlockTreeWindow import BlockTreeWindow
from .VariableEditor import VariableEditor
from .Constants import \
NEW_FLOGRAPH_TITLE, DEFAULT_CONSOLE_WINDOW_WIDTH
-from .Dialogs import TextDisplay, MessageDialogHelper
+from .Dialogs import TextDisplay, MessageDialogWrapper
from .NotebookPage import NotebookPage
from ..core import Messages
@@ -398,10 +398,10 @@ class MainWindow(Gtk.Window):
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK
)
- return MessageDialogHelper(
- Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, 'Unsaved Changes!',
+ return MessageDialogWrapper(
+ self, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, 'Unsaved Changes!',
'Would you like to save changes before closing?', Gtk.ResponseType.OK, buttons
- )
+ ).run_and_destroy()
def _get_files(self):
"""
diff --git a/grc/gui/ParamWidgets.py b/grc/gui/ParamWidgets.py
index 4ab7da6d9a..e5657c288e 100644
--- a/grc/gui/ParamWidgets.py
+++ b/grc/gui/ParamWidgets.py
@@ -168,7 +168,7 @@ class PythonEditorParam(InputParam):
def __init__(self, *args, **kwargs):
InputParam.__init__(self, *args, **kwargs)
- button = self._button = Gtk.Button('Open in Editor')
+ button = self._button = Gtk.Button(label='Open in Editor')
button.connect('clicked', self.open_editor)
self.pack_start(button, True, True, True)
diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py
index 9543a62094..3a0f6ae6de 100644
--- a/grc/gui/PropsDialog.py
+++ b/grc/gui/PropsDialog.py
@@ -43,6 +43,7 @@ class PropsDialog(Gtk.Dialog):
title='Properties: ' + block.name,
transient_for=parent,
modal=True,
+ destroy_with_parent=True,
)
self.add_buttons(
Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT,