diff --git a/logFileModel.py b/logFileModel.py index dc97749..be94cfb 100644 --- a/logFileModel.py +++ b/logFileModel.py @@ -85,7 +85,9 @@ class LogFileModel: return "", -1, -1 offset_in_line = byte_offset - line.byte_offset() - current_char = line.line()[line.byte_index_to_char_index(offset_in_line)] + char_index = line.byte_index_to_char_index(offset_in_line) + # todo char_index may be out of range + current_char = line.line()[char_index] if not self._is_word_char(current_char): return current_char, byte_offset, byte_offset + 1 start_in_line = line.byte_index_to_char_index(byte_offset - line.byte_offset()) diff --git a/main.py b/main.py index c530432..de7bad4 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,4 @@ import logging -import os import signal import ctypes @@ -10,174 +9,13 @@ from PyQt6.QtGui import * import sys import constants -import urlutils -from aboutdialog import AboutDialog -from cutesettings import CuteSettings +from raven.pluginregistry import PluginRegistry from ravenui import RavenUI -from settingsstore import SettingsStore -from highlightingdialog import HighlightingDialog -from tabs import Tabs -from urlutils import url_is_file -MAX_LINE_LENGTH = 4096 logging.basicConfig(level=logging.INFO) log = logging.getLogger("main") - - - -class MainWindow(QMainWindow): - def __init__(self, *args, **kwargs): - super(MainWindow, self).__init__(*args, **kwargs) - - self.settings = SettingsStore.load() - self.setWindowTitle(self.tr("RavenLog")) - self._restore_window() - - self.setDockNestingEnabled(True) - self.setAcceptDrops(True) - - self.tabs = Tabs(self.settings) - - self._menu_recent_files = QMenu(self.tr("Open &Recent"), self) - - self.setCentralWidget(self.tabs) - self.status_bar = QStatusBar(self) - self.setStatusBar(self.status_bar) - self.setMenuBar(self.create_menu_bar()) - - def create_menu_bar(self) -> QMenuBar: - menu_bar = QMenuBar() - - menu_bar.addMenu(self.file_menu()) - menu_bar.addMenu(self.settings_menu()) - menu_bar.addMenu(self.help_menu()) - - return menu_bar - - def file_menu(self) -> QMenu: - file_menu = QMenu(self.tr("&File", "name of the file menu"), self) - - open_file = QAction(QIcon.fromTheme("document-open"), self.tr("&Open..."), self) - open_file.setShortcut('Ctrl+O') - open_file.triggered.connect(self._open_file_dialog) - - # Linux: QIcon.fromTheme("exit") - close_action = QAction(QIcon.fromTheme("close"), self.tr("E&xit", "menu item to close the application"), self) - close_action.setShortcut('Ctrl+X') - close_action.triggered.connect(self.destruct) - - self._update_recent_files_menu() - - file_menu.addAction(open_file) - file_menu.addMenu(self._menu_recent_files) - file_menu.addAction(close_action) - return file_menu - - def settings_menu(self) -> QMenu: - result = QMenu(self.tr("&Settings"), self) - manage = QAction(self.tr("&Highlighter"), self) - manage.setShortcut('Ctrl+H') - manage.triggered.connect(lambda: HighlightingDialog(self.settings).exec()) - result.addAction(manage) - - highlight_search_terms = QAction(self.tr("Highlight &Searches"), self) - highlight_search_terms.setCheckable(True) - highlight_search_terms.setChecked(self.settings.session.getboolean("general", "highlight_search_term")) - highlight_search_terms.triggered.connect( - lambda checked: self.settings.set_session("general", "highlight_search_term", str(checked)) or self.update() - ) - result.addAction(highlight_search_terms) - - new_tab = QAction(self.tr("Open Tab on Save As File"), self) - new_tab.setCheckable(True) - new_tab.setChecked(self.settings.session.getboolean("general", "open_tab_on_save_as_file")) - new_tab.triggered.connect( - lambda checked: self.settings.set_session("general", "open_tab_on_save_as_file", str(checked))) - result.addAction(new_tab) - - return result - - def help_menu(self) -> QMenu: - help_menu = QMenu(self.tr("&Help", "name of the help menu"), self) - - about_action = QAction(QIcon(constants.raven_icon), self.tr("&About"), self) - about_action.triggered.connect(lambda: AboutDialog().exec()) - help_menu.addAction(about_action) - - return help_menu - - def _update_recent_files_menu(self): - self._menu_recent_files.clear() - files = self._get_recent_files() - for file in files: - action = QAction(os.path.basename(file), self) - action.triggered.connect(lambda x, f=file: self.open_file(f)) - self._menu_recent_files.addAction(action) - - def _open_file_dialog(self) -> None: - current_file = self.tabs.current_file() - directory = os.path.dirname(current_file) if current_file else '' - - dialog = QFileDialog(self) - (selected_file, _filter) = dialog.getOpenFileName( - caption=self.tr("Open File"), - directory=directory - ) - if selected_file: - self.tabs.create_tab(selected_file) - self._remember_recent_file(selected_file) - - def open_file(self, file: str) -> None: - self.tabs.create_tab(file) - self._remember_recent_file(file) - - def _get_recent_files(self) -> [str]: - recent_files = self.settings.session.get('general', 'recent_files', fallback='') - files = recent_files.split(os.pathsep) - if "" in files: - files.remove("") - return files - - def _remember_recent_file(self, file: str): - files = self._get_recent_files() - if file in files: - files.remove(file) - files.insert(0, file) - recent_files = os.pathsep.join(files[:10]) - self.settings.set_session('general', 'recent_files', recent_files) - self._update_recent_files_menu() - - def dragEnterEvent(self, e: QDragEnterEvent): - if url_is_file(e.mimeData().text()): - e.accept() - else: - e.ignore() - - def dropEvent(self, e): - file = urlutils.url_to_path(e.mimeData().text()) - self.open_file(file) - - def _restore_window(self): - qsettings = CuteSettings() - geometry_restored = False - geometry = qsettings.value("mainWindow/geometry") - if geometry: - geometry_restored = self.restoreGeometry(geometry) - if not geometry_restored: - self.setGeometry(0, 0, 800, 600) - - def closeEvent(self, event): - self.destruct() - - def destruct(self): - self.tabs.destruct() - self.close() - SettingsStore.save(self.settings) - CuteSettings().set_value("mainWindow/geometry", self.saveGeometry()) - - def stop_signal(signum, _stackframe): """ Handle terminate signal """ try: @@ -212,14 +50,20 @@ if __name__ == "__main__": timer.timeout.connect(lambda: None) timer.start(100) - window = MainWindow() + # init plugins + PluginRegistry.load_plugin("RavenLogPlugin") + PluginRegistry.load_plugin("FilterPlugin") + PluginRegistry.load_plugin("LogFileViewerPlugin") + PluginRegistry.load_plugin("OpenFilePlugin") + + window = PluginRegistry.executeSingle("create_main_window") RavenUI.window = window window.show() - #window.open_file("/home/andi/ws/performanceDb/data/production/logs_2018-09-06_2018-09-06.csv") - #window.open_file("/home/andi/ws/performanceDb/data/production/vapbdcom.csv") - #window.open_file("/var/log/syslog") - #window.open_file("/home/andi/ws/ravenlog/example.log") - #window.open_file("C:\\Users\\andi\\ws\\some.log") + # window.open_file("/home/andi/ws/performanceDb/data/production/logs_2018-09-06_2018-09-06.csv") + # window.open_file("/home/andi/ws/performanceDb/data/production/vapbdcom.csv") + # window.open_file("/var/log/syslog") + # window.open_file("/home/andi/ws/ravenlog/example.log") + # window.open_file("C:\\Users\\andi\\ws\\some.log") signal.signal(signal.SIGINT, stop_signal) signal.signal(signal.SIGTERM, stop_signal) diff --git a/raven/mainwindow.py b/raven/mainwindow.py new file mode 100644 index 0000000..eec12ff --- /dev/null +++ b/raven/mainwindow.py @@ -0,0 +1,268 @@ +import logging +import os +import sys +from typing import List, Optional + +from PyQt6.QtWidgets import * +from PyQt6.QtGui import * + +import constants +import urlutils +from aboutdialog import AboutDialog +from cutesettings import CuteSettings +from raven.pluginregistry import PluginRegistry +from raven.plugins.domain.menucontribution import MenuContribution +from raven.plugins.domain.raction import RAction +from raven.plugins.domain.rmenu import RMenu +from settingsstore import SettingsStore +from highlightingdialog import HighlightingDialog +from tabs import Tabs +from urlutils import url_is_file +from functools import reduce + +MAX_LINE_LENGTH = 4096 + +logging.basicConfig(level=logging.INFO) +log = logging.getLogger("main") + + +def flat_map(array: List[List]) -> List: + return reduce(list.__add__, array) + + +def _action_about(): + about_action = RAction( + "&About", + action=lambda: AboutDialog().exec(), + icon_file=constants.raven_icon + ) + return about_action + + +class MainWindow(QMainWindow): + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + + self.settings = SettingsStore.load() + PluginRegistry.execute("set_settings", self.settings) + + PluginRegistry.execute("set_translator", lambda string: self.tr(string)) + + self.setWindowTitle(self.tr("RavenLog")) + self._restore_window() + + self.setDockNestingEnabled(True) + self.setAcceptDrops(True) + + self.tabs = Tabs(self.settings) + + self._menu_recent_files = QMenu(self.tr("Open &Recent"), self) + + self.setCentralWidget(self.tabs) + self.status_bar = QStatusBar(self) + self.setStatusBar(self.status_bar) + self.setMenuBar(self.create_dynamic_menu_bar()) + + def create_dynamic_menu_bar(self) -> QMenuBar: + menu_bar = QMenuBar() + + menu_contributions: [MenuContribution] = flat_map(PluginRegistry.execute("get_menu_contributions")) + menu_contributions.append(MenuContribution("file", action=self._action_close(), action_id="close app")) + menu_contributions.append(MenuContribution("settings", action=self._action_highlighter())) + menu_contributions.append(MenuContribution("settings", action=self._action_highlight_search_terms())) + menu_contributions.append(MenuContribution("settings", action=self._action_new_tab())) + menu_contributions.append(MenuContribution("help", action=_action_about())) + + known_menus = [ + ("file", self.tr("&File")), + ("settings", self.tr("&Settings")), + ("help", self.tr("&Help")) + ] + + for (menu_id, menu_label) in known_menus: + menu = QMenu(menu_label, self) + mcs: [MenuContribution] = [mc for mc in menu_contributions if mc.menu_id == menu_id] + for menu_contribution in mcs: + print("%s %s" % (menu_id, menu_contribution.action_id)) + if menu_contribution.action: + action = self._raction_to_qaction(menu_contribution.action, menu) + menu.addAction(action) + if menu_contribution.menu: + submenu = QMenu(self.tr(menu_contribution.menu.label), menu_bar) + menu_contribution.menu.add_change_listener( + lambda qmenu=submenu, rmenu=menu_contribution.menu: self._rmenu_update(qmenu, rmenu)) + self._rmenu_update(submenu, menu_contribution.menu) + menu.addMenu(submenu) + + menu_bar.addMenu(menu) + + return menu_bar + + def _rmenu_update(self, qmenu: QMenu, rmenu: RMenu): + qmenu.clear() + for action in rmenu.actions: + action = self._raction_to_qaction(action, qmenu) + qmenu.addAction(action) + + def _raction_to_qaction(self, raction: RAction, qmenu: QMenu) -> QAction: + action = QAction(self.tr(raction.label), qmenu) + if raction.icon_from_theme: + action.setIcon(QIcon.fromTheme(raction.icon_from_theme)) + if raction.icon_file: + action.setIcon(QIcon(raction.icon_file)) + if raction.shortcut: + action.setShortcut(raction.shortcut) + if raction.action: + action.triggered.connect(raction.action) + if raction.checkable: + action.setCheckable(raction.checkable) + action.setChecked(raction.checked) + return action + + def _action_open_file(self) -> RAction: + open_file = RAction("&Open...", action=self._open_file_dialog, shortcut='Ctrl+O', + icon_from_theme="document-open") + return open_file + + def _action_close(self) -> RAction: + # Linux: QIcon.fromTheme("exit") + icon = "close" if sys.platform == 'win32' or sys.platform == 'cygwin' else "exit" + close_action = RAction("E&xit", action=self.destruct, shortcut='Ctrl+X', icon_from_theme=icon) + return close_action + + def create_menu_bar(self) -> QMenuBar: + menu_bar = QMenuBar() + + menu_bar.addMenu(self.file_menu()) + menu_bar.addMenu(self.settings_menu()) + menu_bar.addMenu(self.help_menu()) + + return menu_bar + + def file_menu(self) -> QMenu: + file_menu = QMenu(self.tr("&File", "name of the file menu"), self) + + open_file = self._action_open_file() + close_action = self._action_close() + + self._update_recent_files_menu() + + file_menu.addAction(open_file) + file_menu.addMenu(self._menu_recent_files) + file_menu.addAction(close_action) + return file_menu + + def _action_highlighter(self): + manage = RAction( + "&Highlighter", + action=lambda: HighlightingDialog(self.settings).exec(), + shortcut='Ctrl+H' + ) + return manage + + def _action_highlight_search_terms(self): + highlight_search_terms = RAction( + "Highlight &Searches", + action=lambda checked: self.settings.set_session("general", "highlight_search_term", + str(checked)) or self.update() + ) + highlight_search_terms.set_checkable(True) + highlight_search_terms.set_checked(self.settings.session.getboolean("general", "highlight_search_term")) + return highlight_search_terms + + def _action_new_tab(self): + new_tab = RAction("Open Tab on Save As File") + new_tab.set_checkable(True) + new_tab.set_checked(self.settings.session.getboolean("general", "open_tab_on_save_as_file")) + new_tab.set_action( + lambda checked: self.settings.set_session("general", "open_tab_on_save_as_file", str(checked))) + return new_tab + + def settings_menu(self) -> QMenu: + result = QMenu(self.tr("&Settings"), self) + result.addAction(self._action_highlighter()) + result.addAction(self._action_highlight_search_terms()) + result.addAction(self._action_new_tab()) + return result + + def help_menu(self) -> QMenu: + help_menu = QMenu(self.tr("&Help", "name of the help menu"), self) + help_menu.addAction(_action_about()) + return help_menu + + def _update_recent_files_menu(self): + self._menu_recent_files.clear() + files = self._get_recent_files() + for file in files: + action = QAction(os.path.basename(file), self) + action.triggered.connect(lambda x, f=file: self.open_file(f)) + self._menu_recent_files.addAction(action) + + def _open_file_dialog(self) -> None: + current_file = self.tabs.current_file() + directory = os.path.dirname(current_file) if current_file else '' + + dialog = QFileDialog(self) + (selected_file, _filter) = dialog.getOpenFileName( + caption=self.tr("Open File"), + directory=directory + ) + if selected_file: + self.tabs.create_tab(selected_file) + self._remember_recent_file(selected_file) + + def current_file(self) -> Optional[str]: + return self.tabs.current_file() + + def open_file(self, file: str) -> None: + + self.tabs.create_tab(file) + PluginRegistry.execute("after_open_file", file) + # self._remember_recent_file(file) + # can_open_plugins = PluginRegistry.execute("can_open_file", file) + # if len(can_open_plugins) == 1: + # else: + + def _get_recent_files(self) -> [str]: + recent_files = self.settings.session.get('general', 'recent_files', fallback='') + files = recent_files.split(os.pathsep) + if "" in files: + files.remove("") + return files + + # def _remember_recent_file(self, file: str): + # files = self._get_recent_files() + # if file in files: + # files.remove(file) + # files.insert(0, file) + # recent_files = os.pathsep.join(files[:10]) + # self.settings.set_session('general', 'recent_files', recent_files) + # self._update_recent_files_menu() + + def dragEnterEvent(self, e: QDragEnterEvent): + if url_is_file(e.mimeData().text()): + e.accept() + else: + e.ignore() + + def dropEvent(self, e): + file = urlutils.url_to_path(e.mimeData().text()) + self.open_file(file) + + def _restore_window(self): + qsettings = CuteSettings() + geometry_restored = False + geometry = qsettings.value("mainWindow/geometry") + if geometry: + geometry_restored = self.restoreGeometry(geometry) + if not geometry_restored: + self.setGeometry(0, 0, 800, 600) + + def closeEvent(self, event): + self.destruct() + + def destruct(self): + self.tabs.destruct() + self.close() + SettingsStore.save(self.settings) + CuteSettings().set_value("mainWindow/geometry", self.saveGeometry()) diff --git a/raven/pluginregistry.py b/raven/pluginregistry.py index 3acdb31..83e982d 100644 --- a/raven/pluginregistry.py +++ b/raven/pluginregistry.py @@ -15,53 +15,74 @@ class PluginRegistry(): modules: [ModuleType] = [] @staticmethod - def register_plugin(name: str, plugin: PluginBase): + def _register_plugin(name: str, plugin: PluginBase): PluginRegistry.plugins[name] = plugin - @staticmethod - def get_plugins_by_function(function_name: str) -> [PluginBase]: - result = [] - for plugin in PluginRegistry.plugins.values(): - fun = getattr(plugin, function_name, None) - if callable(fun): - result.append(plugin) - return result - - @staticmethod - def load_module(module_name: str) -> ModuleType: - module_name = f"plugins.{module_name}" - module = import_module(module_name) - PluginRegistry.modules.append(module) - return module + # @staticmethod + # def load_module(module_name: str) -> ModuleType: + # module_name = f"raven.plugins.{module_name}" + # module = import_module(module_name) + # PluginRegistry.modules.append(module) + # return module @staticmethod def load_plugin(plugin_name: str) -> PluginBase: - module_name = f"plugins.{plugin_name.lower()}" + module_name = f"raven.plugins.{plugin_name.lower()}" module = import_module(module_name) if plugin_name in dir(module): plugin_class = getattr(module, plugin_name) if isclass(plugin_class) and issubclass(plugin_class, PluginBase): - PluginRegistry.register_plugin(plugin_name, plugin_class()) + PluginRegistry._register_plugin(plugin_name, plugin_class()) return plugin_class raise RuntimeError("plugin %s not found" % plugin_name) - @staticmethod - def get_modules() -> [ModuleType]: - return PluginRegistry.modules.copy() + # @staticmethod + # def get_modules() -> [ModuleType]: + # return PluginRegistry.modules.copy() @staticmethod def get_plugins() -> [PluginBase]: return PluginRegistry.modules.copy() @staticmethod - def execute(function_name: str, *args): + def executeSingle(function_name: str, *args): + return PluginRegistry._execute(function_name, True, *args) + + @staticmethod + def execute(function_name: str, *args) -> []: + return PluginRegistry._execute(function_name, False, *args) + + @staticmethod + def _execute(function_name: str, return_first: bool, *args) -> []: + result = [] for plugin in PluginRegistry.plugins.values(): fun = getattr(plugin, function_name, None) - sig = signature(fun) if callable(fun): + sig = signature(fun) if len(sig.parameters) != len(args): raise RuntimeError("method %s.%s has wrong number of arguments. expected %s but was %s " % ( - plugin, function_name, len(args), len(sig.parameters))) - fun(args) + plugin, function_name, len(args), len(sig.parameters))) + print("calling %s with args %s" % (fun, args)) + if len(args) == 0: + return_value = fun() + elif len(args) == 1: + return_value = fun(args[0]) + elif len(args) == 2: + return_value = fun(args[0], args[1]) + elif len(args) == 3: + return_value = fun(args[0], args[1], args[2]) + elif len(args) == 4: + return_value = fun(args[0], args[1], args[2], args[3]) + elif len(args) == 5: + return_value = fun(args[0], args[1], args[2], args[3], args[4]) + else: + raise Exception("too many arguments") + + if return_first: + return return_value + result.append(return_value) + if return_first: + return None + return result diff --git a/raven/plugins/domain/__init__.py b/raven/plugins/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/raven/plugins/domain/menucontribution.py b/raven/plugins/domain/menucontribution.py new file mode 100644 index 0000000..5954db9 --- /dev/null +++ b/raven/plugins/domain/menucontribution.py @@ -0,0 +1,11 @@ +from raven.plugins.domain.raction import RAction +from raven.plugins.domain.rmenu import RMenu + + +class MenuContribution(): + def __init__(self, menu_id: str, action: RAction = None, menu: RMenu = None, action_id=None): + super(MenuContribution, self).__init__() + self.menu_id = menu_id + self.action = action + self.menu = menu + self.action_id = action_id diff --git a/raven/plugins/domain/raction.py b/raven/plugins/domain/raction.py new file mode 100644 index 0000000..b42edb2 --- /dev/null +++ b/raven/plugins/domain/raction.py @@ -0,0 +1,37 @@ +class RAction(): + + def __init__(self, + label: str, + action=None, + shortcut: str = None, + icon_from_theme: str = None, + icon_file: str = None, + checkable: bool = False, + checked: bool = False + ): + super(RAction, self).__init__() + self.label = label + self.action = action + self.shortcut = shortcut + self.icon_from_theme = icon_from_theme + self.icon_file = icon_file + self.checkable = checkable + self.checked = checked + + def set_action(self, action): + self.action = action + + def set_icon_from_theme(self, icon_from_theme: str): + self.icon_from_theme = icon_from_theme + + def set_icon_file(self, icon_file: str): + self.icon_file = icon_file + + def set_shortcut(self, shortcut: str): + self.shortcut = shortcut + + def set_checkable(self, checkable: bool): + self.checkable = checkable + + def set_checked(self, checked: bool): + self.checked = checked diff --git a/raven/plugins/domain/rmenu.py b/raven/plugins/domain/rmenu.py new file mode 100644 index 0000000..2127651 --- /dev/null +++ b/raven/plugins/domain/rmenu.py @@ -0,0 +1,26 @@ +from typing import Callable + +from raven.plugins.domain.raction import RAction + + +class RMenu(): + def __init__(self, label: str): + super(RMenu, self).__init__() + self.label = label + self.actions = [] + self.listeners = [] + + def add_action(self, action: RAction): + self.actions.append(action) + self._notify() + + def clear(self): + self.actions.clear() + self._notify() + + def _notify(self): + for listener in self.listeners: + listener() + + def add_change_listener(self, listener: Callable[[], None]): + self.listeners.append(listener) diff --git a/raven/plugins/openfileplugin.py b/raven/plugins/openfileplugin.py new file mode 100644 index 0000000..752e113 --- /dev/null +++ b/raven/plugins/openfileplugin.py @@ -0,0 +1,84 @@ +import os +from typing import Callable + +from PyQt6.QtGui import QAction, QIcon +from PyQt6.QtWidgets import QMenu, QFileDialog + +from raven.pluginbase import PluginBase +from raven.pluginregistry import PluginRegistry +from raven.plugins.domain.menucontribution import MenuContribution +from raven.plugins.domain.raction import RAction +from raven.plugins.domain.rmenu import RMenu +from settings import Settings + + +class OpenFilePlugin(PluginBase): + def __init__(self): + super(OpenFilePlugin, self).__init__() + print("init OpenFilePlugin") + + def set_settings(self, settings: Settings): + self.settings = settings + + def set_translator(self, tr: Callable[[str], str]): + self.tr = tr + + def _action_open_file(self) -> RAction: + open_file = RAction(self.tr("&Open..."), self._open_file_dialog, shortcut='Ctrl+O', + icon_from_theme="document-open") + return open_file + + def _sub_menu_recent_files(self) -> RMenu: + self._menu_recent_files = RMenu("Open &Recent") + self._update_recent_files_menu() + return self._menu_recent_files + + def get_menu_contributions(self) -> [MenuContribution]: + return [ + MenuContribution("file", action=self._action_open_file(), action_id="open file"), + MenuContribution("file", menu=self._sub_menu_recent_files(), action_id="recent files menu"), + ] + + def _open_file_dialog(self) -> None: + current_file = PluginRegistry.executeSingle("current_file") + directory = os.path.dirname(current_file) if current_file else '' + + dialog = QFileDialog() + (selected_file, _filter) = dialog.getOpenFileName( + caption=self.tr("Open File"), + directory=directory + ) + if selected_file: + self._open_file(selected_file) + + def _open_file(self, selected_file: str): + PluginRegistry.executeSingle("create_tab", selected_file) + self._remember_recent_file(selected_file) + + def _get_recent_files(self) -> [str]: + recent_files = self.settings.session.get('general', 'recent_files', fallback='') + print(recent_files) + files = recent_files.split(os.pathsep) + if "" in files: + files.remove("") + return files + + def _update_recent_files_menu(self): + self._menu_recent_files.clear() + files = self._get_recent_files() + for file in files: + action = RAction(os.path.basename(file)) + action.set_action(lambda x, f=file: self._open_file(f)) + self._menu_recent_files.add_action(action) + + def _remember_recent_file(self, file: str): + files = self._get_recent_files() + if file in files: + files.remove(file) + files.insert(0, file) + recent_files = os.pathsep.join(files[:10]) + self.settings.set_session('general', 'recent_files', recent_files) + self._update_recent_files_menu() + + def after_open_file(self, file: str): + self._remember_recent_file(file) diff --git a/raven/plugins/ravenlogplugin.py b/raven/plugins/ravenlogplugin.py new file mode 100644 index 0000000..7cefae3 --- /dev/null +++ b/raven/plugins/ravenlogplugin.py @@ -0,0 +1,21 @@ +from typing import Optional + +from raven.mainwindow import MainWindow +from raven.pluginbase import PluginBase + + +class RavenLogPlugin(PluginBase): + def __init__(self): + super(RavenLogPlugin, self).__init__() + self.main_window = None + + def create_main_window(self): + if not self.main_window: + self.main_window = MainWindow() + return self.main_window + + def current_file(self) -> Optional[str]: + return self.main_window.current_file() + + def create_tab(self, file: str): + self.main_window.tabs.create_tab(file) diff --git a/raven/ravenlog.py b/raven/ravenlog.py index 229c93e..d51c1e0 100644 --- a/raven/ravenlog.py +++ b/raven/ravenlog.py @@ -62,8 +62,8 @@ def set_window_icon(app: QApplication): if __name__ == "__main__": - filterplugin = PluginRegistry.load_plugin("FilterPlugin") - logfileviewerplugin = PluginRegistry.load_plugin("LogFileViewerPlugin") + PluginRegistry.load_plugin("FilterPlugin") + PluginRegistry.load_plugin("LogFileViewerPlugin") PluginRegistry.execute("say_hello", "World") diff --git a/testbed/__init__.py b/testbed/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/testbed/docks.py b/testbed/docks.py new file mode 100644 index 0000000..b929422 --- /dev/null +++ b/testbed/docks.py @@ -0,0 +1,32 @@ +import sys + +from PyQt6.QtWidgets import QApplication, QMainWindow, QDockWidget, QLabel, QTextEdit +from PyQt6.QtCore import Qt + + +class DockWindow(QMainWindow): + def __init__(self, *args, **kwargs): + super(DockWindow, self).__init__(*args, **kwargs) + + self.dock_1 = QDockWidget("Dock1", self) + self.dock_1.layout().addWidget(QLabel("dock1")) + + self.dock_2 = QDockWidget("Dock2", self) + self.dock_2.layout().addWidget(QLabel("dock2")) + + self.dock_3 = QDockWidget("Dock3", self) + self.dock_3.layout().addWidget(QLabel("dock3")) + + self.setCentralWidget(QTextEdit()) + self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.dock_1) + self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.dock_2) + self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.dock_3) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + + window = DockWindow() + window.show() + + app.exec()