def test_editor_widget_not_leaked(self): widget = MultiPythonFileInterpreter() self.assertEqual(1, widget.editor_count) widget.append_new_editor() self.assertEqual(2, widget.editor_count) widget.append_new_editor() self.assertEqual(3, widget.editor_count) widget.close_tab(0) QApplication.processEvents() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 2) widget.close_tab(0) QApplication.processEvents() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 1) widget.close_all() QApplication.processEvents() # there should be zero interpreters self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 0) # close the whole widget, this should delete everything from the QApplication widget.close() QApplication.processEvents() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 0) self.assert_no_toplevel_widgets()
def test_editor_widget_not_leaked(self): widget = MultiPythonFileInterpreter() self.assertEqual(1, widget.editor_count) widget.append_new_editor() self.assertEqual(2, widget.editor_count) widget.append_new_editor() self.assertEqual(3, widget.editor_count) widget.close_tab(0) QApplication.processEvents() self.assert_number_of_widgets_matching( ".interpreter.PythonFileInterpreter", 2) widget.close_tab(0) QApplication.processEvents() self.assert_number_of_widgets_matching( ".interpreter.PythonFileInterpreter", 1) widget.close_all() QApplication.processEvents() # there will always be 1, because we never allow an empty editor widget self.assert_number_of_widgets_matching( ".interpreter.PythonFileInterpreter", 1)
def test_editor_widget_doesnt_create_find_replace_unless_requested(self): widget = MultiPythonFileInterpreter() self.assertEqual(1, widget.editor_count) widget.append_new_editor() self.assert_number_of_widgets_matching( ".interpreter.PythonFileInterpreter", 2) self.assert_number_of_widgets_matching("Embedded", 0) widget.close_tab(1) QApplication.processEvents() # there will always be 1, because we never allow an empty editor widget self.assert_number_of_widgets_matching( ".interpreter.PythonFileInterpreter", 1) self.assert_number_of_widgets_matching("Embedded", 0)
def test_editor_widget_doesnt_create_find_replace_unless_requested(self): widget = MultiPythonFileInterpreter() self.assertEqual(1, widget.editor_count) widget.append_new_editor() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 2) self.assert_number_of_widgets_matching("Embedded", 0) widget.close_tab(1) QApplication.processEvents() # there will always be 1, because we never allow an empty editor widget self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 1) self.assert_number_of_widgets_matching("Embedded", 0) # close the whole widget, this should delete everything from the QApplication widget.close() QApplication.processEvents() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 0) self.assert_no_toplevel_widgets()
def test_editor_widget_deletes_find_replace_dialog(self): widget = MultiPythonFileInterpreter() self.assertEqual(1, widget.editor_count) widget.append_new_editor() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 2) widget.current_editor().show_find_replace_dialog() self.assert_number_of_widgets_matching("Embedded", 1) widget.close_tab(1) QApplication.processEvents() # there will always be 1, because we never allow an empty editor widget self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 1) self.assert_number_of_widgets_matching("Embedded", 0) # close the whole widget, this should delete everything from the QApplication widget.close() QApplication.processEvents() self.assert_number_of_widgets_matching(".interpreter.PythonFileInterpreter", 0) self.assert_no_toplevel_widgets()
class MultiFileEditor(PluginWidget): """ Provides the container for the widget containing the CodeEditors in the Workbench """ def __init__(self, parent): super(MultiFileEditor, self).__init__(parent) # layout self.editors = MultiPythonFileInterpreter(font=text_font(), default_content=DEFAULT_CONTENT, parent=self) layout = QVBoxLayout() layout.addWidget(self.editors) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.setAcceptDrops(True) # attributes self.tabs_open_on_closing = None def load_settings_from_config(self, config): self.editors.load_settings_from_config(config) def execute_current_async(self): """ Executes the selection in the currently active code editor. This is used by MainWindow to execute a file after opening it. """ return self.editors.execute_current_async() def execute_async(self): """ Executes _everything_ in currently active code editor. :return: """ return self.editors.execute_async() def restore_session_tabs(self, session_tabs): self.open_files_in_new_tabs(session_tabs, startup=True) self.editors.close_tab(0) # close default empty tab # ----------- Plugin API -------------------- def app_closing(self): """ Tries to close all editors :return: True if editors can be closed, false if cancelled """ self.tabs_open_on_closing = self.editors.tab_filepaths return self.editors.close_all() def dragEnterEvent(self, event): data = event.mimeData() if data.hasText() and data.hasUrls(): filepaths = [url.toLocalFile() for url in data.urls()] for filepath in filepaths: if osp.splitext(filepath)[1] in ACCEPTED_FILE_EXTENSIONS: event.acceptProposedAction() def dropEvent(self, event): data = event.mimeData() for url in data.urls(): filepath = url.toLocalFile() if osp.splitext(filepath)[1] in ACCEPTED_FILE_EXTENSIONS: try: self.open_file_in_new_tab(filepath) except IOError as io_error: logger.warning("Could not load file:\n '{}'" "".format(io_error)) def get_plugin_title(self): return "Editor" def readSettings(self, settings): try: prev_session_tabs = settings.get(TAB_SETTINGS_KEY) except KeyError: return self.restore_session_tabs(prev_session_tabs) def writeSettings(self, settings): settings.set(TAB_SETTINGS_KEY, set(self.tabs_open_on_closing)) def register_plugin(self): self.main.add_dockwidget(self) # ----------- Plugin Behaviour -------------------- def open_file_in_new_tab(self, filepath, startup=False): return self.editors.open_file_in_new_tab(filepath, startup) def open_files_in_new_tabs(self, filepaths, startup=False): for filepath in filepaths: try: self.open_file_in_new_tab(filepath, startup) except IOError as io_error: logger.warning("Could not load file:\n {}" "".format(io_error)) def save_current_file(self): self.editors.save_current_file() def save_current_file_as(self): self.editors.save_current_file_as()
class MultiFileEditor(PluginWidget): """Provides a tab widget for editing multiple files""" def __init__(self, parent): super(MultiFileEditor, self).__init__(parent) # layout self.editors = MultiPythonFileInterpreter( default_content=DEFAULT_CONTENT, parent=self) layout = QVBoxLayout() layout.addWidget(self.editors) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.setAcceptDrops(True) # attributes self.tabs_open_on_closing = None self.run_action = create_action( self, "Run", on_triggered=self.editors.execute_current, shortcut=("Ctrl+Return", "Ctrl+Enter"), shortcut_context=Qt.ApplicationShortcut) self.abort_action = create_action( self, "Abort", on_triggered=self.editors.abort_current) # menu action to toggle the find/replace dialog self.toggle_find_replace = create_action( self, 'Find/Replace...', on_triggered=self.editors.toggle_find_replace_dialog, shortcut='Ctrl+F') self.toggle_comment_action = create_action( self.editors.current_editor(), "Comment/Uncomment", on_triggered=self.editors.toggle_comment_current, shortcut="Ctrl+/", shortcut_context=Qt.ApplicationShortcut) self.tabs_to_spaces_action = create_action( self, 'Tabs to Spaces', on_triggered=self.editors.tabs_to_spaces_current) self.spaces_to_tabs_action = create_action( self, 'Spaces to Tabs', on_triggered=self.editors.spaces_to_tabs_current) self.toggle_whitespace_action = create_action( self, 'Toggle Whitespace Visible', on_triggered=self.editors.toggle_whitespace_visible_all) # Store actions for adding to menu bar; None will add a separator self.editor_actions = [ self.run_action, self.abort_action, None, self.toggle_find_replace, None, self.toggle_comment_action, self.toggle_whitespace_action, None, self.tabs_to_spaces_action, self.spaces_to_tabs_action, None ] def execute_current(self): '''This is used by MainWindow to execute a file after opening it''' return self.editors.execute_current() def restore_session_tabs(self, session_tabs): self.open_files_in_new_tabs(session_tabs, startup=True) self.editors.close_tab(0) # close default empty tab # ----------- Plugin API -------------------- def app_closing(self): """ Tries to close all editors :return: True if editors can be closed, false if cancelled """ self.tabs_open_on_closing = self.editors.tab_filepaths return self.editors.close_all() def dragEnterEvent(self, event): data = event.mimeData() if data.hasText() and data.hasUrls(): filepaths = [url.toLocalFile() for url in data.urls()] for filepath in filepaths: if osp.splitext(filepath)[1] in ACCEPTED_FILE_EXTENSIONS: event.acceptProposedAction() def dropEvent(self, event): data = event.mimeData() for url in data.urls(): filepath = url.toLocalFile() if osp.splitext(filepath)[1] in ACCEPTED_FILE_EXTENSIONS: try: self.open_file_in_new_tab(filepath) except IOError as io_error: logger.warning("Could not load file:\n '{}'" "".format(io_error)) def get_plugin_title(self): return "Editor" def readSettings(self, settings): try: prev_session_tabs = settings.get(TAB_SETTINGS_KEY) except KeyError: return self.restore_session_tabs(prev_session_tabs) def writeSettings(self, settings): settings.set(TAB_SETTINGS_KEY, self.tabs_open_on_closing) def register_plugin(self): self.main.add_dockwidget(self) # menus add_actions(self.main.editor_menu, self.editor_actions) # ----------- Plugin Behaviour -------------------- def open_file_in_new_tab(self, filepath, startup=False): return self.editors.open_file_in_new_tab(filepath, startup) def open_files_in_new_tabs(self, filepaths, startup=False): for filepath in filepaths: try: self.open_file_in_new_tab(filepath, startup) except IOError as io_error: logger.warning("Could not load file:\n {}" "".format(io_error)) def save_current_file(self): self.editors.save_current_file()