# window.py # # Copyright 2024 Etersoft # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. # # SPDX-License-Identifier: AGPL-3.0-or-later from gi.repository import Gtk, Adw import gettext gettext.textdomain('eepm-play-gui') _ = gettext.gettext from .appsmanager import ApplicationManager from .command_runner import CommandRunner class LogDialog(Adw.Dialog): def __init__(self, win, **kwargs): super().__init__(**kwargs) self.set_title("Лог выполнения") self.set_can_close(False) self.set_follows_content_size(True) self.win = win dialog_clamp = Adw.Clamp( margin_bottom=12, margin_end=12, margin_top=12, margin_start=12, maximum_size=500 ) textview = Gtk.TextView() textview.set_editable(False) textview.set_cursor_visible(False) textbuffer = textview.get_buffer() scrolled_window = Gtk.ScrolledWindow( width_request=304, height_request=259 ) scrolled_window.set_child(textview) scrolled_window.set_vexpand(True) dialog_clamp.set_child(scrolled_window) self.set_child(dialog_clamp) def run(self, command, on_done): self.present(self.win) # Создание и передача функции обратного вызова для обновления UI runner = CommandRunner(on_done=on_done) runner.run_command(command, self) @Gtk.Template(resource_path='/ru/eepm/PlayGUI/window.ui') class EepmPlayGuiWindow(Adw.ApplicationWindow, LogDialog): __gtype_name__ = 'EepmPlayGuiWindow' search_entry = Gtk.Template.Child() search_dropdown = Gtk.Template.Child() loading_spinner = Gtk.Template.Child() choice_listbox = Gtk.Template.Child() apply_button = Gtk.Template.Child() def __init__(self, **kwargs): super().__init__(**kwargs) self.set_title("EPM PLAY") self.checkboxes = None self.apply_button.connect("activated", self.on_apply_clicked) self.search_entry.connect("search-changed", self.on_search_changed) self.search_dropdown.connect("notify::selected", self.on_filter_changed) self.choice_listbox.set_filter_func(self.listbox_filter_func) self.dialog = LogDialog(win=self) self.update_ui() def show_loading_spinner(self): self.loading_spinner.set_visible(True) self.choice_listbox.set_visible(False) # Скрыть группу настроек во время загрузки def hide_loading_spinner(self): self.loading_spinner.set_visible(False) self.choice_listbox.set_visible(True) def on_applications_loaded(self, applications, error=None): if error: print(f"Error: {error}") else: self.applications = applications def update_ui(self): self.show_loading_spinner() # Show loading again for installed apps self.update_button_status() ApplicationManager.get_available_applications(self.on_applications_loaded) ApplicationManager.get_installed_applications(self.on_installed_apps_loaded) self.choice_listbox.invalidate_filter() def on_installed_apps_loaded(self, installed_apps, error=None): if error: print(f"Error: {error}") else: self.installed_apps = installed_apps self.clear_choice_listbox() self.checkboxes = {} for app in self.applications: self.add_application_row(app) self.hide_loading_spinner() def clear_choice_listbox(self): self.choice_listbox.remove_all() def add_application_row(self, app): row = Adw.ActionRow( title=app['name'], subtitle=app['dscr'] ) checkbox = Gtk.CheckButton() checkbox.add_css_class("selection-mode") checkbox.set_active(app['name'] in self.installed_apps) checkbox.connect("toggled", self.on_checkbox_toggled, app['name']) row.add_suffix(checkbox) self.choice_listbox.append(row) self.checkboxes[app['name']] = checkbox def on_checkbox_toggled(self, checkbox, app_name): active = checkbox.get_active() print(f"{app_name} {'установлен' if active else 'снят'}") self.update_button_status() def update_button_status(self): to_install, to_remove = self.get_install_remove_lists() self.apply_button.remove_css_class("suggested-action") self.apply_button.remove_css_class("destructive-action") if to_install and to_remove: self.apply_button.add_css_class("suggested-action") self.apply_button.set_title(_("Remove and install applications")) elif to_install: self.apply_button.add_css_class("suggested-action") self.apply_button.set_title(_("Install applications")) elif to_remove: self.apply_button.add_css_class("destructive-action") self.apply_button.set_title(_("Remove applications")) else: self.apply_button.add_css_class("suggested-action") self.apply_button.set_title(_("Update applications")) def on_search_changed(self, search_entry): self.choice_listbox.invalidate_filter() # Обновление фильтра при изменении поиска def on_filter_changed(self, dropdown, _pspec): self.choice_listbox.invalidate_filter() # Обновление фильтра при изменении фильтра def listbox_filter_func(self, row): """Функция фильтрации для GtkListBox, которая проверяет текст и состояние фильтра""" search_text = self.search_entry.get_text().lower() filter_option = self.search_dropdown.get_selected() # Получение заголовка и подзаголовка строки title = row.get_title().lower() if row.get_title() else '' subtitle = row.get_subtitle().lower() if row.get_subtitle() else '' # Проверка текста поиска (по имени или описанию) matches_search = search_text in title or search_text in subtitle # Проверка по фильтру: установлено/не установлено в системе app_name = row.get_title() if row.get_title() else '' is_installed = app_name in self.installed_apps if filter_option == 1: # Installed return matches_search and is_installed elif filter_option == 2: # Uninstalled return matches_search and not is_installed else: return matches_search # All def on_apply_clicked(self, button): self.show_loading_spinner() # Показать сообщение о загрузке перед выполнением команды to_install, to_remove = self.get_install_remove_lists() commands = self.build_commands(to_install, to_remove) if commands: full_command = " && ".join(commands) pkexec_command = f'pkexec sh -c "{full_command}"' self.dialog.run(pkexec_command, on_done=self.update_ui) else: self.dialog.run("pkexec epm play --update all", on_done=self.update_ui) def get_install_remove_lists(self): if self.checkboxes: to_install = [app_name for app_name, checkbox in self.checkboxes.items() if checkbox.get_active() and app_name not in self.installed_apps] to_remove = [app_name for app_name, checkbox in self.checkboxes.items() if not checkbox.get_active() and app_name in self.installed_apps] return to_install, to_remove else: return False, False def build_commands(self, to_install, to_remove): commands = [] if to_install: commands.append(f"epm play --auto {' '.join(to_install)}") if to_remove: commands.append(f"epm play --auto --remove {' '.join(to_remove)}") return commands