diff --git a/MiAZ/backend/pluginsystem.py b/MiAZ/backend/pluginsystem.py index 9a69cbdc..6eb1f3d7 100644 --- a/MiAZ/backend/pluginsystem.py +++ b/MiAZ/backend/pluginsystem.py @@ -43,6 +43,7 @@ class MiAZPluginType(IntEnum): class MiAZPluginManager(GObject.GObject): def __init__(self, app): + super().__init__() GObject.signal_new('plugins-updated', MiAZPluginManager, GObject.SignalFlags.RUN_LAST, None, () ) @@ -83,9 +84,27 @@ def import_plugin(self, plugin_path): ENV = self.app.get_env() azip.extractall(ENV['LPATH']['PLUGINS']) self.engine.rescan_plugins() + config = self.app.get_config('Plugin') + config.add_available(key=fn1_name) self.log.debug("Plugin '%s' added to '%s'", os.path.basename(plugin_path), ENV['LPATH']['PLUGINS']) + # ~ self.emit('plugins-updated') return valid + def remove_plugin(self, plugin: Peas.PluginInfo): + """Remove plugin for user space plugins""" + # FIXME: Make sure the plugin is deleted and unloaded + ENV = self.app.get_env() + module = plugin.get_module_name() + self.unload_plugin(plugin) + plugin_head = os.path.join(ENV['LPATH']['PLUGINS'], '%s.plugin' % module) + plugin_body = os.path.join(ENV['LPATH']['PLUGINS'], '%s.py' % module) + os.unlink(plugin_head) + os.unlink(plugin_body) + config = self.app.get_config('Plugin') + config.remove_available(key=module) + # ~ self.emit('plugins-updated') + return True + def rescan_plugins(self): try: self.engine.rescan_plugins() @@ -111,7 +130,11 @@ def load_plugin(self, plugin: Peas.PluginInfo) -> bool: return False def unload_plugin(self, plugin: Peas.PluginInfo): - self.engine.unload_plugin(plugin) + try: + self.engine.unload_plugin(plugin) + self.log.debug("Plugin unloaded") + except Exception as error: + self.log.error(error) def get_engine(self): return self.engine @@ -131,7 +154,7 @@ def get_plugin_type(self, plugin_info): else: return MiAZPluginType.SYSTEM - def get_extension(self, module_name): + def get_extension(self, module_name: str): """Gets the extension identified by the specified name. Args: module_name (str): The name of the extension. @@ -144,7 +167,7 @@ def get_extension(self, module_name): return self.extension_set.get_extension(plugin) - def get_plugin_info(self, module_name): + def get_plugin_info(self, module_name: str): """Gets the plugin info for the specified plugin name. Args: module_name (str): The name from the .plugin file of the module. @@ -171,32 +194,17 @@ def _setup_extension_set(self): def _setup_plugins_dir(self): # System plugins + # Mandatory set of plugins for every repository ENV = self.app.get_env() if os.path.exists(ENV['GPATH']['PLUGINS']): self.engine.add_search_path(ENV['GPATH']['PLUGINS']) self.log.debug("Added System plugin dir: %s", ENV['GPATH']['PLUGINS']) # GLobal user plugins + # All user space plugins are available for all repositories + # However, each repository can use none, any or all of them self.add_user_plugins_dir() - # ~ # User plugins for a specific repo - # ~ self.add_repo_plugins_dir() - - # ~ def add_repo_plugins_dir(self): - # ~ try: - # ~ repo = self.backend.repo_config() - # ~ dir_conf = repo['dir_conf'] - # ~ dir_plugins = os.path.join(dir_conf, 'plugins') - # ~ dir_plugins_available = os.path.join(dir_plugins, 'available') - # ~ dir_plugins_used = os.path.join(dir_plugins, 'used') - # ~ os.makedirs(dir_plugins, exist_ok=True) - # ~ os.makedirs(dir_plugins_available, exist_ok=True) - # ~ os.makedirs(dir_plugins_used, exist_ok=True) - # ~ self.engine.add_search_path(dir_plugins_used) - # ~ self.log.debug("Added User plugin dir: %s", dir_plugins_used) - # ~ except KeyError: - # ~ self.log.warning("There isn't any repo loaded right now!") - def add_user_plugins_dir(self): ENV = self.app.get_env() os.makedirs(ENV['LPATH']['PLUGINS'], exist_ok=True) diff --git a/MiAZ/frontend/desktop/app.py b/MiAZ/frontend/desktop/app.py index 1d443bcd..64ea15e4 100644 --- a/MiAZ/frontend/desktop/app.py +++ b/MiAZ/frontend/desktop/app.py @@ -573,4 +573,4 @@ def remove_widget(self, name: str): def statusbar_message(self, message: str): """Statusbar message""" statusbar = self.get_widget('statusbar') - statusbar.statusbar_message(message) + statusbar.message(message) diff --git a/MiAZ/frontend/desktop/widgets/configview.py b/MiAZ/frontend/desktop/widgets/configview.py index 1171b3a2..c5e70a9a 100644 --- a/MiAZ/frontend/desktop/widgets/configview.py +++ b/MiAZ/frontend/desktop/widgets/configview.py @@ -304,6 +304,11 @@ def __init__(self, app): super().__init__(app, 'Plugin') self._update_view_available() + def plugins_updated(self, *args): + # ~ self._update_view_used() + self._update_view_available() + self.log.debug("Selector plugin views updated") + def _setup_view_finish(self): # Setup Available and Used Column Views self.viewAv = MiAZColumnViewPlugin(self.app) @@ -311,25 +316,17 @@ def _setup_view_finish(self): self.viewSl = MiAZColumnViewPlugin(self.app) self.add_columnview_used(self.viewSl) - def _update_view_available(self): - plugin_manager = self.app.get_service('plugin-manager') - items = [] - item_type = self.config.model - for plugin in plugin_manager.plugins: - ptype = plugin_manager.get_plugin_type(plugin) - if ptype == MiAZPluginType.USER: - pid = plugin.get_module_name() - title = plugin.get_description() #+ ' (v%s)' % plugin.get_version() - items.append(item_type(id=pid, title=title)) - self.viewAv.update(items) - - # ~ def _update_view_used(self): + # ~ def _update_view_available(self): + # ~ plugin_manager = self.app.get_service('plugin-manager') # ~ items = [] # ~ item_type = self.config.model - # ~ countries = self.config.load_used() - # ~ for code in countries: - # ~ items.append(item_type(id=code, title=countries[code], icon='%s.svg' % code)) - # ~ self.viewSl.update(items) + # ~ for plugin in plugin_manager.plugins: + # ~ ptype = plugin_manager.get_plugin_type(plugin) + # ~ if ptype == MiAZPluginType.USER: + # ~ pid = plugin.get_module_name() + # ~ title = plugin.get_description() #+ ' (v%s)' % plugin.get_version() + # ~ items.append(item_type(id=pid, title=title)) + # ~ self.viewAv.update(items) def _on_item_used_remove(self, *args): plugin_manager = self.app.get_service('plugin-manager') diff --git a/MiAZ/frontend/desktop/widgets/settings.py b/MiAZ/frontend/desktop/widgets/settings.py index 9cb0585e..4670c5d5 100644 --- a/MiAZ/frontend/desktop/widgets/settings.py +++ b/MiAZ/frontend/desktop/widgets/settings.py @@ -91,9 +91,7 @@ def _create_widget_for_repositories(self): # Load last active repo repos_used = self.config['Repository'].load_used() - self.log.debug("Repositories in use: %s", ','.join(repos_used.keys())) repo_active = self.config['App'].get('current') - self.log.debug("Current active: %s", repo_active) if repo_active in repos_used: model = self.dd_repo.get_model() n = 0 @@ -162,39 +160,39 @@ def _create_widget_for_plugins(self): notebook = Gtk.Notebook() notebook.set_show_border(False) notebook.set_tab_pos(Gtk.PositionType.LEFT) - widget = self._create_widget_for_system_plugins() + widget = self._create_view_plugins_system() label = self.factory.create_notebook_label(icon_name='miaz-app-settings', title='System') notebook.append_page(widget, label) - widget = self._create_widget_for_user_plugins() + widget = self._create_view_plugins_user() label = self.factory.create_notebook_label(icon_name='miaz-res-people', title='User') notebook.append_page(widget, label) vbox.append(notebook) return vbox - def _create_widget_for_system_plugins(self): - from MiAZ.backend.pluginsystem import MiAZPluginType + def _create_view_plugins_system(self): vbox = self.factory.create_box_vertical(margin=0, spacing=0, hexpand=True, vexpand=True) scrwin = self.factory.create_scrolledwindow() + self.app.add_widget('app-settings-plugins-system-scrwin', scrwin) vbox.append(scrwin) pm = self.app.get_service('plugin-manager') - # ~ pm.add_repo_plugins_dir() + view = MiAZColumnViewPlugin(self.app) + view.set_hexpand(True) + view.set_vexpand(True) + self.app.add_widget('app-settings-plugins-system-view', view) + scrwin.set_child(view) - box = Gtk.ListBox.new() - box.set_vexpand(True) - scrwin.set_child(box) + # System Plugins + items = [] + item_type = Plugin for plugin in pm.plugins: if pm.get_plugin_type(plugin) == MiAZPluginType.SYSTEM: - title = "%s" % plugin.get_name() - subtitle = plugin.get_description() + ' (v%s)' % plugin.get_version() - active = plugin.is_loaded() - row = self.factory.create_actionrow(title=title, subtitle=subtitle) - box.append(row) + pid = plugin.get_module_name() + title = plugin.get_description() + items.append(item_type(id=pid, title=title)) + view.update(items) return vbox - def _create_widget_for_user_plugins(self): - # Trick to remove widgets from listbox in Gtk 4.8.3 (Debian 11) - # as the method remove_all is not avaiable only since 4.12 :( - self.listboxwidgets = [] + def _create_view_plugins_user(self): vbox = self.factory.create_box_vertical(margin=0, spacing=0, hexpand=True, vexpand=True) # Add/Remove @@ -204,12 +202,11 @@ def _create_widget_for_user_plugins(self): hbox.append(self.factory.create_button(icon_name='miaz-list-remove', title='Remove plugin', callback=self._on_plugin_remove)) vbox.append(hbox) - # Plugins + # User Plugins scrwin = self.factory.create_scrolledwindow() self.app.add_widget('app-settings-plugins-user-scrwin', scrwin) vbox.append(scrwin) pm = self.app.get_service('plugin-manager') - # ~ pm.add_repo_plugins_dir() view = MiAZColumnViewPlugin(self.app) view.set_hexpand(True) view.set_vexpand(True) @@ -231,21 +228,10 @@ def update_user_plugins(self): pid = plugin.get_module_name() plugin_path = os.path.join(ENV['LPATH']['PLUGINS'], '%s.plugin' % pid) if os.path.exists(plugin_path): - title = plugin.get_description() #+ ' (v%s)' % plugin.get_version() + title = plugin.get_description() items.append(item_type(id=pid, title=title)) - self.log.debug("Updating with plugin '%s'", pid) view.update(items) - # ~ # Update listbox - # ~ for plugin in pm.plugins: - # ~ if pm.get_plugin_type(plugin) == MiAZPluginType.USER: - # ~ title = "%s" % plugin.get_name() - # ~ subtitle = plugin.get_description() + ' (v%s)' % plugin.get_version() - # ~ active = plugin.is_loaded() - # ~ row = self.factory.create_actionrow(title=title, subtitle=subtitle) - # ~ listbox.append(row) - # ~ self.listboxwidgets.append(row) - def on_filechooser_response(self, dialog, response, data): if response == Gtk.ResponseType.ACCEPT: plugin_manager = self.app.get_service('plugin-manager') @@ -277,19 +263,15 @@ def _on_plugin_add(self, *args): filechooser_dialog.show() def _on_plugin_remove(self, *args): - ENV = self.app.get_env() plugin_manager = self.app.get_service('plugin-manager') view = self.app.get_widget('app-settings-plugins-user-view') module = view.get_selected_items()[0] plugin = plugin_manager.get_plugin_info(module.id) - plugin_manager.unload_plugin(plugin) - plugin_head = os.path.join(ENV['LPATH']['PLUGINS'], '%s.plugin' % module.id) - plugin_body = os.path.join(ENV['LPATH']['PLUGINS'], '%s.py' % module.id) - self.util.filename_delete(plugin_head) - self.util.filename_delete(plugin_body) - self.log.debug("Plugin '%s' deleted", module.id) - self.app.message("Plugin '%s' deleted" % module.id) - self.update_user_plugins() + deleted = plugin_manager.remove_plugin(plugin) + if deleted: + self.log.debug("Plugin '%s' deleted", module.id) + self.app.statusbar_message("Plugin '%s' deleted" % module.id) + self.update_user_plugins() def get_plugin_status(self, name: str) -> bool: plugins = self.config['App'].get('plugins') @@ -358,3 +340,4 @@ def create_tab(item_type): page, label = create_tab(item_type) self.notebook.append_page(page, label) + diff --git a/MiAZ/frontend/desktop/widgets/window.py b/MiAZ/frontend/desktop/widgets/window.py index 9b424bde..a661cdda 100644 --- a/MiAZ/frontend/desktop/widgets/window.py +++ b/MiAZ/frontend/desktop/widgets/window.py @@ -21,7 +21,8 @@ def __init__(self, app, name, title, **kwargs): self.name = name self.title = title self.set_title(title) - self.log = get_logger(name) + logname = "Miaz.%s" % name.replace('-', '.').title() + self.log = get_logger(logname) self.app.add_widget('window-%s' % name, self) self.connect('close-request', self._on_window_close_request) evk = Gtk.EventControllerKey.new() diff --git a/MiAZ/frontend/desktop/widgets/workspace.py b/MiAZ/frontend/desktop/widgets/workspace.py index 7eaba741..51c16e70 100644 --- a/MiAZ/frontend/desktop/widgets/workspace.py +++ b/MiAZ/frontend/desktop/widgets/workspace.py @@ -226,7 +226,7 @@ def _on_selection_changed(self, selection, position, n_items): docs = self.util.get_files() self.log.debug(', '.join([item.id for item in self.selected_items])) label.set_markup("%d / %d / %d" % (len(self.selected_items), len(model), len(docs))) - self.app.message("Selected %d of %d documents in current view (total documents: %d)" % (len(self.selected_items), len(model), len(docs))) + self.app.statusbar_message("Selected %d of %d documents in current view (total documents: %d)" % (len(self.selected_items), len(model), len(docs))) # ~ if len(self.selected_items) == 1: # ~ menu = self.app.get_widget('workspace-menu-single') # ~ self.popDocsSel.set_menu_model(menu) diff --git a/data/examples/user_plugins/hello.plugin b/data/examples/user_plugins/hello.plugin deleted file mode 100644 index e6008e18..00000000 --- a/data/examples/user_plugins/hello.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Plugin] -Module=hello -Name=MiAZHelloItem -Loader=Python3 -Description=Hello World plugin example -Authors=Tomás Vírseda -Copyright=Copyright © 2023 Tomás Vírseda -Website=http://github.com/t00m/MiAZ -Help=http://github.com/t00m/MiAZ/README.adoc -Version=0.1 diff --git a/data/examples/user_plugins/hello.py b/data/examples/user_plugins/hello.py deleted file mode 100644 index bf13c50e..00000000 --- a/data/examples/user_plugins/hello.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- - -""" -# File: hello.py -# Author: Tomás Vírseda -# License: GPL v3 -# Description: Example of MiAZuser plugin -""" - -import tempfile -from gettext import gettext as _ - -from gi.repository import GObject -from gi.repository import Peas - -from MiAZ.backend.log import get_logger -from MiAZ.frontend.desktop.widgets.dialogs import CustomDialog - - -class MiAZToolbarHelloItemPlugin(GObject.GObject, Peas.Activatable): - __gtype_name__ = 'MiAZToolbarHelloItemPlugin' - object = GObject.Property(type=GObject.Object) - - def __init__(self): - self.log = get_logger('Plugin.HelloItem') - - def do_activate(self): - API = self.object - self.app = API.app - self.factory = self.app.get_service('factory') - view = self.app.get_widget('workspace-view') - selection = view.get_selection() - self.add_toolbar_button() - - def do_deactivate(self): - # Remove button - toolbar = self.app.get_widget('headerbar-right-box') - button = self.app.get_widget('toolbar-top-button-hello') - toolbar.remove(button) - self.app.remove_widget('toolbar-top-button-hello') - - def add_toolbar_button(self, *args): - button = self.app.get_widget('toolbar-top-button-hello') - if button is None: - toolbar_top_right = self.app.get_widget('headerbar-right-box') - button = self.factory.create_button(icon_name='help-faq', callback=self.callback) - button.set_visible(True) - self.app.add_widget('toolbar-top-button-hello', button) - toolbar_top_right.append(button) - else: - button.set_visible(True) - - def callback(self, *args): - window = self.app.get_widget('window') - dtype = 'info' - title = "Hello World!" - text = 'This an example' - dialog = CustomDialog(app=self.app, parent=window, use_header_bar=True, dtype=dtype, title=title, text=text) - dialog.set_modal(True) - dialog.show() diff --git a/data/examples/user_plugins/noupdate.zip b/data/examples/user_plugins/noupdate.zip new file mode 100644 index 00000000..4d199d81 Binary files /dev/null and b/data/examples/user_plugins/noupdate.zip differ diff --git a/data/examples/user_plugins/scan.plugin b/data/examples/user_plugins/scan.plugin deleted file mode 100644 index 104e3f60..00000000 --- a/data/examples/user_plugins/scan.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Plugin] -Module=scan -Name=MiAZImportFromScan -Loader=Python3 -Description=Open scanner application -Authors=Tomás Vírseda -Copyright=Copyright © 2023 Tomás Vírseda -Website=http://github.com/t00m/MiAZ -Help=http://github.com/t00m/MiAZ/README.adoc -Version=0.1 diff --git a/data/examples/user_plugins/scan.py b/data/examples/user_plugins/scan.py deleted file mode 100644 index b05b0512..00000000 --- a/data/examples/user_plugins/scan.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- - -""" -# File: hello.py -# Author: Tomás Vírseda -# License: GPL v3 -# Description: Scan plugin -""" - -import os -import re -import glob -import tempfile -from gettext import gettext as _ - -from gi.repository import Gio -from gi.repository import GObject -from gi.repository import Peas - -from MiAZ.backend.log import get_logger -from MiAZ.frontend.desktop.widgets.dialogs import CustomDialog - - -class MiAZImportFromScanPlugin(GObject.GObject, Peas.Activatable): - __gtype_name__ = 'MiAZImportFromScanPlugin' - object = GObject.Property(type=GObject.Object) - - def __init__(self): - self.log = get_logger('Plugin.Scan') - self._search_scan_app() - - def _search_scan_app(self): - desktop_files = glob.glob('/usr/share/applications/*.desktop') - DAI = Gio.DesktopAppInfo() - self.scanapp = None - for desktop_path in desktop_files: - desktop_name = os.path.basename(desktop_path) - try: - appinfo = DAI.new(desktop_name) - if re.search('scan', appinfo.get_categories(), re.IGNORECASE): - self.scanapp = appinfo - break; - except TypeError as error: - pass - - # ~ if found is not None: - # ~ name=appinfo.get_display_name() - # ~ self.icon_name=appinfo.get_string("Icon") - # ~ description=appinfo.get_description() or appinfo.get_generic_name() or "" - # ~ keywords=appinfo.get_keywords() - # ~ app_id=appinfo.get_id() - # ~ executable=os.path.basename(appinfo.get_string("TryExec") or appinfo.get_executable() or "") - # ~ self.log.info("Scan app: %s (%s) -> %s", name, executable, description) - # ~ self.log.info(appinfo.list_actions()) - # ~ appinfo.launch() - # ~ else: - # ~ self.icon_name = '' - - - def do_activate(self): - API = self.object - self.app = API.app - self.factory = self.app.get_service('factory') - button = self.app.get_widget('miaz-import-button-popover') - if self.scanapp is not None: - self.icon_name=self.scanapp.get_string("Icon") - btnImportFromScan = self.factory.create_button(icon_name=self.icon_name, callback=self.callback) - self.rowImportScan = self.factory.create_actionrow(title=_('Scan document'), subtitle=_('Open the scan application'), suffix=btnImportFromScan) - else: - self.rowImportScan = self.factory.create_actionrow(title=_('No scanner app found'), subtitle=_('FInd and install a scanner app from the Software catalog')) - button.add_widget(self.rowImportScan) - self.log.debug("Plugin Scan activated") - - def do_deactivate(self): - # Remove button - button = self.app.get_widget('miaz-import-button-popover') - button.remove_widget(self.rowImportScan) - - def add_toolbar_button(self, *args): - button = self.app.get_widget('toolbar-top-button-hello') - if button is None: - toolbar_top_right = self.app.get_widget('headerbar-right-box') - button = self.factory.create_button(icon_name='help-faq', callback=self.callback) - button.set_visible(True) - self.app.add_widget('toolbar-top-button-hello', button) - toolbar_top_right.append(button) - else: - button.set_visible(True) - - def callback(self, *args): - if self.scanapp is not None: - self.scanapp.launch() diff --git a/data/examples/user_plugins/scan.zip b/data/examples/user_plugins/scan.zip new file mode 100644 index 00000000..6829ee0b Binary files /dev/null and b/data/examples/user_plugins/scan.zip differ diff --git a/data/resources/plugins/scan.plugin b/data/resources/plugins/scan.plugin deleted file mode 100644 index 104e3f60..00000000 --- a/data/resources/plugins/scan.plugin +++ /dev/null @@ -1,10 +0,0 @@ -[Plugin] -Module=scan -Name=MiAZImportFromScan -Loader=Python3 -Description=Open scanner application -Authors=Tomás Vírseda -Copyright=Copyright © 2023 Tomás Vírseda -Website=http://github.com/t00m/MiAZ -Help=http://github.com/t00m/MiAZ/README.adoc -Version=0.1 diff --git a/data/resources/plugins/scan.py b/data/resources/plugins/scan.py deleted file mode 100644 index 28ae7a84..00000000 --- a/data/resources/plugins/scan.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- - -""" -# File: hello.py -# Author: Tomás Vírseda -# License: GPL v3 -# Description: Scan plugin -""" - -import os -import re -import glob -import tempfile -from gettext import gettext as _ - -from gi.repository import Gio -from gi.repository import GObject -from gi.repository import Peas - -from MiAZ.backend.log import get_logger -from MiAZ.frontend.desktop.widgets.dialogs import CustomDialog - - -class MiAZImportFromScanPlugin(GObject.GObject, Peas.Activatable): - __gtype_name__ = 'MiAZImportFromScanPlugin' - object = GObject.Property(type=GObject.Object) - - def __init__(self): - self.log = get_logger('Plugin.Scan') - self._search_scan_app() - - def _search_scan_app(self): - desktop_files = glob.glob('/usr/share/applications/*.desktop') - DAI = Gio.DesktopAppInfo() - self.scanapp = None - for desktop_path in desktop_files: - desktop_name = os.path.basename(desktop_path) - try: - appinfo = DAI.new(desktop_name) - if re.search('scan', appinfo.get_categories(), re.IGNORECASE): - self.scanapp = appinfo - break; - except TypeError as error: - pass - - # ~ if found is not None: - # ~ name=appinfo.get_display_name() - # ~ self.icon_name=appinfo.get_string("Icon") - # ~ description=appinfo.get_description() or appinfo.get_generic_name() or "" - # ~ keywords=appinfo.get_keywords() - # ~ app_id=appinfo.get_id() - # ~ executable=os.path.basename(appinfo.get_string("TryExec") or appinfo.get_executable() or "") - # ~ self.log.info("Scan app: %s (%s) -> %s", name, executable, description) - # ~ self.log.info(appinfo.list_actions()) - # ~ appinfo.launch() - # ~ else: - # ~ self.icon_name = '' - - - def do_activate(self): - API = self.object - self.app = API.app - self.factory = self.app.get_service('factory') - button = self.app.get_widget('miaz-import-button-popover') - if self.scanapp is not None: - self.icon_name=self.scanapp.get_string("Icon") - btnImportFromScan = self.factory.create_button(icon_name=self.icon_name, callback=self.callback) - self.rowImportScan = self.factory.create_actionrow(title=_('Scan document'), subtitle=_('Open the scan application'), suffix=btnImportFromScan) - else: - self.rowImportScan = self.factory.create_actionrow(title=_('No scanner app found'), subtitle=_('FInd and install a scanner app from the Software catalog')) - button.add_widget(self.rowImportScan) - self.log.debug("Plugin Scan activated") - - def do_deactivate(self): - # Remove entry from popover - button = self.app.get_widget('miaz-import-button-popover') - button.remove_widget(self.rowImportScan) - - def callback(self, *args): - if self.scanapp is not None: - self.scanapp.launch()