def get_locale_keys( key: str = None, locale_dir: str = resource.get_path('locale') ) -> Tuple[str, dict]: locale_path = None if key is None: current_locale = locale.getdefaultlocale() else: current_locale = [key.strip().lower()] if current_locale: current_locale = current_locale[0] for locale_file in glob.glob(locale_dir + '/*'): name = locale_file.split('/')[-1] if current_locale == name or current_locale.startswith(name + '_'): locale_path = locale_file break if not locale_path: return current_locale if current_locale else key, {} with open(locale_path, 'r') as f: locale_keys = f.readlines() locale_obj = {} for line in locale_keys: line_strip = line.strip() if line_strip: try: keyval = line_strip.split('=') locale_obj[keyval[0].strip()] = keyval[1].strip() except: print("Error decoding i18n line '{}'".format(line)) return locale_path.split('/')[-1], locale_obj
def __init__(self, managers: List[SoftwareManager], context: ApplicationContext, config: dict, settings_manager: GenericSettingsManager = None): super(GenericSoftwareManager, self).__init__(context=context) self.managers = managers self.map = {t: m for m in self.managers for t in m.get_managed_types()} self._available_cache = {} if config['system'][ 'single_dependency_checking'] else None self.thread_prepare = None self.i18n = context.i18n self.disk_loader_factory = context.disk_loader_factory self.logger = context.logger self._already_prepared = [] self.working_managers = [] self.config = config self.settings_manager = settings_manager self.http_client = context.http_client self.configman = CoreConfigManager() self.extra_actions = [ CustomSoftwareAction(i18n_label_key='action.reset', i18n_status_key='action.reset.status', manager_method='reset', manager=self, icon_path=resource.get_path('img/logo.svg'), requires_root=False, refresh=False) ] self.dynamic_extra_actions = { CustomSoftwareAction(i18n_label_key='action.backups', i18n_status_key='action.backups.status', manager_method='launch_timeshift', manager=self, icon_path='timeshift', requires_root=False, refresh=False): self.is_backups_action_available }
def ask_root_password(i18n: I18n): diag = QInputDialog() diag.setStyleSheet( """QLineEdit { border-radius: 5px; font-size: 16px; border: 1px solid lightblue }""" ) diag.setInputMode(QInputDialog.TextInput) diag.setTextEchoMode(QLineEdit.Password) diag.setWindowIcon(QIcon(resource.get_path('img/lock.png'))) diag.setWindowTitle(i18n['popup.root.title']) diag.setLabelText('') diag.setOkButtonText(i18n['popup.root.continue'].capitalize()) diag.setCancelButtonText(i18n['popup.button.cancel'].capitalize()) diag.resize(400, 200) for attempt in range(3): ok = diag.exec_() if ok: if not validate_password(diag.textValue()): body = i18n['popup.root.bad_password.body'] if attempt == 2: body += '. ' + i18n['popup.root.bad_password.last_try'] show_message(title=i18n['popup.root.bad_password.title'], body=body, type_=MessageType.ERROR) ok = False diag.setTextValue('') if ok: return diag.textValue(), ok else: break return '', False
def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): ti = time.time() self.logger.info("Initializing") taskman = task_manager if task_manager else TaskManager() # empty task manager to prevent null pointers create_config = CreateConfigFile(taskman=taskman, configman=self.configman, i18n=self.i18n, task_icon_path=get_path('img/logo.svg'), logger=self.logger) create_config.start() if self.managers: internet_on = self.context.is_internet_available() prepare_tasks = [] for man in self.managers: if man not in self._already_prepared and self._can_work(man): t = Thread(target=man.prepare, args=(taskman, root_password, internet_on), daemon=True) t.start() prepare_tasks.append(t) self._already_prepared.append(man) for t in prepare_tasks: t.join() tf = time.time() self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti))
def read_default_themes() -> Dict[str, str]: return { f.split('/')[-1].split('.')[0].lower(): f for f in glob.glob(resource.get_path('style/**/*.qss')) }
def __init__(self, window: QWidget, manager: GenericSoftwareManager, i18n: dict, config: Configuration, show_panel_after_restart: bool = False): super(GemSelectorPanel, self).__init__() self.window = window self.manager = manager self.config = config self.setLayout(QGridLayout()) self.setWindowIcon(QIcon(resource.get_path('img/logo.svg'))) self.setWindowTitle(i18n['gem_selector.title']) self.resize(400, 400) self.show_panel_after_restart = show_panel_after_restart self.label_question = QLabel(i18n['gem_selector.question']) self.label_question.setStyleSheet('QLabel { font-weight: bold}') self.layout().addWidget(self.label_question, 0, 1, Qt.AlignHCenter) self.bt_proceed = QPushButton(i18n['change'].capitalize()) self.bt_proceed.setStyleSheet(css.OK_BUTTON) self.bt_proceed.clicked.connect(self.save) self.bt_exit = QPushButton(i18n['exit'].capitalize()) self.bt_exit.clicked.connect(self.exit) self.gem_map = {} gem_options = [] default = set() for m in manager.managers: if m.can_work(): modname = m.__module__.split('.')[-2] op = InputOption( label=i18n.get('gem.{}.label'.format(modname), modname.capitalize()), tooltip=i18n.get('gem.{}.info'.format(modname)), value=modname, icon_path='{r}/gems/{n}/resources/img/{n}.png'.format( r=ROOT_DIR, n=modname)) gem_options.append(op) self.gem_map[modname] = m if m.is_enabled() and m in manager.working_managers: default.add(op) if self.config.enabled_gems: default_ops = { o for o in gem_options if o.value in self.config.enabled_gems } else: default_ops = default self.bt_proceed.setEnabled(bool(default_ops)) self.gem_select_model = MultipleSelectComponent( label='', options=gem_options, default_options=default_ops, max_per_line=3) self.gem_select = MultipleSelectQt(self.gem_select_model, self.check_state) self.layout().addWidget(self.gem_select, 1, 1) self.layout().addWidget(new_spacer(), 2, 1) self.layout().addWidget(self.bt_proceed, 3, 1, Qt.AlignRight) self.layout().addWidget(self.bt_exit, 3, 1, Qt.AlignLeft) self.adjustSize() self.setFixedSize(self.size()) qt_utils.centralize(self)
def new_manage_panel(app_args: Namespace, app_config: dict, logger: logging.Logger) -> Tuple[QApplication, QWidget]: i18n = generate_i18n(app_config, resource.get_path('locale')) cache_cleaner = CacheCleaner() cache_factory = DefaultMemoryCacheFactory(expiration_time=int( app_config['memory_cache']['data_expiration']), cleaner=cache_cleaner) icon_cache = cache_factory.new( int(app_config['memory_cache']['icon_expiration'])) http_client = HttpClient(logger) context = ApplicationContext( i18n=i18n, http_client=http_client, download_icons=bool(app_config['download']['icons']), app_root_dir=ROOT_DIR, cache_factory=cache_factory, disk_loader_factory=DefaultDiskCacheLoaderFactory(logger), logger=logger, distro=util.get_distro(), file_downloader=AdaptableFileDownloader( logger, bool(app_config['download']['multithreaded']), i18n, http_client, app_config['download']['multithreaded_client']), app_name=__app_name__) managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config, default_locale=DEFAULT_I18N_KEY) if app_args.reset: util.clean_app_files(managers) exit(0) manager = GenericSoftwareManager(managers, context=context, config=app_config) app = new_qt_application(app_config, quit_on_last_closed=True) if app_args.settings: # only settings window manager.prepare(None, None, None) # only checks the available managers return app, SettingsWindow(manager=manager, i18n=i18n, screen_size=app.primaryScreen().size(), window=None) else: manage_window = ManageWindow(i18n=i18n, manager=manager, icon_cache=icon_cache, screen_size=app.primaryScreen().size(), config=app_config, context=context, http_client=http_client, icon=util.get_default_icon()[1], logger=logger) prepare = PreparePanel(screen_size=app.primaryScreen().size(), context=context, manager=manager, i18n=i18n, manage_window=manage_window) cache_cleaner.start() return app, prepare
def gen_tip_icon(self, tip: str) -> QLabel: tip_icon = QLabel() tip_icon.setToolTip(tip.strip()) tip_icon.setPixmap( QIcon(resource.get_path('img/about.svg')).pixmap(QSize(12, 12))) return tip_icon
def show_pkg_actions(self, pkg: PackageView): menu_row = QMenu() menu_row.setCursor(QCursor(Qt.PointingHandCursor)) if pkg.model.installed: if pkg.model.has_history(): action_history = QAction(self.i18n["manage_window.apps_table.row.actions.history"]) action_history.setIcon(QIcon(resource.get_path('img/history.svg'))) def show_history(): self.window.get_app_history(pkg) action_history.triggered.connect(show_history) menu_row.addAction(action_history) if pkg.model.can_be_downgraded(): action_downgrade = QAction(self.i18n["manage_window.apps_table.row.actions.downgrade"]) def downgrade(): if dialog.ask_confirmation( title=self.i18n['manage_window.apps_table.row.actions.downgrade'], body=self._parag(self.i18n['manage_window.apps_table.row.actions.downgrade.popup.body'].format(self._bold(str(pkg)))), i18n=self.i18n): self.window.downgrade(pkg) action_downgrade.triggered.connect(downgrade) action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg'))) menu_row.addAction(action_downgrade) if pkg.model.supports_ignored_updates(): if pkg.model.is_update_ignored(): action_ignore_updates = QAction( self.i18n["manage_window.apps_table.row.actions.ignore_updates_reverse"]) action_ignore_updates.setIcon(QIcon(resource.get_path('img/revert_update_ignored.svg'))) else: action_ignore_updates = QAction(self.i18n["manage_window.apps_table.row.actions.ignore_updates"]) action_ignore_updates.setIcon(QIcon(resource.get_path('img/ignore_update.svg'))) def ignore_updates(): self.window.ignore_updates(pkg) action_ignore_updates.triggered.connect(ignore_updates) menu_row.addAction(action_ignore_updates) if bool(pkg.model.get_custom_supported_actions()): for action in pkg.model.get_custom_supported_actions(): item = QAction(self.i18n[action.i18_label_key]) if action.icon_path: item.setIcon(QIcon(action.icon_path)) def custom_action(): if dialog.ask_confirmation( title=self.i18n[action.i18_label_key], body=self._parag('{} {} ?'.format(self.i18n[action.i18_label_key], self._bold(str(pkg)))), i18n=self.i18n): self.window.execute_custom_action(pkg, action) item.triggered.connect(custom_action) menu_row.addAction(item) menu_row.adjustSize() menu_row.popup(QCursor.pos()) menu_row.exec_()
def __init__(self, app_config: dict): super(AboutDialog, self).__init__() i18n = generate_i18n(app_config, resource.get_path('locale/about')) self.setWindowTitle('{} ({})'.format(i18n['about.title'].capitalize(), __app_name__)) layout = QVBoxLayout() self.setLayout(layout) logo_container = QWidget() logo_container.setObjectName('logo_container') logo_container.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) logo_container.setLayout(QHBoxLayout()) label_logo = QLabel() label_logo.setObjectName('logo') logo_container.layout().addWidget(label_logo) layout.addWidget(logo_container) label_name = QLabel(__app_name__) label_name.setObjectName('app_name') layout.addWidget(label_name) label_version = QLabel(i18n['about.version'].lower() + ' ' + __version__) label_version.setObjectName('app_version') layout.addWidget(label_version) layout.addWidget(QLabel('')) line_desc = QLabel(i18n['about.info.desc']) line_desc.setObjectName('app_description') layout.addWidget(line_desc) layout.addWidget(QLabel('')) available_gems = [ f for f in glob('{}/gems/*'.format(ROOT_DIR)) if not f.endswith('.py') and not f.endswith('__pycache__') ] available_gems.sort() gems_widget = QWidget() gems_widget.setLayout(QHBoxLayout()) gems_widget.layout().addWidget(QLabel()) gem_logo_size = int(0.032552083 * QApplication.primaryScreen().size().height()) for gem_path in available_gems: icon = QLabel() icon.setObjectName('gem_logo') icon_path = gem_path + '/resources/img/{}.svg'.format( gem_path.split('/')[-1]) icon.setPixmap( QIcon(icon_path).pixmap(gem_logo_size, gem_logo_size)) gems_widget.layout().addWidget(icon) gems_widget.layout().addWidget(QLabel()) layout.addWidget(gems_widget) layout.addWidget(QLabel('')) label_more_info = QLabel() label_more_info.setObjectName('app_more_information') label_more_info.setText(i18n['about.info.link'] + " <a href='{url}'>{url}</a>".format( url=PROJECT_URL)) label_more_info.setOpenExternalLinks(True) layout.addWidget(label_more_info) label_license = QLabel() label_license.setObjectName('app_license') label_license.setText("<a href='{}'>{}</a>".format( LICENSE_URL, i18n['about.info.license'])) label_license.setOpenExternalLinks(True) layout.addWidget(label_license) layout.addWidget(QLabel('')) label_trouble_question = QLabel(i18n['about.info.trouble.question']) label_trouble_question.setObjectName('app_trouble_question') layout.addWidget(label_trouble_question) label_trouble_answer = QLabel(i18n['about.info.trouble.answer']) label_trouble_answer.setObjectName('app_trouble_answer') layout.addWidget(label_trouble_answer) layout.addWidget(QLabel('')) label_rate_question = QLabel(i18n['about.info.rate.question']) label_rate_question.setObjectName('app_rate_question') layout.addWidget(label_rate_question) label_rate_answer = QLabel(i18n['about.info.rate.answer']) label_rate_answer.setObjectName('app_rate_answer') layout.addWidget(label_rate_answer) layout.addWidget(QLabel('')) self.adjustSize() self.setFixedSize(self.size())
def notify_user(msg: str, icon_path: str = resource.get_path('img/logo.svg')): os.system("notify-send -a {} {} '{}'".format( __app_name__, "-i {}".format(icon_path) if icon_path else '', msg))
def __init__(self, model: MultipleSelectComponent, parent: QWidget = None): super(FormMultipleSelectQt, self).__init__(parent=parent) self.model = model self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) if model.max_width > 0: self.setMaximumWidth(model.max_width) if model.max_height > 0: self.setMaximumHeight(model.max_height) self._layout = QGridLayout() self.setLayout(self._layout) if model.label: line = 1 self.layout().addWidget(QLabel(), 0, 1) else: line = 0 col = 0 pixmap_help = QPixmap() for op in model.options: # loads the help icon if at least one option has a tooltip if op.tooltip: try: pixmap_help = QIcon(resource.get_path('img/about.svg')).pixmap(QSize(16, 16)) except: traceback.print_exc() break for op in model.options: comp = CheckboxQt(op, model, None) if model.values and op in model.values: self.value = comp comp.setChecked(True) widget = QWidget() widget.setLayout(QHBoxLayout()) widget.layout().addWidget(comp) if op.tooltip: help_icon = QLabel() help_icon.setPixmap(pixmap_help) help_icon.setToolTip(op.tooltip) help_icon.setCursor(QCursor(Qt.PointingHandCursor)) widget.layout().addWidget(help_icon) self._layout.addWidget(widget, line, col) if col + 1 == self.model.max_per_line: line += 1 col = 0 else: col += 1 if model.label: self.layout().addWidget(QLabel(), line + 1, 1)
def __init__(self, i18n: dict, icon_cache: MemoryCache, manager: SoftwareManager, disk_cache: bool, download_icons: bool, screen_size, suggestions: bool, display_limit: int, config: Configuration, context: ApplicationContext, notifications: bool, tray_icon=None): super(ManageWindow, self).__init__() self.i18n = i18n self.manager = manager self.tray_icon = tray_icon self.working = False # restrict the number of threaded actions self.pkgs = [] # packages current loaded in the table self.pkgs_available = [] # all packages loaded in memory self.pkgs_installed = [] # cached installed packages self.display_limit = display_limit self.icon_cache = icon_cache self.disk_cache = disk_cache self.download_icons = download_icons self.screen_size = screen_size self.config = config self.context = context self.notifications = notifications self.icon_app = QIcon(resource.get_path('img/logo.svg')) self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__) self.setWindowIcon(self.icon_app) self.layout = QVBoxLayout() self.setLayout(self.layout) self.toolbar_top = QToolBar() self.toolbar_top.addWidget(new_spacer()) self.label_status = QLabel() self.label_status.setText('') self.label_status.setStyleSheet("font-weight: bold") self.toolbar_top.addWidget(self.label_status) self.toolbar_search = QToolBar() self.toolbar_search.setStyleSheet("spacing: 0px;") self.toolbar_search.setContentsMargins(0, 0, 0, 0) label_pre_search = QLabel() label_pre_search.setStyleSheet( "background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;" ) self.toolbar_search.addWidget(label_pre_search) self.input_search = QLineEdit() self.input_search.setMaxLength(20) self.input_search.setFrame(False) self.input_search.setPlaceholderText( self.i18n['window_manage.input_search.placeholder'] + "...") self.input_search.setToolTip( self.i18n['window_manage.input_search.tooltip']) self.input_search.setStyleSheet( "QLineEdit { background-color: white; color: gray; spacing: 0; height: 30px; font-size: 12px; width: 300px}" ) self.input_search.returnPressed.connect(self.search) self.toolbar_search.addWidget(self.input_search) label_pos_search = QLabel() label_pos_search.setPixmap(QPixmap( resource.get_path('img/search.svg'))) label_pos_search.setStyleSheet( "background: white; padding-right: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px;" ) self.toolbar_search.addWidget(label_pos_search) self.ref_toolbar_search = self.toolbar_top.addWidget( self.toolbar_search) self.toolbar_top.addWidget(new_spacer()) self.layout.addWidget(self.toolbar_top) self.toolbar = QToolBar() self.toolbar.setStyleSheet( 'QToolBar {spacing: 4px; margin-top: 15px; margin-bottom: 5px}') self.checkbox_updates = QCheckBox() self.checkbox_updates.setText(self.i18n['updates'].capitalize()) self.checkbox_updates.stateChanged.connect(self._handle_updates_filter) self.ref_checkbox_updates = self.toolbar.addWidget( self.checkbox_updates) self.checkbox_only_apps = QCheckBox() self.checkbox_only_apps.setText( self.i18n['manage_window.checkbox.only_apps']) self.checkbox_only_apps.setChecked(True) self.checkbox_only_apps.stateChanged.connect( self._handle_filter_only_apps) self.ref_checkbox_only_apps = self.toolbar.addWidget( self.checkbox_only_apps) self.any_type_filter = 'any' self.cache_type_filter_icons = {} self.combo_filter_type = QComboBox() self.combo_filter_type.setStyleSheet('QLineEdit { height: 2px}') self.combo_filter_type.setEditable(True) self.combo_filter_type.lineEdit().setReadOnly(True) self.combo_filter_type.lineEdit().setAlignment(Qt.AlignCenter) self.combo_filter_type.activated.connect(self._handle_type_filter) self.combo_filter_type.addItem( load_icon(resource.get_path('img/logo.svg'), 14), self.i18n[self.any_type_filter].capitalize(), self.any_type_filter) self.ref_combo_filter_type = self.toolbar.addWidget( self.combo_filter_type) self.input_name_filter = InputFilter(self.apply_filters_async) self.input_name_filter.setMaxLength(10) self.input_name_filter.setPlaceholderText( self.i18n['manage_window.name_filter.placeholder'] + '...') self.input_name_filter.setToolTip( self.i18n['manage_window.name_filter.tooltip']) self.input_name_filter.setStyleSheet( "QLineEdit { background-color: white; color: gray;}") self.input_name_filter.setFixedWidth(130) self.ref_input_name_filter = self.toolbar.addWidget( self.input_name_filter) self.toolbar.addWidget(new_spacer()) self.bt_installed = QPushButton() self.bt_installed.setToolTip( self.i18n['manage_window.bt.installed.tooltip']) self.bt_installed.setIcon(QIcon(resource.get_path('img/disk.png'))) self.bt_installed.setText( self.i18n['manage_window.bt.installed.text'].capitalize()) self.bt_installed.clicked.connect(self._show_installed) self.bt_installed.setStyleSheet(toolbar_button_style('#A94E0A')) self.ref_bt_installed = self.toolbar.addWidget(self.bt_installed) self.bt_refresh = QPushButton() self.bt_refresh.setToolTip(i18n['manage_window.bt.refresh.tooltip']) self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg'))) self.bt_refresh.setText(self.i18n['manage_window.bt.refresh.text']) self.bt_refresh.setStyleSheet(toolbar_button_style('#2368AD')) self.bt_refresh.clicked.connect( lambda: self.refresh_apps(keep_console=False)) self.ref_bt_refresh = self.toolbar.addWidget(self.bt_refresh) self.bt_upgrade = QPushButton() self.bt_upgrade.setToolTip(i18n['manage_window.bt.upgrade.tooltip']) self.bt_upgrade.setIcon(QIcon(resource.get_path('img/app_update.svg'))) self.bt_upgrade.setText(i18n['manage_window.bt.upgrade.text']) self.bt_upgrade.setStyleSheet(toolbar_button_style('#20A435')) self.bt_upgrade.clicked.connect(self.update_selected) self.ref_bt_upgrade = self.toolbar.addWidget(self.bt_upgrade) self.layout.addWidget(self.toolbar) self.table_apps = AppsTable(self, self.icon_cache, disk_cache=self.disk_cache, download_icons=self.download_icons) self.table_apps.change_headers_policy() self.layout.addWidget(self.table_apps) toolbar_console = QToolBar() self.checkbox_console = QCheckBox() self.checkbox_console.setText( self.i18n['manage_window.checkbox.show_details']) self.checkbox_console.stateChanged.connect(self._handle_console) self.checkbox_console.setVisible(False) self.ref_checkbox_console = toolbar_console.addWidget( self.checkbox_console) toolbar_console.addWidget(new_spacer()) self.label_displayed = QLabel() toolbar_console.addWidget(self.label_displayed) self.layout.addWidget(toolbar_console) self.textarea_output = QPlainTextEdit(self) self.textarea_output.resize(self.table_apps.size()) self.textarea_output.setStyleSheet("background: black; color: white;") self.layout.addWidget(self.textarea_output) self.textarea_output.setVisible(False) self.textarea_output.setReadOnly(True) self.toolbar_substatus = QToolBar() self.toolbar_substatus.addWidget(new_spacer()) self.label_substatus = QLabel() self.toolbar_substatus.addWidget(self.label_substatus) self.toolbar_substatus.addWidget(new_spacer()) self.layout.addWidget(self.toolbar_substatus) self._change_label_substatus('') self.thread_update = self._bind_async_action( UpdateSelectedApps(self.manager, self.i18n), finished_call=self._finish_update_selected) self.thread_refresh = self._bind_async_action( RefreshApps(self.manager), finished_call=self._finish_refresh_apps, only_finished=True) self.thread_uninstall = self._bind_async_action( UninstallApp(self.manager, self.icon_cache), finished_call=self._finish_uninstall) self.thread_get_info = self._bind_async_action( GetAppInfo(self.manager), finished_call=self._finish_get_info) self.thread_get_history = self._bind_async_action( GetAppHistory(self.manager, self.i18n), finished_call=self._finish_get_history) self.thread_search = self._bind_async_action( SearchPackages(self.manager), finished_call=self._finish_search, only_finished=True) self.thread_downgrade = self._bind_async_action( DowngradeApp(self.manager, self.i18n), finished_call=self._finish_downgrade) self.thread_suggestions = self._bind_async_action( FindSuggestions(man=self.manager), finished_call=self._finish_search, only_finished=True) self.thread_run_app = self._bind_async_action( LaunchApp(self.manager), finished_call=self._finish_run_app, only_finished=False) self.thread_custom_action = self._bind_async_action( CustomAction(manager=self.manager), finished_call=self._finish_custom_action) self.thread_apply_filters = ApplyFilters() self.thread_apply_filters.signal_finished.connect( self._finish_apply_filters_async) self.thread_apply_filters.signal_table.connect( self._update_table_and_upgrades) self.signal_table_update.connect( self.thread_apply_filters.stop_waiting) self.thread_install = InstallPackage(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache, locale_keys=self.i18n) self._bind_async_action(self.thread_install, finished_call=self._finish_install) self.thread_animate_progress = AnimateProgress() self.thread_animate_progress.signal_change.connect( self._update_progress) self.thread_verify_models = VerifyModels() self.thread_verify_models.signal_updates.connect( self._notify_model_data_change) self.toolbar_bottom = QToolBar() self.toolbar_bottom.setIconSize(QSize(16, 16)) self.toolbar_bottom.setStyleSheet('QToolBar { spacing: 3px }') self.toolbar_bottom.addWidget(new_spacer()) self.progress_bar = QProgressBar() self.progress_bar.setMaximumHeight(10 if QApplication.instance().style( ).objectName().lower() == 'windows' else 4) self.progress_bar.setTextVisible(False) self.ref_progress_bar = self.toolbar_bottom.addWidget( self.progress_bar) self.toolbar_bottom.addWidget(new_spacer()) self.combo_styles = StylesComboBox( parent=self, i18n=i18n, show_panel_after_restart=bool(tray_icon)) self.combo_styles.setStyleSheet('QComboBox {font-size: 12px;}') self.ref_combo_styles = self.toolbar_bottom.addWidget( self.combo_styles) bt_settings = IconButton( icon_path=resource.get_path('img/app_settings.svg'), action=self._show_settings_menu, background='#12ABAB', tooltip=self.i18n['manage_window.bt_settings.tooltip']) self.ref_bt_settings = self.toolbar_bottom.addWidget(bt_settings) self.layout.addWidget(self.toolbar_bottom) qt_utils.centralize(self) self.filter_only_apps = True self.type_filter = self.any_type_filter self.filter_updates = False self._maximized = False self.progress_controll_enabled = True self.recent_installation = False self.dialog_about = None self.first_refresh = suggestions self.thread_warnings = ListWarnings(man=manager, locale_keys=i18n) self.thread_warnings.signal_warnings.connect(self._show_warnings)
def main(): if not os.getenv('PYTHONUNBUFFERED'): os.environ['PYTHONUNBUFFERED'] = '1' args = app_args.read() logger = logs.new_logger(__app_name__, bool(args.logs)) app_args.validate(args, logger) i18n_key, i18n = util.get_locale_keys(args.locale) cache_cleaner = CacheCleaner() cache_factory = DefaultMemoryCacheFactory(expiration_time=args.cache_exp, cleaner=cache_cleaner) icon_cache = cache_factory.new(args.icon_exp) context = ApplicationContext(i18n=i18n, http_client=HttpClient(logger), disk_cache=args.disk_cache, download_icons=args.download_icons, app_root_dir=ROOT_DIR, cache_factory=cache_factory, disk_loader_factory=DefaultDiskCacheLoaderFactory(disk_cache_enabled=args.disk_cache, logger=logger), logger=logger, file_downloader=AdaptableFileDownloader(logger, bool(args.download_mthread))) user_config = config.read() app = QApplication(sys.argv) app.setApplicationName(__app_name__) app.setApplicationVersion(__version__) app.setWindowIcon(QIcon(resource.get_path('img/logo.svg'))) if user_config.style: app.setStyle(user_config.style) else: if app.style().objectName().lower() not in {'fusion', 'breeze'}: app.setStyle('Fusion') managers = gems.load_managers(context=context, locale=i18n_key, config=user_config) manager = GenericSoftwareManager(managers, context=context, app_args=args) manager.prepare() manage_window = ManageWindow(i18n=i18n, manager=manager, icon_cache=icon_cache, disk_cache=args.disk_cache, download_icons=bool(args.download_icons), screen_size=app.primaryScreen().size(), suggestions=args.sugs, display_limit=args.max_displayed, config=user_config, context=context, notifications=bool(args.system_notifications)) if args.tray: tray_icon = TrayIcon(i18n=i18n, manager=manager, manage_window=manage_window, check_interval=args.check_interval, update_notification=bool(args.system_notifications)) manage_window.set_tray_icon(tray_icon) tray_icon.show() if args.show_panel: tray_icon.show_manage_window() else: manage_window.refresh_apps() manage_window.show() cache_cleaner.start() sys.exit(app.exec_())
def load_resource_icon(path: str, width: int, height: int = None) -> QIcon: return load_icon(resource.get_path(path), width, height)
def new_manage_panel(app_args: Namespace, app_config: dict, logger: logging.Logger) -> Tuple[QApplication, QWidget]: i18n = generate_i18n(app_config, resource.get_path('locale')) cache_cleaner = CacheCleaner() cache_factory = DefaultMemoryCacheFactory(expiration_time=int(app_config['memory_cache']['data_expiration']), cleaner=cache_cleaner) icon_cache = cache_factory.new(int(app_config['memory_cache']['icon_expiration'])) http_client = HttpClient(logger) downloader = AdaptableFileDownloader(logger=logger, multithread_enabled=app_config['download']['multithreaded'], multithread_client=app_config['download']['multithreaded_client'], i18n=i18n, http_client=http_client, check_ssl=app_config['download']['check_ssl']) context = ApplicationContext(i18n=i18n, http_client=http_client, download_icons=bool(app_config['download']['icons']), app_root_dir=ROOT_DIR, cache_factory=cache_factory, disk_loader_factory=DefaultDiskCacheLoaderFactory(logger), logger=logger, distro=util.get_distro(), file_downloader=downloader, app_name=__app_name__, app_version=__version__, internet_checker=InternetChecker(offline=app_args.offline), suggestions_mapping=read_suggestions_mapping(), root_user=user.is_root()) managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config, default_locale=DEFAULT_I18N_KEY, logger=logger) if app_args.reset: util.clean_app_files(managers) exit(0) force_suggestions = bool(app_args.suggestions) manager = GenericSoftwareManager(managers, context=context, config=app_config, force_suggestions=force_suggestions) app = new_qt_application(app_config=app_config, logger=logger, quit_on_last_closed=True) screen_size = app.primaryScreen().size() context.screen_width, context.screen_height = screen_size.width(), screen_size.height() logger.info(f"Screen: {screen_size.width()} x {screen_size.height()} " f"(DPI: {int(app.primaryScreen().logicalDotsPerInch())})") if app_args.settings: # only settings window manager.cache_available_managers() return app, SettingsWindow(manager=manager, i18n=i18n, window=None) else: manage_window = ManageWindow(i18n=i18n, manager=manager, icon_cache=icon_cache, screen_size=screen_size, config=app_config, context=context, http_client=http_client, icon=util.get_default_icon()[1], force_suggestions=force_suggestions, logger=logger) prepare = PreparePanel(screen_size=screen_size, context=context, manager=manager, i18n=i18n, manage_window=manage_window, app_config=app_config, force_suggestions=force_suggestions) cache_cleaner.start() return app, prepare
def __init__(self, model: MultipleSelectComponent, callback): super(MultipleSelectQt, self).__init__(model.label if model.label else None) self.setStyleSheet(css.GROUP_BOX) self.model = model self._layout = QGridLayout() self.setLayout(self._layout) if model.max_width > 0: self.setMaximumWidth(model.max_width) if model.max_height > 0: self.setMaximumHeight(model.max_height) if model.label: line = 1 pre_label = QLabel() self.layout().addWidget(pre_label, 0, 1) else: line = 0 col = 0 pixmap_help = QPixmap() for op in model.options: # loads the help icon if at least one option has a tooltip if op.tooltip: with open(resource.get_path('img/about.svg'), 'rb') as f: pixmap_help.loadFromData(f.read()) pixmap_help = pixmap_help.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation) break for op in model.options: comp = CheckboxQt(op, model, callback) if model.values and op in model.values: self.value = comp comp.setChecked(True) widget = QWidget() widget.setLayout(QHBoxLayout()) widget.layout().addWidget(comp) if op.tooltip: help_icon = QLabel() help_icon.setPixmap(pixmap_help) help_icon.setToolTip(op.tooltip) widget.layout().addWidget(help_icon) self._layout.addWidget(widget, line, col) if col + 1 == self.model.max_per_line: line += 1 col = 0 else: col += 1 if model.label: pos_label = QLabel() self.layout().addWidget(pos_label, line + 1, 1)
def __init__(self, config: dict, screen_size: QSize, logger: logging.Logger, manage_process: Popen = None, settings_process: Popen = None): super(TrayIcon, self).__init__() self.app_config = config self.i18n = generate_i18n(config, resource.get_path('locale/tray')) self.screen_size = screen_size self.manage_process = manage_process self.settings_process = settings_process self.logger = logger self.http_client = HttpClient(logger=logger) if config['ui']['tray']['default_icon']: self.icon_default = QIcon(config['ui']['tray']['default_icon']) else: self.icon_default = QIcon.fromTheme('bauh_tray_default') if self.icon_default.isNull(): self.icon_default = load_resource_icon('img/logo.svg', 24) if config['ui']['tray']['updates_icon']: self.icon_updates = QIcon(config['ui']['tray']['updates_icon']) else: self.icon_updates = QIcon.fromTheme('bauh_tray_updates') if self.icon_updates.isNull(): self.icon_updates = load_resource_icon('img/logo_update.svg', 24) self.setIcon(self.icon_default) self.menu = QMenu() self.action_manage = self.menu.addAction( self.i18n['tray.action.manage']) self.action_manage.triggered.connect(self.show_manage_window) self.action_settings = self.menu.addAction( self.i18n['tray.settings'].capitalize()) self.action_settings.triggered.connect(self.show_settings_window) self.action_about = self.menu.addAction(self.i18n['tray.action.about']) self.action_about.triggered.connect(self.show_about) self.action_exit = self.menu.addAction(self.i18n['tray.action.exit']) self.action_exit.triggered.connect(lambda: QCoreApplication.exit()) self.setContextMenu(self.menu) self.manage_window = None self.dialog_about = None self.settings_window = None self.check_lock = Lock() self.check_thread = UpdateCheck(check_interval=int( config['updates']['check_interval']), check_file=False, lock=self.check_lock, logger=logger) self.check_thread.signal.connect(self.notify_updates) self.check_thread.start() self.recheck_thread = UpdateCheck(check_interval=5, check_file=True, lock=self.check_lock, logger=logger) self.recheck_thread.signal.connect(self.notify_updates) self.recheck_thread.start() self.update_thread = AppUpdateCheck(http_client=self.http_client, logger=self.logger, i18n=self.i18n) self.update_thread.start() self.last_updates = set() self.update_notification = bool(config['system']['notifications']) self.lock_notify = Lock() self.activated.connect(self.handle_click) self.set_default_tooltip()
def __init__(self, pkg: PackageView, root: QWidget, i18n: I18n, checked: bool = True, clickable: bool = True): super(UpdateToggleButton, self).__init__() self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.app_view = pkg self.root = root layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignCenter) self.setLayout(layout) self.bt = QToolButton() self.bt.setCursor(QCursor(Qt.PointingHandCursor)) self.bt.setCheckable(True) self.bt.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) if clickable: self.bt.clicked.connect(self.change_state) self.bt.setStyleSheet('QToolButton { background: ' + GREEN + ' }' + 'QToolButton:checked { background: gray } ' + ('QToolButton:disabled { background: #d69003 }' if not clickable and not checked else '')) layout.addWidget(self.bt) if not checked: self.bt.click() if clickable: self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg'))) self.setToolTip('{} {}'.format( i18n['manage_window.apps_table.upgrade_toggle.tooltip'], i18n['manage_window.apps_table.upgrade_toggle.enabled.tooltip'] )) else: if not checked: self.bt.setIcon(QIcon( resource.get_path('img/exclamation.svg'))) self.bt.setEnabled(False) tooltip = i18n['{}.update.disabled.tooltip'.format( pkg.model.gem_name)] if tooltip: self.setToolTip(tooltip) else: self.setToolTip('{} {}'.format( i18n[ 'manage_window.apps_table.upgrade_toggle.tooltip'], i18n[ 'manage_window.apps_table.upgrade_toggle.disabled.tooltip'] )) else: self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg'))) self.bt.setCheckable(False)
def ask_confirmation(title: str, body: str, i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')), widgets: List[QWidget] = None) -> bool: popup = ConfirmationDialog(title=title, body=body, i18n=i18n, icon=icon, widgets=widgets) popup.exec_() return popup.confirmed
def __init__(self, model: MultipleSelectComponent, callback): super(MultipleSelectQt, self).__init__(model.label if model.label else None) self.setStyleSheet(css.GROUP_BOX) self.model = model self._layout = QGridLayout() self.setLayout(self._layout) if model.max_width > 0: self.setMaximumWidth(model.max_width) if model.max_height > 0: self.setMaximumHeight(model.max_height) if model.label: line = 1 pre_label = QLabel() self.layout().addWidget(pre_label, 0, 1) else: line = 0 col = 0 pixmap_help = QPixmap() for op in model.options: # loads the help icon if at least one option has a tooltip if op.tooltip: try: pixmap_help = QIcon(resource.get_path('img/about.svg')).pixmap(QSize(16, 16)) except: traceback.print_exc() break for op in model.options: comp = CheckboxQt(op, model, callback) if model.values and op in model.values: self.value = comp comp.setChecked(True) widget = QWidget() widget.setLayout(QHBoxLayout()) widget.layout().addWidget(comp) if op.tooltip: help_icon = QLabel() help_icon.setPixmap(pixmap_help) help_icon.setToolTip(op.tooltip) widget.layout().addWidget(help_icon) self._layout.addWidget(widget, line, col) if col + 1 == self.model.max_per_line: line += 1 col = 0 else: col += 1 if model.label: pos_label = QLabel() self.layout().addWidget(pos_label, line + 1, 1)
def __init__(self, title: str, body: Optional[str], i18n: I18n, icon: QIcon = QIcon(resource.get_path('img/logo.svg')), widgets: Optional[List[QWidget]] = None, confirmation_button: bool = True, deny_button: bool = True, window_cancel: bool = False, confirmation_label: Optional[str] = None, deny_label: Optional[str] = None): super(ConfirmationDialog, self).__init__() if not window_cancel: self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint) self.setLayout(QVBoxLayout()) self.setWindowTitle(title) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) self.setMinimumWidth(250) self.confirmed = False if icon: self.setWindowIcon(icon) container_body = QWidget() container_body.setObjectName('confirm_container_body') container_body.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) if widgets: container_body.setLayout(QVBoxLayout()) scroll = QScrollArea(self) scroll.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) scroll.setFrameShape(QFrame.NoFrame) scroll.setWidgetResizable(True) scroll.setWidget(container_body) self.layout().addWidget(scroll) else: container_body.setLayout(QHBoxLayout()) self.layout().addWidget(container_body) lb_icon = QLabel() lb_icon.setObjectName('confirm_icon') lb_icon.setPixmap(QApplication.style().standardIcon(QStyle.SP_MessageBoxQuestion).pixmap(QSize(48, 48))) container_body.layout().addWidget(lb_icon) if body: lb_msg = QLabel(body) lb_msg.setObjectName('confirm_msg') container_body.layout().addWidget(lb_msg) if widgets: for w in widgets: container_body.layout().addWidget(w) else: container_body.layout().addWidget(new_spacer()) container_bottom = QWidget() container_bottom.setObjectName('confirm_container_bottom') container_bottom.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) container_bottom.setLayout(QHBoxLayout()) self.layout().addWidget(container_bottom) container_bottom.layout().addWidget(new_spacer()) if confirmation_button: bt_confirm = QPushButton(confirmation_label.capitalize() if confirmation_label else i18n['popup.button.yes']) bt_confirm.setObjectName('ok') bt_confirm.setCursor(QCursor(Qt.PointingHandCursor)) bt_confirm.setDefault(True) bt_confirm.setAutoDefault(True) bt_confirm.clicked.connect(self.confirm) container_bottom.layout().addWidget(bt_confirm) if deny_button: bt_cancel = QPushButton(deny_label.capitalize() if deny_label else i18n['popup.button.no']) bt_cancel.setObjectName('bt_cancel') bt_cancel.setCursor(QCursor(Qt.PointingHandCursor)) bt_cancel.clicked.connect(self.close) container_bottom.layout().addWidget(bt_cancel) if not confirmation_button: bt_cancel.setDefault(True) bt_cancel.setAutoDefault(True)
def __init__(self, app_config: dict): super(AboutDialog, self).__init__() i18n = generate_i18n(app_config, resource.get_path('locale/about')) self.setWindowTitle('{} ({})'.format(i18n['about.title'].capitalize(), __app_name__)) layout = QVBoxLayout() self.setLayout(layout) label_logo = QLabel() icon = QIcon(resource.get_path('img/logo.svg')).pixmap(64, 64) label_logo.setPixmap(icon) label_logo.setAlignment(Qt.AlignCenter) layout.addWidget(label_logo) label_name = QLabel(__app_name__) label_name.setStyleSheet('font-weight: bold; font-size: 14px') label_name.setAlignment(Qt.AlignCenter) layout.addWidget(label_name) label_version = QLabel(i18n['about.version'].lower() + ' ' + __version__) label_version.setStyleSheet('QLabel { font-size: 11px; font-weight: bold }') label_version.setAlignment(Qt.AlignCenter) layout.addWidget(label_version) layout.addWidget(QLabel('')) line_desc = QLabel(i18n['about.info.desc']) line_desc.setStyleSheet('font-size: 12px; font-weight: bold;') line_desc.setAlignment(Qt.AlignCenter) line_desc.setMinimumWidth(400) layout.addWidget(line_desc) layout.addWidget(QLabel('')) available_gems = [f for f in glob('{}/gems/*'.format(ROOT_DIR)) if not f.endswith('.py') and not f.endswith('__pycache__')] available_gems.sort() gems_widget = QWidget() gems_widget.setLayout(QHBoxLayout()) gems_widget.layout().addWidget(QLabel()) for gem_path in available_gems: icon = QLabel() icon_path = gem_path + '/resources/img/{}.svg'.format(gem_path.split('/')[-1]) icon.setPixmap(QIcon(icon_path).pixmap(QSize(25, 25))) gems_widget.layout().addWidget(icon) gems_widget.layout().addWidget(QLabel()) layout.addWidget(gems_widget) layout.addWidget(QLabel('')) label_more_info = QLabel() label_more_info.setStyleSheet('font-size: 11px;') label_more_info.setText(i18n['about.info.link'] + " <a href='{url}'>{url}</a>".format(url=PROJECT_URL)) label_more_info.setOpenExternalLinks(True) label_more_info.setAlignment(Qt.AlignCenter) layout.addWidget(label_more_info) label_license = QLabel() label_license.setStyleSheet('font-size: 11px;') label_license.setText("<a href='{}'>{}</a>".format(LICENSE_URL, i18n['about.info.license'])) label_license.setOpenExternalLinks(True) label_license.setAlignment(Qt.AlignCenter) layout.addWidget(label_license) layout.addWidget(QLabel('')) label_trouble_question = QLabel(i18n['about.info.trouble.question']) label_trouble_question.setStyleSheet('font-size: 10px; font-weight: bold') label_trouble_question.setAlignment(Qt.AlignCenter) layout.addWidget(label_trouble_question) label_trouble_answer = QLabel(i18n['about.info.trouble.answer']) label_trouble_answer.setStyleSheet('font-size: 10px;') label_trouble_answer.setAlignment(Qt.AlignCenter) layout.addWidget(label_trouble_answer) layout.addWidget(QLabel('')) label_rate_question = QLabel(i18n['about.info.rate.question']) label_rate_question.setStyleSheet('font-size: 10px; font-weight: bold;') label_rate_question.setAlignment(Qt.AlignCenter) layout.addWidget(label_rate_question) label_rate_answer = QLabel(i18n['about.info.rate.answer']) label_rate_answer.setStyleSheet('font-size: 10px;') label_rate_answer.setAlignment(Qt.AlignCenter) layout.addWidget(label_rate_answer) layout.addWidget(QLabel('')) self.adjustSize() self.setFixedSize(self.size())
def get_available_keys() -> Set[str]: locale_dir = resource.get_path('locale') return {file.split('/')[-1] for file in glob.glob(locale_dir + '/*')}