def do_startup(self): Gtk.Application.do_startup(self) Handy.init() actions = [{ 'name': 'settings', 'func': self.show_settings_window, 'accel': '<Primary>comma' }, { 'name': 'about', 'func': self.show_about_dialog }, { 'name': 'logout', 'func': self.log_out }, { 'name': 'search-guilds', 'func': self.search_guilds, 'accel': '<Primary>k' }] for a in actions: c_action = Gio.SimpleAction.new(a['name'], None) c_action.connect('activate', a['func']) self.add_action(c_action) if 'accel' in a.keys(): self.set_accels_for_action(f'app.{a["name"]}', [a['accel']])
def main(version): Handy.init() app = Application() app.version = version return app.run(sys.argv)
def do_startup(self): Gtk.Application.do_startup(self) GLib.set_application_name(_('Komikku')) GLib.set_prgname(self.application_id) Handy.init() Notify.init(_('Komikku'))
def do_startup(self): # Startup application Gtk.Application.do_startup(self) self.setup_actions() self.load_css() # Init Handy Handy.init()
def _setup(self): Handy.init() self._popup = None self._worker = None self._busy = False self._editing = False self._to_copy = [] self._to_cut = [] self._last_clicked = None self._dont_activate = False self._force_select = False self._history = [] self._index = -1 self._search_delay_handler_id = 0 self.gesture = Gtk.GestureLongPress.new(self.treeview) self.gesture.connect("pressed", self._on_long_pressed) self.filtered.set_visible_func(self._filter, data=None) self.sorted.set_default_sort_func(self._sort, None) self.selection.connect("changed", self._on_selection_changed) self.selection.set_select_function(self._on_select) self.treeview.connect("row-activated", self._on_row_activated) self.treeview.connect("button-press-event", self._on_clicked) self.name_cell.connect("editing-started", self._on_rename_started) self.name_cell.connect("editing-canceled", self._on_rename_finished) self.name_cell.connect("edited", self._on_rename_updated) self.previous.connect("clicked", self._on_go_previous) self.next.connect("clicked", self._on_go_next) self.rename.connect("clicked", self._on_rename_clicked) self.delete.connect("clicked", self._on_delete_clicked) self.cut.connect("clicked", self._on_cut_clicked) self.copy.connect("clicked", self._on_copy_clicked) self.paste.connect("clicked", self._on_paste_clicked) self.select_all.connect("clicked", self._on_select_all) self.select_none.connect("clicked", self._on_select_none) self.new_folder.connect("clicked", self._on_new_folder) self.close_button.connect("clicked", self._on_button_closed) self.help_button.connect("clicked", self._on_help_clicked) self.about_button.connect("clicked", self._on_about_clicked) self.show_hidden_button.connect("toggled", self._on_hidden_toggled) self.a_to_z_button.connect("toggled", self._on_sort_toggled) self.search.connect("toggled", self._on_search_toggled) self.search_entry.connect("search-changed", self._on_search_changed) self.search_entry.connect("stop-search", self._on_search_stopped) self.back.connect("clicked", self._on_back_clicked) places = PortfolioPlaces() places.connect("updated", self._on_places_updated) places.connect("removed", self._on_places_removed) self.places_box.add(places) self._move(PortfolioPlaces.PORTFOLIO_HOME_DIR)
def do_startup(self): log.info(distro.linux_distribution(full_distribution_name=False)) log.info("Starting up cozy " + __version__) self.ui = CozyUI(self.pkgdatadir, self, __version__) init_db() Gtk.Application.do_startup(self) Handy.init() log.info("libhandy version: {}".format(Handy._version)) self.ui.startup()
def do_startup(self): Gtk.Application.do_startup(self) Handy.init() action = Gio.SimpleAction.new("quit", None) action.connect("activate", self.do_quit) self.add_action(action) self.set_accels_for_action("app.quit", ["<Ctl>q"])
def do_startup(self): Handy.init() Gtk.Application.do_startup(self) self.setup_actions() self.build_gstreamer_pipeline() devmonitor = Gst.DeviceMonitor.new() devmonitor.add_filter('Video/Source', Gst.Caps.from_string('video/x-raw')) logger.debug('Monitor: {}', devmonitor) self.devmonitor = devmonitor NM.Client.new_async(None, self.cb_networkmanager_client_init_done)
def init(): print("initializing") Gtk.init() Handy.init() global builder builder = Gtk.Builder() builder.add_from_file("/app/bin/app.ui") builder.connect_signals(Handler()) global objects objects = builder.get_objects() o("window").show_all() print("initialized")
def do_startup(self): Gtk.Application.do_startup(self) css_provider = Gtk.CssProvider() css_provider.load_from_resource(f"{Constants.RESOURCEID}/ui/style.css") screen = Gdk.Screen.get_default() Gtk.StyleContext.add_provider_for_screen( screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, ) self.setup_actions() Handy.init()
def do_startup(self): if os.path.isdir('/var/cache/files') is False: os.mkdir('/var/cache/files') os.mkdir('/var/cache/files/covers') Gtk.Application.do_startup(self) Handy.init() action = Gio.SimpleAction.new("quit", None) action.connect("activate", self.do_quit) self.add_action(action) self.set_accels_for_action("app.quit", ["<Ctl>q"])
def do_startup(self): Gtk.Application.do_startup(self) Handy.init() # Load CSS css_provider = Gtk.CssProvider() css_provider.load_from_resource( '/com/rafaelmardojai/WebfontKitGenerator/css/styles.css') screen = Gdk.Screen.get_default() style_context = Gtk.StyleContext() style_context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
def __init__(self, version: str = None): super().__init__(application_id=APP_ID, flags=Gio.ApplicationFlags.HANDLES_OPEN | Gio.ApplicationFlags.HANDLES_COMMAND_LINE) Handy.init() self.add_main_option('new', 110, GLib.OptionFlags.OPTIONAL_ARG, GLib.OptionArg.STRING, _('Open new document on start.')) self.version = version # Init GSettings self.settings = Settings.new() self.init_style() self.window: NorkaWindow = None # Init storage location and SQL structure self.base_path = os.path.join(GLib.get_user_data_dir(), APP_TITLE) storage_path = self.settings.get_string("storage-path") if not storage_path: storage_path = os.path.join(self.base_path, STORAGE_NAME) self.settings.set_string("storage-path", storage_path) self.storage = Storage(storage_path) try: self.storage.init() except Exception as e: sys.exit(e) quit_action = Gio.SimpleAction.new("quit", None) quit_action.connect("activate", self.on_quit) self.add_action(quit_action) self.set_accels_for_action('app.quit', ('<Control>q',)) about_action = Gio.SimpleAction.new("about", None) about_action.connect("activate", self.on_about) self.add_action(about_action) preferences_action = Gio.SimpleAction.new("preferences", None) preferences_action.connect("activate", self.on_preferences) self.add_action(preferences_action) self.set_accels_for_action('app.preferences', ('<Control>comma',)) shortcuts_action = Gio.SimpleAction.new("shortcuts", None) shortcuts_action.connect("activate", self.on_shortcuts) self.add_action(shortcuts_action) format_shortcuts_action = Gio.SimpleAction.new("format_shortcuts", None) format_shortcuts_action.connect("activate", self.on_format_shortcuts) self.add_action(format_shortcuts_action)
def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) Handy.init() if self.__window is None: from lollypop.window import Window self.init() self.__window = Window() self.__window.connect("delete-event", self.__hide_on_delete) self.__window.setup() self.__window.show() self.player.restore_state()
def do_startup(self): Gtk.Application.do_startup(self) Handy.init() self._style_manager = Handy.StyleManager.get_default() if settings.get_int('theme') == 0: self._style_manager.set_color_scheme(Handy.ColorScheme.FORCE_LIGHT) elif settings.get_int('theme') == 1: self._style_manager.set_color_scheme(Handy.ColorScheme.FORCE_DARK) elif settings.get_int('theme') == 2: self._style_manager.set_color_scheme(Handy.ColorScheme.FORCE_LIGHT) elif settings.get_int('theme') == 3: self._style_manager.set_color_scheme( Handy.ColorScheme.PREFER_LIGHT) action = Gio.SimpleAction.new('prefs', None) action.connect('activate', self._prefs_cb) self.add_action(action) self.set_accels_for_action('app.prefs', ('<Ctrl>E', )) action = Gio.SimpleAction.new('theme_system', None) action.connect('activate', self._theme_system) self.add_action(action) action = Gio.SimpleAction.new('theme_light', None) action.connect('activate', self._theme_light) self.add_action(action) action = Gio.SimpleAction.new('theme_sepia', None) action.connect('activate', self._theme_sepia) self.add_action(action) action = Gio.SimpleAction.new('theme_dark', None) action.connect('activate', self._theme_dark) self.add_action(action) action = Gio.SimpleAction.new('shortcuts', None) action.connect('activate', self._shortcuts_cb) self.add_action(action) self.set_accels_for_action('app.shortcuts', ('<Ctrl>question', )) action = Gio.SimpleAction.new('about', None) action.connect('activate', self._about_cb) self.add_action(action) action = Gio.SimpleAction.new('quit', None) action.connect('activate', self._quit_cb) self.add_action(action) self.set_accels_for_action('app.quit', ('<Ctrl>Q', ))
def __init__(self, application_id, *args, **kwargs): super().__init__(*args, application_id=application_id, flags=Gio.ApplicationFlags.HANDLES_OPEN, **kwargs) self.add_main_option("verbose", b"v", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Verbose output", None) Handy.init() self.window = None self.settings = Settings.new() self.inhibitor = None self._application_id = application_id
def do_startup(self): Gtk.Application.do_startup(self) GLib.set_application_name(_('Dialect')) GLib.set_prgname('com.github.gi_lom.dialect') self.setup_actions() Handy.init() # Init Handy Gst.init(None) # Init Gst # Load CSS css_provider = Gtk.CssProvider() css_provider.load_from_resource(f'{RES_PATH}/style.css') screen = Gdk.Screen.get_default() style_context = Gtk.StyleContext() style_context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
class HelloWindow(Handy.ApplicationWindow): __gtype_name__ = 'HelloWindow' Handy.init() def __init__(self, **kwargs): super().__init__(**kwargs) header = Handy.HeaderBar() header.props.show_close_button = True header.props.hexpand = True header.props.title = "Hello World" label = Gtk.Label("Hello World") label.props.expand = True label.props.valign = label.props.halign = Gtk.Align.CENTER grid = Gtk.Grid() grid.props.expand = True grid.attach(header, 0, 0, 1, 1) grid.attach(label, 0, 1, 1, 1) self.add(grid) self.props.default_width = 480 self.props.default_height = 320 self.show_all()
def do_startup(self): Gtk.Application.do_startup(self) css_provider = Gtk.CssProvider() css_provider.load_from_resource('/io/github/seadve/Kooha/style.css') screen = Gdk.Screen.get_default() Gtk.StyleContext.add_provider_for_screen( screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, ) self.settings = Gio.Settings.new('io.github.seadve.Kooha') self.setup_actions() Handy.init()
def do_startup(self): Gtk.Application.do_startup(self) Handy.init() css_provider = Gtk.CssProvider() css_provider.load_from_resource('/io/github/seadve/Kooha/style.css') screen = Gdk.Screen.get_default() Gtk.StyleContext.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) self.settings = Gio.Settings.new('io.github.seadve.Kooha') self.setup_actions() self.set_accels_for_action("app.show-shortcuts", ["<Ctrl>question"]) self.set_accels_for_action("app.change-capture-mode", ["<Ctrl>f"]) self.set_accels_for_action("app.quit", ["<Ctrl>q"])
def do_startup(self): Gtk.Application.do_startup(self) Handy.init() self.settings = Gio.Settings.new(self.props.application_id) self.settings.bind('dark-mode', Gtk.Settings.get_default(), 'gtk-application-prefer-dark-theme', Gio.SettingsBindFlags.DEFAULT) # Setup actions new_window_action = Gio.SimpleAction.new( 'new-window', GLib.VariantType('s')) new_window_action.connect( 'activate', lambda _, param: self._new_window(param.get_string())) self.add_action(new_window_action) # Setup CSS css_uri = 'resource:///com/github/unrud/VideoDownloader/style.css' css_provider = Gtk.CssProvider() css_provider.load_from_file(Gio.File.new_for_uri(css_uri)) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
def do_startup(self): Gtk.Application.do_startup(self) Handy.init() web_context = WebKit2.WebContext.get_default() web_context.set_cache_model(WebKit2.CacheModel.WEB_BROWSER) cookies_file_path = GLib.get_user_data_dir() + '/cookies' cookie_manager = WebKit2.WebContext.get_cookie_manager(web_context) cookie_manager.set_accept_policy(WebKit2.CookieAcceptPolicy.ALWAYS) cookie_manager.set_persistent_storage(cookies_file_path, WebKit2.CookiePersistentStorage.TEXT) actions = [ ('prefs', self._prefs_cb, ('<Ctrl>E',)), ('shortcuts', self._shortcuts_cb, ('<Ctrl>F1',)), ('about', self._about_cb, None) ] for action, callback, accel in actions: simple_action = Gio.SimpleAction.new(action, None) simple_action.connect('activate', callback) self.add_action(simple_action) if accel: self.set_accels_for_action('app.' + action, accel)
from concurrent.futures import ThreadPoolExecutor from pathlib import Path import gi gi.require_version("Handy", "1") gi.require_version("Gtk", "3.0") gi.require_version("WebKit2", "4.0") from gi.repository import Gtk, GLib, Gdk, WebKit2, GdkPixbuf, Gio, Handy from hn.api import top_stories, get_comment, get_story, Comment, Story from hn.bus import Bus BG_TASKS = ThreadPoolExecutor(max_workers=4) WEBEXT_DIR = '/home/david/git/webkit-webextension' Handy.init( ) # Must call this otherwise the Template() calls don't know how to resolve any Hdy* widgets def load_icon_to_pixbuf(name, width): path = f'/hn/icons/{name}' pixbuf = GdkPixbuf.Pixbuf.new_from_resource_at_scale(path, width, -1, True) return pixbuf data = pkg_resources.resource_stream('hn', 'resources') glib_data = GLib.Bytes.new(data.read()) resource = Gio.Resource.new_from_data(glib_data) resource._register() _rs = Gio.resources_lookup_data('/hn/css/reader_mode.css', Gio.ResourceLookupFlags.NONE) READER_CSS = _rs.get_data().decode().replace('\n', '')
class BottlesListEntry(Handy.ActionRow): __gtype_name__ = 'BottlesListEntry' Handy.init() '''Get widgets from template''' btn_details = Gtk.Template.Child() btn_delete = Gtk.Template.Child() btn_browse = Gtk.Template.Child() btn_run = Gtk.Template.Child() btn_upgrade = Gtk.Template.Child() btn_repair = Gtk.Template.Child() btn_programs = Gtk.Template.Child() btn_run_executable = Gtk.Template.Child() label_environment = Gtk.Template.Child() label_state = Gtk.Template.Child() icon_damaged = Gtk.Template.Child() grid_versioning = Gtk.Template.Child() def __init__(self, window, configuration, arg_executable, **kwargs): super().__init__(**kwargs) '''Init template''' self.init_template() '''Common variables''' self.window = window self.runner = window.runner self.configuration = configuration[1] self.label_environment_context = self.label_environment.get_style_context( ) self.arg_executable = arg_executable '''Check runner type by name''' if self.configuration.get("Runner").startswith("lutris"): self.runner_type = "wine" else: self.runner_type = "proton" '''Signal connections''' self.btn_details.connect('pressed', self.show_details) self.btn_details.connect('activate', self.show_details) self.btn_delete.connect('pressed', self.confirm_delete) self.btn_upgrade.connect('pressed', self.upgrade_runner) self.btn_run.connect('pressed', self.run_executable) self.btn_browse.connect('pressed', self.run_browse) self.btn_repair.connect('pressed', self.repair) self.btn_run_executable.connect('pressed', self.run_executable) '''Populate widgets''' self.grid_versioning.set_visible(self.configuration.get("Versioning")) self.label_state.set_text(str(self.configuration.get("State"))) self.set_title(self.configuration.get("Name")) self.label_environment.set_text(self.configuration.get("Environment")) self.label_environment_context.add_class( "tag-%s" % self.configuration.get("Environment").lower()) '''Toggle btn_upgrade if self.configuration.get("Runner") != self.runner.get_latest_runner(self.runner_type): self.btn_upgrade.set_visible(True) ''' '''If configuration is broken''' if self.configuration.get("Broken"): for w in [self.btn_repair, self.icon_damaged]: w.set_visible(True) for w in [ self.btn_details, self.btn_upgrade, self.btn_run, self.btn_browse, self.btn_programs ]: w.set_sensitive(False) else: '''Check for arguments from configuration''' if self.arg_executable: logging.info( _("Arguments found for executable: [{executable}]."). format(executable=self.arg_executable)) for w in [ self.btn_details, self.btn_upgrade, self.btn_run, self.btn_browse, self.btn_programs, self.btn_delete ]: w.set_visible(False) self.btn_run_executable.set_visible(True) '''Repair bottle''' def repair(self, widget): self.runner.repair_bottle(self.configuration) '''Display file dialog for executable''' def run_executable(self, widget): if not self.arg_executable: '''If executable is not Bottles argument''' file_dialog = Gtk.FileChooserDialog( _("Choose a Windows executable file"), self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) '''Create filters for allowed extensions''' filter_exe = Gtk.FileFilter() filter_exe.set_name(".exe") filter_exe.add_pattern("*.exe") filter_msi = Gtk.FileFilter() filter_msi.set_name(".msi") filter_msi.add_pattern("*.msi") filter_bat = Gtk.FileFilter() filter_bat.set_name(".bat") filter_bat.add_pattern("*.bat") file_dialog.add_filter(filter_exe) file_dialog.add_filter(filter_msi) file_dialog.add_filter(filter_bat) response = file_dialog.run() if response == Gtk.ResponseType.OK: self.runner.run_executable(self.configuration, file_dialog.get_filename()) file_dialog.destroy() else: '''Use executable provided as bottles argument''' self.runner.run_executable(self.configuration, self.arg_executable) self.arg_executable = False self.runner.update_bottles() '''Browse bottle drive_c files''' def run_browse(self, widget): self.runner.open_filemanager(self.configuration) '''Show dialog to confirm runner upgrade''' def upgrade_runner(self, widget): dialog_upgrade = BottlesMessageDialog( parent=self.window, title=_("Confirm upgrade"), message=_("This will change the runner from {0} to {1}.").format( self.configuration.get("Runner"), self.runner.get_latest_runner(self.runner_type))) response = dialog_upgrade.run() if response == Gtk.ResponseType.OK: self.runner.update_configuration(self.configuration, "Runner", self.runner.get_latest_runner()) self.btn_upgrade.set_visible(False) dialog_upgrade.destroy() '''Show details page''' def show_details(self, widget): self.window.show_details_view(configuration=self.configuration) '''Show dialog to confirm bottle deletion''' def confirm_delete(self, widget): dialog_delete = BottlesMessageDialog( parent=self.window, title=_("Confirm deletion"), message=_( "Are you sure you want to delete this Bottle and all files?")) response = dialog_delete.run() if response == Gtk.ResponseType.OK: self.runner.delete_bottle(self.configuration) self.destroy() dialog_delete.destroy()
# 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # SPDX-License-Identifier: GPL-3.0-or-later from gi.repository import Gtk, Handy from .shalatlistrow import ShalatListRow Handy.init() @Gtk.Template(resource_path='/com/github/yursan9/Arkan/ui/shalatlist.ui') class ShalatList(Handy.Column): __gtype_name__ = 'ShalatList' listbox = Gtk.Template.Child() def __init__(self, **kwargs): super().__init__(**kwargs) self.listbox.set_header_func(self._add_separator) def populate(self, data): self.listbox.foreach(lambda widget: widget.destroy())
class MainWindow(Handy.ApplicationWindow): __gtype_name__ = 'MainWindow' __gsignals__ = { 'notify-requested': (GObject.SIGNAL_RUN_CLEANUP, None, (str, )) } Handy.init() main_container = Gtk.Template.Child() main_paned = Gtk.Template.Child() main_overlay = Gtk.Template.Child() button_next_page = Gtk.Template.Child() button_previous_page = Gtk.Template.Child() status_loading = Gtk.Template.Child() loading_spinner = Gtk.Template.Child() loading_message = Gtk.Template.Child() revealer_notification = Gtk.Template.Child() notification_icon = Gtk.Template.Child() notification_message = Gtk.Template.Child() # To remember the previous value of application variables for deciding # which child widget to reload tarajem_visibility: bool = False surah_number: int = None ayah_number: int = None page_number: int = None page_focused: int = None tarajem_names: list = None def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Set the window application name identifier, so it can be recognized # by the user in the desktop application switcher self.set_title(const.APPLICATION_NAME) self.set_wmclass(const.APPLICATION_NAME, const.APPLICATION_NAME) # FIXME: the application icon appears too small self.set_default_icon_name(const.APPLICATION_ID) self.setup_headerbar() self.setup_musshaf_viewer() self.setup_tarajem_viewer() self.show_all() self.setup_window_size() # Init self variables self.surah_number = glob.surah_number self.ayah_number = glob.ayah_number # Init children states self.headerbar.popover_nav.update() self.headerbar.popover_nav_alt.update() self.musshaf_viewer_right.update() self.musshaf_viewer_left.update() self.headerbar.popover_tarajem.populate() self.headerbar.popover_telaawa.populate() self.headerbar.popover_listofcontents.populate() self.headerbar.button_open_tarajem.set_active(glob.tarajem_visibility) def setup_headerbar(self) -> None: self.headerbar = HeaderBar() self.main_container.pack_start(self.headerbar, False, True, 0) self.headerbar.connect('tarajem-toggled', self.reload_tarajem_viewer) self.headerbar.connect('mobileview-toggled', self.reload_navigation_panel) self.headerbar.popover_bookmark.connect( 'go-to-page', self.headerbar.popover_nav.go_to_defined_page) self.headerbar.popover_bookmark.connect( 'go-to-page', self.headerbar.popover_nav_alt.go_to_defined_page) self.headerbar.popover_search.connect( 'go-to-suraya', self.headerbar.popover_nav.go_to_suraya) self.headerbar.popover_search.connect( 'go-to-suraya', self.headerbar.popover_nav_alt.go_to_suraya) self.headerbar.popover_tarajem.connect('tarajem-names-updated', self.reload_tarajem_viewer) self.headerbar.popover_telaawa.connect( 'go-to-next-ayah', self.headerbar.popover_nav.go_to_next_ayah) self.headerbar.popover_telaawa.connect( 'go-to-previous-ayah', self.headerbar.popover_nav.go_to_previous_ayah) self.headerbar.popover_telaawa.connect( 'go-to-next-ayah', self.headerbar.popover_nav_alt.go_to_next_ayah) self.headerbar.popover_telaawa.connect( 'go-to-previous-ayah', self.headerbar.popover_nav_alt.go_to_previous_ayah) self.headerbar.popover_listofcontents.connect( 'go-to-suraya', self.headerbar.popover_nav.go_to_suraya) self.headerbar.popover_listofcontents.connect( 'go-to-suraya', self.headerbar.popover_nav_alt.go_to_suraya) self.button_next_page.connect( 'clicked', self.headerbar.popover_nav.go_to_next_page) self.button_previous_page.connect( 'clicked', self.headerbar.popover_nav.go_to_previous_page) self.button_next_page.connect( 'clicked', self.headerbar.popover_nav_alt.go_to_next_page) self.button_previous_page.connect( 'clicked', self.headerbar.popover_nav_alt.go_to_previous_page) self.headerbar.popover_menu.connect('page-scaled', self.resize_musshaf) self.headerbar.popover_menu.connect('dualpage-toggled', self.toggle_dualpage) self.headerbar.popover_menu.connect('nightmode-toggled', self.toggle_nightmode) def setup_musshaf_viewer(self) -> None: self.musshaf_viewer_right = MusshafViewer(0) self.main_paned.pack_end(self.musshaf_viewer_right, True, True, 0) self.musshaf_viewer_right.image.set_halign(Gtk.Align.START) self.musshaf_viewer_right.eventbox.set_halign(Gtk.Align.START) self.musshaf_viewer_left = MusshafViewer(1) self.musshaf_viewer_left.image.set_halign(Gtk.Align.END) self.musshaf_viewer_left.eventbox.set_halign(Gtk.Align.END) self.main_paned.pack_start(self.musshaf_viewer_left, True, True, 0) if glob.night_mode: self.main_paned.set_name('musshaf-dark') else: self.main_paned.set_name('musshaf-light') self.headerbar.popover_nav.connect('reload-musshaf-viewer', self.reload_musshaf_viewer) self.headerbar.popover_nav.connect('reload-musshaf-viewer', self.toggle_bookmark) self.headerbar.popover_nav.connect('reload-tarajem-viewer', self.reload_tarajem_viewer) self.headerbar.popover_nav.connect('reload-telaawa-player', self.reload_telaawa_player) self.headerbar.popover_nav_alt.connect('reload-musshaf-viewer', self.reload_musshaf_viewer) self.headerbar.popover_nav_alt.connect('reload-musshaf-viewer', self.toggle_bookmark) self.headerbar.popover_nav_alt.connect('reload-tarajem-viewer', self.reload_tarajem_viewer) self.headerbar.popover_nav_alt.connect('reload-telaawa-player', self.reload_telaawa_player) self.musshaf_viewer_right.connect('selected-ayah-changed', self.reload_navigation_panel) self.musshaf_viewer_right.connect('hovered-ayah-changed', self.reload_tarajem_viewer) self.musshaf_viewer_right.connect('focused-page-changed', self.reload_tarajem_viewer) self.musshaf_viewer_left.connect('selected-ayah-changed', self.reload_navigation_panel) self.musshaf_viewer_left.connect('hovered-ayah-changed', self.reload_tarajem_viewer) self.musshaf_viewer_left.connect('focused-page-changed', self.reload_tarajem_viewer) def setup_tarajem_viewer(self) -> None: self.tarajem_viewer = TarajemViewer() def setup_window_size(self) -> None: """Scale the window to fit all its visible contents The size is calculated from scaled two-side image pages (or a page). For its height, the headerbar height is added. """ musshaf_dir = path.join(const.USER_DATA_PATH, f'musshaf/{glob.musshaf_name}') # Load a sample image page of the opened Musshaf ID by assuming that # all image pages have the same size image_filepath = path.join(musshaf_dir, '1.jpg') page_image = GdkPixbuf.Pixbuf.new_from_file(image_filepath) page_width = round(page_image.get_width() * glob.page_scale) page_height = round(page_image.get_height() * glob.page_scale) headerbar_size = self.headerbar.get_allocation() window_height = page_height + headerbar_size.height if glob.dual_page: window_width = page_width * 2 + const.PAGE_MARGIN / 2 * 3 else: window_width = page_width self.resize(window_width, window_height + const.PAGE_MARGIN) self.tarajem_viewer.scrolledwindow.set_size_request( page_width, page_height) if glob.dual_page: window_width = window_width + 52 else: window_width = window_width + 72 self.set_size_request(window_width, self.get_size_request()[1]) @Gtk.Template.Callback() def on_key_press(self, window: Gtk.Window, event: Gdk.EventKey) -> None: if event.keyval == Gdk.KEY_Left: self.headerbar.popover_nav.go_to_next_page() self.headerbar.popover_nav_alt.go_to_next_page() elif event.keyval == Gdk.KEY_Right: self.headerbar.popover_nav.go_to_previous_page() self.headerbar.popover_nav_alt.go_to_previous_page() elif event.keyval == Gdk.KEY_Up: self.headerbar.popover_nav.go_to_previous_ayah( fixed_surah_no=False) self.headerbar.popover_nav_alt.go_to_previous_ayah( fixed_surah_no=False) elif event.keyval == Gdk.KEY_Down: self.headerbar.popover_nav.go_to_next_ayah(fixed_surah_no=False) self.headerbar.popover_nav_alt.go_to_next_ayah( fixed_surah_no=False) @Gtk.Template.Callback() def on_key_release(self, window: Gtk.Window, event: Gdk.EventKey) -> None: ... @Gtk.Template.Callback() def on_loses_focus(self, window: Gtk.Window, event: Gdk.EventFocus) -> None: ... @Gtk.Template.Callback() def on_quit(self, widget: Gtk.Widget) -> None: # Free resources player = self.headerbar.popover_telaawa player.playbin.set_state(Gst.State.NULL) Gst.Object.unref(player.playbin) if player.playbin_seeker: GLib.source_remove(player.playbin_seeker) @Gtk.Template.Callback() def on_buttonnav_entered(self, widget: Gtk.Widget, event: Gdk.EventCrossing) -> None: widget.get_children()[0].set_reveal_child(True) @Gtk.Template.Callback() def on_buttonnav_left(self, widget: Gtk.Widget, event: Gdk.EventCrossing) -> None: widget.get_children()[0].set_reveal_child(False) def reload_navigation_panel(self, widget: Gtk.Widget) -> None: if not glob.mobile_view: self.headerbar.popover_nav.update() else: self.headerbar.popover_nav_alt.update() def reload_musshaf_viewer(self, widget: Gtk.Widget) -> None: self.musshaf_viewer_right.update() if glob.dual_page: self.musshaf_viewer_left.update() def reload_tarajem_viewer(self, widget: Gtk.Widget) -> None: if self.page_focused != glob.page_focused \ or self.tarajem_visibility != glob.tarajem_visibility: self.tarajem_visibility = glob.tarajem_visibility self.main_paned.remove(self.musshaf_viewer_right) self.main_paned.remove(self.tarajem_viewer) self.main_paned.remove(self.musshaf_viewer_left) if glob.dual_page: self.musshaf_viewer_right.image.set_halign(Gtk.Align.START) self.musshaf_viewer_right.eventbox.set_halign(Gtk.Align.START) if glob.tarajem_visibility \ and glob.page_focused >= 0: # a negative is invalid # If the right Musshaf viewer is on focus, replace the left # Musshaf viewer with tarajem viewer and vice versa. if glob.page_focused == self.musshaf_viewer_right.id: self.main_paned.remove(self.musshaf_viewer_left) self.tarajem_viewer.set_halign(Gtk.Align.END) self.main_paned.pack_start(self.tarajem_viewer, True, True, 0) self.main_paned.pack_end(self.musshaf_viewer_right, True, True, 0) else: self.main_paned.remove(self.musshaf_viewer_right) self.tarajem_viewer.set_halign(Gtk.Align.START) self.main_paned.pack_end(self.tarajem_viewer, True, True, 0) self.main_paned.pack_start(self.musshaf_viewer_left, True, True, 0) else: # If tarajem opening is not requested, display only Musshaf # viewer(s) self.main_paned.pack_start(self.musshaf_viewer_left, True, True, 0) self.main_paned.pack_end(self.musshaf_viewer_right, True, True, 0) else: self.main_paned.pack_end(self.musshaf_viewer_right, True, True, 0) self.musshaf_viewer_right.image.set_halign(Gtk.Align.CENTER) self.musshaf_viewer_right.eventbox.set_halign(Gtk.Align.CENTER) # TODO: support displaying tarajem on mobile view mode if not glob.tarajem_visibility \ or not glob.dual_page: return is_page_no_updated = self.page_number != glob.page_number if is_page_no_updated: self.page_number = glob.page_number is_tarajem_names_updated = self.tarajem_names != glob.tarajem_names if is_tarajem_names_updated: self.tarajem_names = deepcopy(glob.tarajem_names) is_content_updated = is_page_no_updated \ or is_tarajem_names_updated # Populate tarajem based on the page where the focused ayah is # located if glob.page_focused == self.musshaf_viewer_right.id: self.tarajem_viewer.populate( self.musshaf_viewer_right.bboxes, self.musshaf_viewer_right.bboxes_hovered, self.musshaf_viewer_right.bboxes_focused, is_content_updated) else: self.tarajem_viewer.populate( self.musshaf_viewer_left.bboxes, self.musshaf_viewer_left.bboxes_hovered, self.musshaf_viewer_left.bboxes_focused, is_content_updated) def reload_telaawa_player(self, widget: Gtk.Widget) -> None: state = self.surah_number != glob.surah_number \ or self.ayah_number != glob.ayah_number self.surah_number = glob.surah_number self.ayah_number = glob.ayah_number player = self.headerbar.popover_telaawa if not player.ready_to_play: player.playback(TelaawaPlayerState.STOP) return if state: player.playback(TelaawaPlayerState.PLAY) else: player.playback(TelaawaPlayerState.STOP) def toggle_bookmark(self, widget: Gtk.Widget) -> None: for bookmark in glob.bookmark_names: musshaf_name, page_number, _ = bookmark.split(';') page_number = int(page_number) if musshaf_name != glob.musshaf_name: continue if page_number == glob.page_number: is_bookmarked = True break else: is_bookmarked = False self.headerbar.is_updating = True self.headerbar.button_toggle_bookmark.set_active(is_bookmarked) self.headerbar.is_updating = False def resize_musshaf(self, widget: Gtk.Widget) -> None: # FIXME: widget allocations are not updated immediately self.musshaf_viewer_right.update(True) if glob.dual_page: self.musshaf_viewer_left.update(True) self.setup_window_size() def toggle_dualpage(self, widget: Gtk.Widget) -> None: self.reload_tarajem_viewer(widget) self.resize_musshaf(widget) self.setup_window_size() def toggle_nightmode(self, widget: Gtk.Widget) -> None: self.musshaf_viewer_right.update(True) if glob.dual_page: self.musshaf_viewer_left.update(True) if glob.night_mode: self.main_paned.set_name('musshaf-dark') else: self.main_paned.set_name('musshaf-light')
def do_startup(self): Gtk.Application.do_startup(self) Handy.init()
class SpotipyneWindow(Handy.ApplicationWindow): __gtype_name__ = 'SpotipyneWindow' Handy.init() headerbar_switcher = Gtk.Template.Child() bottom_switcher = Gtk.Template.Child() player_deck = Gtk.Template.Child() main_stack = Gtk.Template.Child() back_button_stack = Gtk.Template.Child() back_button_playlists = Gtk.Template.Child() back_button_search = Gtk.Template.Child() simple_controls_parent = Gtk.Template.Child() def init_cover_art_loader(self): self.cover_art_loader = CoverArtLoader() def init_spotify_playback(self): self.spotify_playback = SpotifyPlayback(self.cover_art_loader) def init_simple_controls(self): self.simple_controls = SimpleControls(self.spotify_playback) self.simple_controls_parent.pack_start(self.simple_controls, False, True, 0) self.simple_controls.set_reveal_child(False) def init_library_overview(self): self.library_overview = LibraryOverview(self.sp_gui, self.back_button_playlists) self.main_stack.add_titled(self.library_overview, 'Library', 'Library') self.main_stack.child_set_property(self.library_overview, 'icon-name', 'applications-multimedia-symbolic') def init_search_overview(self): self.search_overview = SearchOverview(self.sp_gui, self.back_button_search) self.main_stack.add_titled(self.search_overview, 'Search', 'Search') self.main_stack.child_set_property(self.search_overview, 'icon-name', 'edit-find-symbolic') def init_back_buttons(self): self.back_button_stack.child_set_property self.main_stack.bind_property("visible-child-name", self.back_button_stack, "visible-child-name") def init_login(self): self.login_page = Login(self.on_logged_in) self.player_deck.add(self.login_page) self.player_deck.set_visible_child(self.login_page) self.player_deck.show_all() def __init__(self, **kwargs): super().__init__(**kwargs) self.init_login() def on_logged_in(self): self.player_deck.remove(self.login_page) self.player_deck.set_visible_child(self.player_deck.get_children()[0]) self.init_cover_art_loader() self.sp_gui = SpotifyGuiBuilder(self.cover_art_loader) self.init_library_overview() self.init_search_overview() self.init_spotify_playback() self.init_simple_controls() self.init_back_buttons() self.headerbar_switcher.bind_property("title-visible", self.bottom_switcher, "reveal", GObject.BindingFlags.SYNC_CREATE)
class CpupowerGuiWindow(Gtk.ApplicationWindow): __gtype_name__ = "CpupowerGuiWindow" Handy.init() cpu_box = Gtk.Template.Child() gov_box = Gtk.Template.Child() adj_min = Gtk.Template.Child() adj_max = Gtk.Template.Child() spin_min = Gtk.Template.Child() spin_max = Gtk.Template.Child() min_sl = Gtk.Template.Child() max_sl = Gtk.Template.Child() apply_btn = Gtk.Template.Child() toall = Gtk.Template.Child() about_dialog = Gtk.Template.Child() profile_box = Gtk.Template.Child() default_profile_pref = Gtk.Template.Child() energy_pref_box = Gtk.Template.Child() tree_view = Gtk.Template.Child() headerbar_switcher = Gtk.Template.Child() bottom_switcher = Gtk.Template.Child() squeezer = Gtk.Template.Child() default_allcpus = Gtk.Template.Child() default_ticks = Gtk.Template.Child() default_ticks_num = Gtk.Template.Child() default_energy_per_cpu = Gtk.Template.Child() energy_pref_percpu = Gtk.Template.Child() profile_overview = Gtk.Template.Child() def __init__(self, **kwargs): super().__init__(**kwargs) self.squeezer.connect("notify::visible-child", self.on_headerbar_squeezer_notify) # Read configuration self.conf = CpuPowerConfig() # Get GUI config and profiles self.gui_conf = self.conf.get_gui_settings() self.profiles = self.conf.profiles self.profile = None self.refreshing = False self.settings = {} self.energy_pref_avail = False self.energy_per_cpu = False self.tree_store = None self.tick_marks_enabled = True self.ticks_markup = True self.selected_cpus = None self.style_ctx = self.tree_view.get_style_context() self.fg = self.style_ctx.get_color(Gtk.StateFlags.NORMAL).to_string() self.update_cpubox() self.load_cpu_settings() self._update_tree_view() self.configure_gui() self.upd_sliders() GLib.timeout_add(500, self._update_current_freq) # Application actions action = Gio.SimpleAction.new("Exit", None) action.connect("activate", self.quit) self.add_action(action) def on_headerbar_squeezer_notify(self, squeezer, event): child = squeezer.get_visible_child() self.bottom_switcher.set_reveal(child != self.headerbar_switcher) self.gov_box.set_use_subtitle(child != self.headerbar_switcher) self.energy_pref_box.set_use_subtitle(child != self.headerbar_switcher) self.cpu_box.set_use_subtitle(child != self.headerbar_switcher) @contextmanager def lock(self): """Helper function to stop widgets from refreshing""" self.refreshing = True yield self.refreshing = False def load_cpu_settings(self): """Initialise the configuration store""" for cpu in self.online_cpus: self.settings[cpu] = CpuSettings(cpu) self._update_treeview_style(cpu, False) self.energy_pref_avail = self.settings[0].energy_pref_avail def update_cpubox(self): """Update the CPU Combobox""" self.cpu_store = Gio.ListStore() self.cpu_box.bind_name_model(self.cpu_store, lambda x: x.name) for cpu in self.online_cpus: self.cpu_store.append(CpuCore(cpu)) self.cpu_box.set_selected_index(0) def configure_gui(self): """Configures GUI based on the config file""" # Add prefix checkbutton to cpu self.cpu_online = Gtk.CheckButton() self.cpu_online.connect("toggled", self.on_cpu_online_toggled) self.cpu_box.add_prefix(self.cpu_online) self.cpu_online.show() # Preferences toggle_default = self.gui_conf.getboolean("allcpus_default", False) self.toall.set_active(toggle_default) self.default_allcpus.set_active(toggle_default) default_ticks = self.gui_conf.getboolean("tick_marks_enabled", True) self.tick_marks_enabled = default_ticks self.default_ticks.set_active(default_ticks) default_ticks_num = self.gui_conf.getboolean("frequency_ticks", True) self.ticks_markup = default_ticks_num self.default_ticks_num.set_active(default_ticks_num) default_energy_percpu = self.gui_conf.getboolean( "energy_pref_per_cpu", False) self.energy_per_cpu = default_energy_percpu self.default_energy_per_cpu.set_active(default_energy_percpu) self.update_profile_boxes() self.generate_profiles_page() # Check if intel pstate perfs are available if self.energy_pref_avail: self.energy_pref_box.set_visible(True) self.energy_pref_percpu.set_visible(True) def update_profile_boxes(self): # Configure profiles box self.prof_store = Gio.ListStore() # Empty profile name to use for resetting settings self.prof_store.append(Profile(_("No profile"))) # Add the profiles from configuration for prof in self.conf.profiles: profile = self.conf.get_profile(prof) self.prof_store.append(Profile(profile)) self.profile_box.bind_name_model(self.prof_store, lambda x: x.name) # Add the profiles from configuration to prefs model = Gio.ListStore() for prof in self.conf.profiles: model.append(Profile(prof)) index = self.conf.get_profile_index(self.conf.default_profile) self.default_profile_pref.bind_name_model(model, lambda x: x.name) self.default_profile_pref.set_selected_index(index) def generate_profiles_page(self): """Create preference groups for profiles""" new_profile = Handy.PreferencesGroup(title=_("New profile")) self.custom_profiles = Handy.PreferencesGroup(title=_("User profiles")) self.system_profiles = Handy.PreferencesGroup( title=_("System profiles")) self.builtin_profiles = Handy.PreferencesGroup( title=_("Built-in profiles")) self.profile_overview.add(new_profile) self.profile_overview.add(self.custom_profiles) self.profile_overview.add(self.system_profiles) self.profile_overview.add(self.builtin_profiles) # Add entry for current profile prof_entry = Handy.ActionRow(title=_("Profile name")) save_me = Gtk.Button.new_from_icon_name("document-save", 1) save_me.set_sensitive(False) self.profile_name_entry = Gtk.Entry(placeholder_text=_("Name")) self.profile_name_entry.connect("changed", self.on_prof_name_changed, save_me) prof_entry.add(self.profile_name_entry) prof_entry.add(save_me) save_me.connect("clicked", self.on_save_profile_clicked) prof_entry.set_activatable_widget(save_me) new_profile.add(prof_entry) self._generate_profile_list() def update_profiles_page(self): """Update listed profiles""" for child in self.custom_profiles.get_children(): child.destroy() for child in self.system_profiles.get_children(): child.destroy() for child in self.builtin_profiles.get_children(): child.destroy() self._generate_profile_list() def _generate_profile_list(self): """Create profile listings using fresh config""" # Add entries for saved profiles for prof in self.conf.profiles: profile = self.conf.get_profile(prof) prof_entry = Handy.ActionRow() prof_entry.set_title(prof) if profile._custom: if not profile.system: delete_me = Gtk.Button.new_from_icon_name("edit-delete", 1) prof_entry.add(delete_me) delete_me.connect("clicked", self.on_delete_profile_clicked, prof) prof_entry.set_activatable_widget(delete_me) self.custom_profiles.add(prof_entry) else: self.system_profiles.add(prof_entry) else: self.builtin_profiles.add(prof_entry) self.profile_overview.show_all() def quit(self, *args): """Quit""" HELPER.quit() exit(0) def _reset_energy_conf(self, cpu): if cpu == -1: # Reset conf for all cpus for cpu, conf in self.settings.items(): conf.reset_energy_pref() self._update_treeview_style(cpu, conf.changed) return self.settings[cpu].reset_energy_pref() def _update_settings_freqs(self, cpu, fmin, fmax): """Updates settings frequency values for `cpu` Args: cpu: Index of cpu to update fmin: Minimum scaling frequency fmax: Minimum scaling frequency """ if self.toall.get_active(): for cpu, row in enumerate(self.tree_store): conf = self.settings.get(cpu) if conf is not None: conf.freqs = (fmin, fmax) row[2] = fmin row[3] = fmax self._update_treeview_style(cpu, conf.changed) else: conf = self.settings.get(cpu) if conf is not None: conf.freqs = (fmin, fmax) self.tree_store[cpu][2] = fmin self.tree_store[cpu][3] = fmax self._update_treeview_style(cpu, conf.changed) def _update_settings_online(self, cpu, online): """Updates settings online value for `cpu` Args: cpu: Index of cpu to update online: Boolean indication if cpu is online or offline """ conf = self.settings.get(cpu) if conf is not None: conf.online = online self._update_treeview_style(cpu, conf.changed) def _update_treeview_style(self, cpu, changed): """Change style on the tree view if cpu settings were changed Args: cpu: Index of cpu to update changed: If changed is True, cpu text will be coloured red, otherwise the text will be black """ style = 1 if changed else 0 if cpu <= len(self.settings): if self.tree_store: self.tree_store[cpu][6] = style def upd_sliders(self): """Updates the slider widgets by reading the sys files""" cpu = self._get_active_cpu() conf = self.settings.get(cpu) if not conf: return freq_min_hw, freq_max_hw = conf.hw_lims cpu_online = conf.online freq_min, freq_max = conf.freqs with self.lock(): # Use the flag to skip callbacks self._update_gov_box() if self.energy_pref_avail: self._update_energy_pref_box() # Update sliders self._set_sliders_sensitive(cpu_online) self._update_frequency_marks(cpu) self.adj_min.set_lower(freq_min_hw) self.adj_min.set_upper(freq_max_hw) self.adj_max.set_lower(freq_min_hw) self.adj_max.set_upper(freq_max_hw) self.adj_min.set_value(freq_min) self.adj_max.set_value(freq_max) self.apply_btn.set_sensitive(self.is_conf_changed) self.cpu_online.set_active(cpu_online) self.cpu_online.set_sensitive(bool( HELPER.cpu_allowed_offline(cpu))) self._update_treeview_style(cpu, conf.changed) def _get_active_cpu(self): """Helper function to get cpu from combobox""" # cpu_iter = self.cpu_box.get_active_iter() index = self.cpu_box.get_selected_index() if index != -1: return index # if cpu_iter is not None: # return int(self.cpu_store[cpu_iter][0]) # self.update_cpubox() return 0 def _set_profile_settings(self, profile): """Set the settings based on the selected profile""" self.load_cpu_settings() # Discard any changes # If no profile selected reset and disable apply_btn if profile.name == _("No profile"): self.toall.set_sensitive(True) for cpu, conf in self.settings.items(): conf.reset_conf() self.update_tree_view(cpu, conf) return False self.toall.set_active(False) # Turn off toggle self.toall.set_sensitive(False) # Disable toggle prof_settings = profile.settings for cpu, settings in prof_settings.items(): conf = self.settings.get(cpu) if not conf: continue conf.freqs_scaled = settings["freqs"] if settings["governor"] in conf.governors: conf.governor = settings["governor"] conf.online = settings["online"] self.update_tree_view(cpu, conf) self._update_treeview_style(cpu, conf.changed) return True def _set_sliders_sensitive(self, state): """Enable/Disable sliders and combo boxes""" self.spin_min.set_sensitive(state) self.spin_max.set_sensitive(state) self.min_sl.set_sensitive(state) self.max_sl.set_sensitive(state) self.gov_box.set_sensitive(state) self.energy_pref_box.set_sensitive(state) def _update_frequency_marks(self, cpu): """Add or remove slider marks for frequency steps Args: cpu: Index of cpu to query """ # Clear marks self.min_sl.clear_marks() self.max_sl.clear_marks() if not self.tick_marks_enabled: return steps = self.get_cpu_frequency_steps(cpu) if not steps: minf = self.adj_min.get_lower() maxf = self.adj_min.get_upper() steps = [minf, maxf - (maxf - minf) / 2, maxf] markup = "{:1.2f}" for frequency in steps: freq = float(frequency / 1e3) mark = markup.format(freq) if self.ticks_markup: self.min_sl.add_mark(frequency, Gtk.PositionType.TOP, mark) else: self.min_sl.add_mark(frequency, Gtk.PositionType.TOP) self.max_sl.add_mark(frequency, Gtk.PositionType.TOP) def _update_energy_pref_box(self): """Updates the energy performance combobox""" cpu = self._get_active_cpu() conf = self.settings.get(cpu) energy_pref = conf.energy_pref_id energy_prefs = conf.energy_prefs if energy_pref != -1: pref_store = Gio.ListStore() for prefid, pref in enumerate(energy_prefs): if "_" in pref: pref = pref.replace("_", " ") pref_store.append(EnergyPref(prefid, pref.capitalize())) self.energy_pref_box.bind_name_model(pref_store, lambda x: x.name) self.energy_pref_box.set_selected_index(energy_pref) self.energy_pref_box.set_sensitive(True) else: self.energy_pref_box.set_sensitive(False) def _update_gov_box(self): """Updates the governor combobox""" cpu = self._get_active_cpu() conf = self.settings.get(cpu) governor = conf.govid governors = conf.governors if governor is not None: gov_store = Gio.ListStore() for govid, gov in enumerate(governors): gov_store.append(Governor(govid, gov.capitalize())) self.gov_box.bind_name_model(gov_store, lambda x: x.name) self.gov_box.set_selected_index(governor) self.gov_box.set_sensitive(True) else: self.gov_box.set_sensitive(False) def _update_current_freq(self): """Callback to update the tree view with current CPU frequency""" for cpu in self.online_cpus: current_freq = read_current_freq(cpu) / 1e3 self.tree_store[cpu][5] = current_freq return True def on_freq_edited(self, widget, path, value, index): """Update the sliders when frequencies change from table""" value = float(value) cpu = int(path) conf = self.settings[cpu] fmin, fmax = conf.freqs if cpu == self._get_active_cpu(): if index == 2: self.adj_min.set_value(value) else: self.adj_max.set_value(value) else: if index == 2: conf.freqs = value, fmax else: conf.freqs = fmin, value def on_tree_toggled(self, widget, path): """Update online cpu toggle""" # check if it can be disabled allowed = bool(HELPER.cpu_allowed_offline(int(path))) if not allowed: return online = not self.tree_store[path][1] self.tree_store[path][1] = online self.settings[int(path)].online = online if int(path) == self._get_active_cpu(): self.cpu_online.set_active(online) def _update_tree_view(self): """Updates the tree view""" self.tree_store = Gtk.ListStore(int, bool, float, float, str, float, int) for cpu, conf in self.settings.items(): fmin, fmax = conf.freqs self.tree_store.append([ cpu, conf.online, fmin, fmax, conf.governor.capitalize(), 0.0, 0 ]) for i, column_title in enumerate([ _("CPU"), _("Online"), _("Min"), _("Max"), _("Governor"), _("Current freq."), ]): if column_title == _("Online"): renderer = Gtk.CellRendererToggle() renderer.connect("toggled", self.on_tree_toggled) column = Gtk.TreeViewColumn(column_title, renderer, active=i) elif column_title == _("Current freq."): renderer = Gtk.CellRendererSpin(digits=2) column = Gtk.TreeViewColumn(column_title, renderer, text=i, style=6) column.set_cell_data_func(renderer, self.conv_float, 5) elif column_title in [_("Min"), _("Max")]: index = 2 if column_title == _("Min") else 3 adj = Gtk.Adjustment( value=0, lower=fmin, upper=fmax, step_increment=10, page_increment=50, page_size=0, ) renderer = Gtk.CellRendererSpin(editable=True, adjustment=adj, digits=2) renderer.connect("edited", self.on_freq_edited, index) column = Gtk.TreeViewColumn(column_title, renderer, text=i, style=6) column.set_cell_data_func(renderer, self.conv_float, index) else: renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(column_title, renderer, text=i, style=6) self.tree_view.append_column(column) self.tree_view.set_model(self.tree_store) def update_tree_view(self, cpu, conf): """Update values inside the tree view""" if cpu == -1: for row in self.tree_store: row[2], row[3] = conf.freqs row[1] = conf.online row[4] = conf.governor.capitalize() else: row = self.tree_store[cpu] row[2], row[3] = conf.freqs row[1] = conf.online row[4] = conf.governor.capitalize() @Gtk.Template.Callback() def on_tree_selection(self, selection): model, treeiter = selection.get_selected() if treeiter is not None: self.cpu_box.set_selected_index(model[treeiter][0]) @Gtk.Template.Callback() def on_prefs_changed(self, pref, value): name = Gtk.Buildable.get_name(pref) value_str = str(value) if name == "default_profile_pref": mod = pref.get_model() ind = self.default_profile_pref.get_selected_index() self.conf.set("Profile", "profile", mod[ind].name) elif name == "default_allcpus": self.gui_conf["allcpus_default"] = value_str elif name == "default_ticks": self.tick_marks_enabled = value self.gui_conf["tick_marks_enabled"] = value_str self._update_frequency_marks(self._get_active_cpu()) elif name == "default_ticks_num": self.ticks_markup = value self.gui_conf["frequency_ticks"] = value_str self._update_frequency_marks(self._get_active_cpu()) elif name == "default_energy_per_cpu": self.gui_conf["energy_pref_per_cpu"] = value_str self.energy_per_cpu = value self.conf.write_settings() @Gtk.Template.Callback() def on_cpu_changed(self, widget, value): """Callback for cpu box""" # pylint: disable=W0612,W0613 selection = self.tree_view.get_selection() index = widget.get_selected_index() if index > -1: selection.select_path(index) self.upd_sliders() @Gtk.Template.Callback() def on_toall_toggled(self, widget): """Enable/Disable cpu_box""" cpu = self._get_active_cpu() settings = self.settings[cpu] if widget.get_active(): for cpu, conf in self.settings.items(): conf.freqs = settings.freqs conf.governor = settings.governor conf.online = settings.online conf.energy_pref = settings.energy_pref self.update_tree_view(cpu, conf) self.apply_btn.set_sensitive(self.is_conf_changed) @Gtk.Template.Callback() def on_adj_min_value_changed(self, *args): """Callback for adj_min""" # pylint: disable=W0612,W0613 if self.refreshing: return cpu = self._get_active_cpu() fmin = self.adj_min.get_value() fmax = self.adj_max.get_value() fmin_hw, fmax_hw = self.settings[cpu].hw_lims if fmin > fmax: if fmin + 10 > fmax_hw: fmax = fmax_hw else: fmax = fmin + 10 elif fmax < fmin: if fmax - 10 < fmin_hw: fmin = fmin_hw else: fmin = fmax - 10 with self.lock(): self.adj_min.set_value(fmin) self.adj_max.set_value(fmax) self._update_settings_freqs(cpu, fmin, fmax) self.apply_btn.set_sensitive(self.is_conf_changed) @Gtk.Template.Callback() def on_adj_max_value_changed(self, *args): """Callback for adj_max""" # pylint: disable=W0612,W0613 if self.refreshing: return cpu = self._get_active_cpu() fmin = self.adj_min.get_value() fmax = self.adj_max.get_value() if fmax < fmin: fmin = fmax - 10 with self.lock(): self.adj_min.set_value(fmin) self._update_settings_freqs(cpu, fmin, fmax) self.apply_btn.set_sensitive(self.is_conf_changed) @Gtk.Template.Callback() def on_refresh_clicked(self, *args): """Callback for refresh button""" if self.toall.get_active(): for cpu in self.settings.keys(): self._refresh_cpu_settings(cpu) else: cpu = self._get_active_cpu() self._refresh_cpu_settings(cpu) def _refresh_cpu_settings(self, cpu): self.settings[cpu].update_conf() self.update_tree_view(cpu, self.settings[cpu]) if self.energy_pref_avail: if not self.energy_per_cpu: self._reset_energy_conf(-1) else: self._reset_energy_conf(cpu) self.upd_sliders() def on_cpu_online_toggled(self, *args): """Callback for cpu_online toggle""" if self.refreshing: return cpu = self._get_active_cpu() conf = self.settings[cpu] conf.online = self.cpu_online.get_active() self._set_sliders_sensitive(conf.online) self.apply_btn.set_sensitive(self.is_conf_changed) self.tree_store[cpu][1] = conf.online self._update_treeview_style(cpu, conf.changed) @Gtk.Template.Callback() def on_governor_changed(self, *args): """Callback for governor combobox Change governor and enable apply_btn """ # pylint: disable=W0612,W0613 if self.refreshing: return mod = self.gov_box.get_model() gov = mod[self.gov_box.get_selected_index()] # Update store if self.toall.get_active(): for cpu, row in enumerate(self.tree_store): conf = self.settings.get(cpu) conf.governor = gov.govid row[4] = gov.name self._update_treeview_style(cpu, conf.changed) else: cpu = self._get_active_cpu() conf = self.settings.get(cpu) conf.governor = gov.govid self.tree_store[cpu][4] = gov.name self._update_treeview_style(cpu, conf.changed) self.apply_btn.set_sensitive(self.is_conf_changed) @Gtk.Template.Callback() def on_energy_pref_box_changed(self, *args): """Callback for energy combobox Change energy pref and enable apply_btn """ # pylint: disable=W0612,W0613 if self.refreshing: return mod = self.energy_pref_box.get_model() energy_pref = mod[self.energy_pref_box.get_selected_index()] active_cpu = self._get_active_cpu() # Note that tasks may by migrated from one CPU to another by the scheduler’s # load-balancing algorithm and if different energy vs performance hints are # set for those CPUs, that may lead to undesirable outcomes. # To avoid such issues it is better to set the same energy vs performance hint # for all CPUs or to pin every task potentially sensitive to them # to a specific CPU. # https://www.kernel.org/doc/html/v4.12/admin-guide/pm/intel_pstate.html#energy-vs-performance-hints error = False if self.energy_per_cpu: conf = self.settings.get(active_cpu) if conf.governor == "performance" and energy_pref.name != "Performance": error = True with self.lock(): self.energy_pref_box.set_selected_index( conf.energy_pref_id) else: conf.energy_pref = energy_pref.prefid self._update_treeview_style(active_cpu, conf.changed) else: for cpu, conf in self.settings.items(): if conf.governor == "performance" and energy_pref.name != "Performance": error = True if cpu == active_cpu: with self.lock(): self.energy_pref_box.set_selected_index( conf.energy_pref_id) continue conf.energy_pref = energy_pref.prefid self._update_treeview_style(cpu, conf.changed) if error: error_message( _("Energy profiles other than performance are not allowed when the governor is set to performance." ), self, ) self.apply_btn.set_sensitive(self.is_conf_changed) @Gtk.Template.Callback() def on_profile_changed(self, *args): """Callback for profile combobox Change profile and enable apply_btn """ # pylint: disable=W0612,W0613 if self.refreshing: return mod = self.profile_box.get_model() profile = mod[self.profile_box.get_selected_index()] # Update store self.profile = profile.name self._set_profile_settings(profile) self.upd_sliders() self.apply_btn.set_sensitive(self.is_conf_changed) @Gtk.Template.Callback() def on_apply_clicked(self, button): """Write changes back to sysfs""" ret = -1 cpu = self._get_active_cpu() conf = self.settings[cpu] fmin, fmax = conf.freqs_scaled gov = conf.governor pref = conf.energy_pref if not HELPER.isauthorized(): error_message( _("You don't have permissions to update cpu settings!"), self) # Update only the cpus whose settings were changed changed_cpus = [ cpu for cpu, conf in self.settings.items() if conf.changed ] for cpu in changed_cpus: conf = self.settings.get(cpu) cpu_online = conf.online ret = self.set_cpu_online(cpu) if cpu_online: if conf.setting_changed("freqs"): ret += self.set_cpu_frequencies(cpu) if conf.setting_changed("governor"): ret += self.set_cpu_governor(cpu) if conf.setting_changed("energy_pref"): ret += self.set_cpu_energy_preferences(cpu) for cpu in self.settings.keys(): self._refresh_cpu_settings(cpu) # Update sliders self.profile_box.set_selected_index(0) self.load_cpu_settings() self.upd_sliders() if ret == 0: button.set_sensitive(False) else: error = ERRORS[ret] error_message(error, self) def on_prof_name_changed(self, entry, button): """Checks if there is text in entry""" if self.profile_name_entry.get_text() != "": button.set_sensitive(True) else: button.set_sensitive(False) def on_save_profile_clicked(self, button): """Callback saving the profile""" name = self.profile_name_entry.get_text().strip() self.conf.create_profile_from_settings(name, self.settings) self.profiles = self.conf.profiles self.update_profile_boxes() self.update_profiles_page() self.profile_name_entry.set_text("") def on_delete_profile_clicked(self, button, profile): """Callback for about""" message = Gtk.MessageDialog( transient_for=self, type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.OK_CANCEL, text=_("Are you sure sure you want to delete this profile?"), ) response = message.run() if response == Gtk.ResponseType.OK: self.conf.delete_profile(profile) self.profiles = self.conf.profiles self.update_profile_boxes() self.update_profiles_page() message.destroy() @Gtk.Template.Callback() def on_about_clicked(self, button): """Callback for about""" self.show_about_dialog() def show_about_dialog(self): """Shows the about dialog""" self.about_dialog.run() self.about_dialog.hide() @property def online_cpus(self): """Convenience function to get a list of available CPUs""" avail = HELPER.get_cpus_available() if avail: return [int(cpu) for cpu in avail] return avail @property def is_conf_changed(self): """Helper function to check if settings were changed""" changed = [cpu for cpu, conf in self.settings.items() if conf.changed] return len(changed) > 0 @staticmethod def is_online(cpu): """Wrapper to get the online state for a cpu Args: cpu: Index of cpu to query Returns: bool: True if cpu is online, false otherwise """ online = HELPER.get_cpus_online() present = HELPER.get_cpus_present() return (cpu in present) and (cpu in online) @staticmethod def is_offline(cpu): """Wrapper to get the online state for a cpu Args: cpu: Index of cpu to query Returns: bool: True if cpu is offline, false otherwise """ offline = HELPER.get_cpus_offline() present = HELPER.get_cpus_present() return (cpu in present) and (cpu in offline) @staticmethod def get_cpu_governors(cpu): """Wrapper to get the list of available governors for a cpu Args: cpu: Index of cpu to query """ governors = [] for gov in HELPER.get_cpu_governors(cpu): governors.append(str(gov)) return governors @staticmethod def get_cpu_frequency_steps(cpu): """Wrapper to get the list of available frequencies Args: cpu: Index of cpu to query """ frequencies = read_available_frequencies(cpu) if not frequencies: return [] # Convert and scale frequencies to MHz return [int(freq) / 1e3 for freq in frequencies] @staticmethod def conv_float(col, cell, model, treeiter, data): """Helper function to convert float to string with 2 decimal digits""" val = model.get(treeiter, data)[0] cell.set_property("text", "{:.2f}".format(val)) def set_cpu_online(self, cpu): """Sets the online attribute for cpu Args: cpu: Index of cpu to set """ conf = self.settings.get(cpu) if conf is None: return cpu_online = conf.online # If cpu is offline, enable and update freq settings if self.is_offline(cpu) and cpu_online: ret = HELPER.set_cpu_online(cpu) self.upd_sliders() return ret # If cpu is online, disable if self.is_online(cpu) and not cpu_online: # Set offline only if CPU is allowed to go offline if HELPER.cpu_allowed_offline(cpu): return HELPER.set_cpu_offline(cpu) # No change to the cpu return 0 def set_cpu_governor(self, cpu): """Sets the governor for cpu Args: cpu: Index of cpu to set """ ret = -1 conf = self.settings.get(cpu) if conf is None: return ret gov = conf.governor # If govid is None means that there is an error with the kernel # https://github.com/vagnum08/cpupower-gui/issues/12 if gov is None: return ret ret = HELPER.update_cpu_governor(cpu, gov) if ret != 0: return -11 return ret def set_cpu_energy_preferences(self, cpu): """Sets the energy performance preference for cpu Args: cpu: Index of cpu to set """ # Return success if not available if not self.energy_pref_avail: return 0 ret = -1 conf = self.settings.get(cpu) if conf is None: return ret pref = conf.energy_pref if not pref: return ret ret = HELPER.update_cpu_energy_prefs(cpu, pref) if ret != 0: return -12 return ret def set_cpu_frequencies(self, cpu): """Sets the frequency limits for cpu Args: cpu: Index of cpu to set """ ret = -1 conf = self.settings.get(cpu) if conf is None: return ret fmin, fmax = conf.freqs_scaled if (fmin is not None) and (fmax is not None): ret = HELPER.update_cpu_settings(cpu, fmin, fmax) if ret != 0: return -13 return ret
class HeaderBar(Handy.HeaderBar): __gtype_name__ = 'HeaderBar' __gsignals__ = { 'tarajem-toggled': (GObject.SIGNAL_RUN_CLEANUP, None, ()), 'mobileview-toggled': (GObject.SIGNAL_RUN_CLEANUP, None, ())} Handy.init() squeezer = Gtk.Template.Child() headerbar_switcher = Gtk.Template.Child() button_open_search = Gtk.Template.Child() button_open_tarajem = Gtk.Template.Child() button_tarajem_option = Gtk.Template.Child() button_telaawa_playback = Gtk.Template.Child() icon_telaawa_playback = Gtk.Template.Child() button_telaawa_option = Gtk.Template.Child() button_open_mainmenu = Gtk.Template.Child() window_title = Gtk.Template.Child() window_title_alt = Gtk.Template.Child() button_open_navigation = Gtk.Template.Child() button_open_navigation_alt = Gtk.Template.Child() is_updating: bool = False # to prevent unwanted widget triggering def __init__( self, **kwargs) -> None: super().__init__(**kwargs) child = self.squeezer.get_visible_child() glob.mobile_view = child != self.headerbar_switcher self.setup_tarajem_popover() self.setup_navigation_popover() self.setup_telaawa_popover() self.setup_main_menu() # Watch the squeezer when it starts to hide some of its children self.squeezer.connect('notify::visible-child', self.on_squeezed) def setup_tarajem_popover(self) -> None: self.popover_tarajem = TarajemPopover() self.button_tarajem_option.set_popover(self.popover_tarajem) def setup_navigation_popover(self) -> None: self.popover_nav = NavigationPopover(0) self.button_open_navigation.set_popover(self.popover_nav) self.popover_nav_alt = NavigationPopover(1) self.popover_nav_alt.container.set_orientation( Gtk.Orientation.VERTICAL) self.popover_nav_alt.container.set_spacing(8) self.button_open_navigation_alt.set_popover(self.popover_nav_alt) # Whenever focused ayah changed, change the window title to the newly # selected ayah self.popover_nav.connect('change-win-title', self.change_title) self.popover_nav_alt.connect('change-win-title', self.change_title) def setup_telaawa_popover(self) -> None: self.popover_telaawa = TelaawaPopover() self.button_telaawa_option.set_popover(self.popover_telaawa) self.popover_telaawa.connect('telaawa-playback', self.telaawa_toggled) def setup_main_menu(self) -> None: self.popover_menu = MainMenu() self.button_open_mainmenu.set_popover(self.popover_menu) @Gtk.Template.Callback() def toggle_tarajem( self, button: Gtk.ToggleButton) -> None: glob.tarajem_visibility = button.get_active() self.emit('tarajem-toggled') @Gtk.Template.Callback() def toggle_telaawa( self, button: Gtk.ToggleButton) -> None: if self.is_updating: return is_activated = button.get_active() if is_activated: self.icon_telaawa_playback.set_from_icon_name( 'media-playback-pause-symbolic', Gtk.IconSize.BUTTON) else: self.icon_telaawa_playback.set_from_icon_name( 'media-playback-start-symbolic', Gtk.IconSize.BUTTON) button_playback_telaawa = self.popover_telaawa.button_toggle_playback button_playback_telaawa.set_active(is_activated) def telaawa_toggled( self, button: Gtk.ToggleButton, state: bool) -> None: self.is_updating = True self.button_telaawa_playback.set_active(state) self.is_updating = False if state: self.icon_telaawa_playback.set_from_icon_name( 'media-playback-pause-symbolic', Gtk.IconSize.BUTTON) else: self.icon_telaawa_playback.set_from_icon_name( 'media-playback-start-symbolic', Gtk.IconSize.BUTTON) def on_squeezed( self, squeezer: Handy.Squeezer, event: Gdk.Event) -> None: child = squeezer.get_visible_child() glob.mobile_view = child != self.headerbar_switcher self.emit('mobileview-toggled') def change_title( self, widget: Gtk.Widget, title: str) -> None: if not glob.mobile_view: self.window_title.set_text(title) else: self.window_title_alt.set_text(title)