def build_bar(): icon_theme = Gtk.IconTheme.get_default() orientation = Gtk.Orientation.VERTICAL if args.vertical else Gtk.Orientation.HORIZONTAL box = Gtk.Box(orientation=orientation) box.set_property("name", "bar") appendix = load_json(build_from_file) for entry in appendix: name = entry["name"] exec = entry["exec"] icon = entry["icon"] image = None if icon.startswith('/'): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon, args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(config_dir, 'icon-missing.svg'), args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) else: try: if icon.endswith('.svg') or icon.endswith('.png'): icon = entry.icon.split('.')[0] pixbuf = icon_theme.load_icon(icon, args.s, Gtk.IconLookupFlags.FORCE_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(config_dir, 'icon-missing.svg'), args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) button = Gtk.Button() button.set_property("name", "button") button.set_always_show_image(True) button.set_image(image) button.set_image_position(Gtk.PositionType.TOP) button.set_label(name) button.set_property("width_request", args.bw) button.set_property("height_request", args.bh) button.connect('clicked', launch, exec) box.pack_start(button, False, False, int(args.p / 2)) return box
def build_menu(): icon_theme = Gtk.IconTheme.get_default() menu = Gtk.Menu() if not args.no_menu: win.search_item = Gtk.MenuItem() win.search_item.add(win.search_box) win.search_item.set_sensitive(False) menu.add(win.search_item) # Prepend favourite items (-f or -fn argument used) favs_number = 0 if args.favourites: favs_number = 5 elif args.fn: favs_number = args.fn if favs_number > 0: global sorted_cache if len(sorted_cache) < favs_number: favs_number = len(sorted_cache) to_prepend = [] # list of favourite items for i in range(favs_number): fav_exec = sorted_cache[i][0] for item in all_entries: if item.exec == fav_exec and item not in to_prepend: to_prepend.append(item) break # stop searching, there may be duplicates on the list # build menu items for entry in to_prepend: name = entry.name exec = entry.exec icon = entry.icon hbox = Gtk.HBox() label = Gtk.Label() label.set_text(name) if icon.startswith('/'): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon, args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(config_dir, 'icon-missing.svg'), args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) else: try: if icon.endswith('.svg') or icon.endswith('.png'): icon = entry.icon.split('.')[0] pixbuf = icon_theme.load_icon(icon, args.s, Gtk.IconLookupFlags.FORCE_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(config_dir, 'icon-missing.svg'), args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) if image: hbox.pack_start(image, False, False, 10) if name: hbox.pack_start(label, False, False, 0) item = Gtk.MenuItem() item.set_property("name", "item") item.add(hbox) item.connect('activate', launch, exec) menu.append(item) if to_prepend: separator = Gtk.SeparatorMenuItem() separator.set_property("name", "separator") menu.append(separator) # actual system menu with submenus for each category if c_audio_video: append_submenu(c_audio_video, menu, 'AudioVideo') if c_development: append_submenu(c_development, menu, 'Development') if c_game: append_submenu(c_game, menu, 'Game') if c_graphics: append_submenu(c_graphics, menu, 'Graphics') if c_network: append_submenu(c_network, menu, 'Network') if c_office: append_submenu(c_office, menu, 'Office') if c_science: append_submenu(c_science, menu, 'Science') if c_settings: append_submenu(c_settings, menu, 'Settings') if c_system: append_submenu(c_system, menu, 'System') if c_utility: append_submenu(c_utility, menu, 'Utility') if c_other: append_submenu(c_other, menu, 'Other') # user-defined menu from default or custom file (see args) if args.append or args.af or args.no_menu or pipe_menu: if not args.no_menu: # nothing above to separate separator = Gtk.SeparatorMenuItem() separator.set_property("name", "separator") menu.append(separator) if pipe_menu: try: appendix = json.loads(''.join(pipe_menu)) except: appendix = None else: appendix = load_json(build_from_file) if appendix: for entry in appendix: try: name = entry["name"] except KeyError: name = "No name given" try: exec = entry["exec"] except KeyError: exec = '' try: icon = entry["icon"] except KeyError: icon = None hbox = Gtk.HBox() label = Gtk.Label() label.set_text(name) if icon: if icon.startswith('/'): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon, args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(config_dir, 'icon-missing.svg'), args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) else: try: if icon.endswith('.svg') or icon.endswith('.png'): icon = entry.icon.split('.')[0] pixbuf = icon_theme.load_icon(icon, args.s, Gtk.IconLookupFlags.FORCE_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(os.path.join(config_dir, 'icon-missing.svg'), args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) else: image = None if image: hbox.pack_start(image, False, False, 10) if name: hbox.pack_start(label, False, False, 0) item = Gtk.MenuItem() item.set_property("name", "item") item.add(hbox) item.connect('activate', launch, exec, True) # do not cache! menu.append(item) menu.connect("hide", win.die) menu.set_property("reserve_toggle_size", False) menu.show_all() return menu
def main(): # exit if already running, thanks to Slava V at https://stackoverflow.com/a/384493/4040598 pid_file = os.path.join(tempfile.gettempdir(), 'sgtk-menu.pid') fp = open(pid_file, 'w') try: fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: subprocess.run("pkill -f sgtk-menu", shell=True) sys.exit(2) global build_from_file if not sys.stdin.isatty(): global pipe_menu pipe_menu = [] for line in sys.stdin: pipe_menu.append(line.rstrip()) parser = argparse.ArgumentParser(description="GTK menu for sway, i3 and some other WMs") placement = parser.add_mutually_exclusive_group() placement.add_argument("-b", "--bottom", action="store_true", help="display menu at the bottom") placement.add_argument("-c", "--center", action="store_true", help="center menu on the screen") placement.add_argument("-p", "--pointer", action="store_true", help="display at mouse pointer (not-sway only)") favourites = parser.add_mutually_exclusive_group() favourites.add_argument("-f", "--favourites", action="store_true", help="prepend 5 most used items") favourites.add_argument('-fn', type=int, help="prepend <FN> most used items") appendix = parser.add_mutually_exclusive_group() appendix.add_argument("-a", "--append", action="store_true", help="append custom menu from {}".format(build_from_file)) appendix.add_argument("-af", type=str, help="append custom menu from {}".format(os.path.join(config_dir, '<AF>'))) parser.add_argument("-n", "--no-menu", action="store_true", help="skip menu, display appendix only") parser.add_argument("-l", type=str, help="force language (e.g. \"de\" for German)") parser.add_argument("-s", type=int, default=20, help="menu icon size (min: 16, max: 48, default: 20)") parser.add_argument("-w", type=int, help="menu width in px (integer, default: screen width / 8)") parser.add_argument("-d", type=int, default=100, help="menu delay in milliseconds (default: 100; sway & i3 only)") parser.add_argument("-o", type=float, default=0.3, help="overlay opacity (min: 0.0, max: 1.0, default: 0.3; sway only)") parser.add_argument("-t", type=int, default=30, help="sway submenu lines limit (default: 30)") parser.add_argument("-y", type=int, default=0, help="y offset from edge to display menu at") parser.add_argument("-css", type=str, default="style.css", help="use alternative {} style sheet instead of style.css" .format(os.path.join(config_dir, '<CSS>'))) parser.add_argument("-v", "--version", action="store_true", help="display version and exit") parser.add_argument("-wm", action="store_true", help="display detected Window Manager and exit") global args args = parser.parse_args() if args.version: print_version() sys.exit(0) if args.wm: print(wm) sys.exit(0) if pipe_menu: args.no_menu = True if not wm == "sway" and not args.d == 100: args.d = 0 print("[-d] argument ignored if not-sway") # Create default config files if not found create_default_configs(config_dir) css_file = os.path.join(config_dirs()[0], args.css) if os.path.exists( os.path.join(config_dirs()[0], 'style.css')) else None if args.s < 16: args.s = 16 elif args.s > 48: args.s = 48 # Replace appendix file name with custom - if any if args.af: build_from_file = os.path.join(config_dirs()[0], args.af) if css_file: screen = Gdk.Screen.get_default() provider = Gtk.CssProvider() try: provider.load_from_path(css_file) Gtk.StyleContext.add_provider_for_screen( screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) except Exception as e: print(e) # cache stores number of clicks on each item global cache cache = load_json(cache_file) if not cache: save_json(cache, cache_file) global sorted_cache sorted_cache = sorted(cache.items(), reverse=True, key=lambda x: x[1]) global locale locale = get_locale_string(args.l) category_names_dictionary = localized_category_names(locale) # replace additional category names with main ones for name in category_names: main_category_name = additional_to_main(name) try: localized_names_dictionary[main_category_name] = category_names_dictionary[main_category_name] except: pass screen = Gdk.Screen.get_default() provider = Gtk.CssProvider() style_context = Gtk.StyleContext() style_context.add_provider_for_screen( screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) # find all .desktop entries, create DesktopEntry class instances; # DesktopEntry adds itself to the proper List in the class constructor list_entries() # Overlay window global win win = MainWindow() geometry = (0, 0, 0, 0) # If we're not on sway neither i3, this won't return values until the window actually shows up. # Let's try as many times as needed. The retries int protects from an infinite loop. retries = 0 while geometry[0] == 0 and geometry[1] == 0 and geometry[2] == 0 and geometry[3] == 0: geometry = display_geometry(win, wm, mouse_pointer) retries += 1 if retries > 50: print("\nFailed to get the current screen geometry, exiting...\n") sys.exit(2) x, y, w, h = geometry if wm == "sway": # resize to current screen dimensions on sway win.resize(w, h) else: win.resize(0, 0) if args.center: x = x + (w // 2) y = y + (h // 2) elif args.bottom: y = h - args.y elif args.pointer: if mouse_pointer: x, y = mouse_pointer.position else: print("\nYou need the python-pynput package!\n") else: y = y + args.y win.move(x, y) win.menu = build_menu() win.menu.set_property("name", "menu") global menu_items_list menu_items_list = win.menu.get_children() win.menu.propagate_key_event = False win.menu.connect("key-release-event", win.search_items) # Let's reserve some width for long entries found with the search box if args.w: win.menu.set_property("width_request", args.w) else: win.menu.set_property("width_request", int(win.screen_dimensions[0] / 8)) win.show_all() GLib.timeout_add(args.d, open_menu) Gtk.main()
def main(): # exit if already running, thanks to Slava V at https://stackoverflow.com/a/384493/4040598 pid_file = os.path.join(tempfile.gettempdir(), 'sgtk-grid.pid') fp = open(pid_file, 'w') try: fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: subprocess.run("pkill -f sgtk-grid", shell=True) sys.exit(2) global build_from_file parser = argparse.ArgumentParser( description="Application grid for sgtk-menu") parser.add_argument( '-d', type=str, default="", help="use alternate folder list (: delimited) for .Desktop files") parser.add_argument('-c', type=int, default=6, help="number of grid columns (default: 6)") parser.add_argument('-t', type=int, default=30, help="top margin width in px (default: 30)") parser.add_argument('-b', type=int, default=15, help="bottom margin width in px (default: 15)") favourites = parser.add_mutually_exclusive_group() favourites.add_argument( "-f", action="store_true", help="prepend 1 row of favourites (most used items)") favourites.add_argument('-fn', default=0, type=int, help="prepend <FN> rows of favourites") parser.add_argument("-l", type=str, help="force language (e.g. \"de\" for German)") parser.add_argument("-s", type=int, default=72, help="menu icon size (min: 16, max: 96, default: 72)") parser.add_argument( "-o", type=float, default=0.9, help="overlay opacity (min: 0.0, max: 1.0, default: 0.9)") parser.add_argument( "-css", type=str, default="grid.css", help="use alternative {} style sheet instead of grid.css".format( os.path.join(config_dir, '<CSS>'))) global args args = parser.parse_args() # Create default config files if not found create_default_configs(config_dir) css_file = os.path.join(config_dirs()[0], args.css) if os.path.exists( os.path.join(config_dirs()[0], 'style.css')) else None if args.s < 16: args.s = 16 elif args.s > 96: args.s = 96 if css_file: screen = Gdk.Screen.get_default() provider = Gtk.CssProvider() try: provider.load_from_path(css_file) Gtk.StyleContext.add_provider_for_screen( screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) except Exception as e: print(e) # cache stores number of clicks on each item global cache cache = load_json(cache_file) if not cache: save_json(cache, cache_file) global sorted_cache sorted_cache = sorted(cache.items(), reverse=True, key=lambda x: x[1]) global locale locale = get_locale_string(args.l) screen = Gdk.Screen.get_default() provider = Gtk.CssProvider() style_context = Gtk.StyleContext() style_context.add_provider_for_screen( screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) # find all .desktop entries, create AppButton class instances; list_entries() # find favourites in the list above if args.f or args.fn > 0: list_favs() # Overlay window global win win = MainWindow() geometry = (0, 0, 0, 0) # If we're not on sway neither i3, this won't return values until the window actually shows up. # Let's try as many times as needed. The retries int protects from an infinite loop. retries = 0 while geometry[0] == 0 and geometry[1] == 0 and geometry[ 2] == 0 and geometry[3] == 0: geometry = display_geometry(win, wm, mouse_pointer) retries += 1 if retries > 50: print("\nFailed to get the current screen geometry, exiting...\n") sys.exit(2) x, y, w, h = geometry if wm == "sway": win.resize(w, h) # align width of all buttons max_width = 0 for item in all_apps: width = item.get_allocated_width() if width > max_width: max_width = width for item in all_favs: item.set_size_request(max_width, max_width / 2) for item in all_apps: item.set_size_request(max_width, max_width / 2) win.search_box.set_size_request(max_width, 0) if all_favs: win.sep1.set_size_request(w / 3, 1) win.show_all() # If done inside the constructor on Openbox, stops the window from grabbing focus! win.set_skip_taskbar_hint(True) # Necessary in FVWM, otherwise it always gets on screen 0 win.move(x, y) Gtk.main()
def build_menu(commands): icon_theme = Gtk.IconTheme.get_default() menu = Gtk.Menu() win.search_item = Gtk.MenuItem() win.search_item.add(win.search_box) win.search_item.set_sensitive(False) menu.add(win.search_item) # actual drun menu for command in commands: item = Gtk.MenuItem.new_with_label(command) item.set_property("name", "item-dmenu") item.connect('activate', launch, command) all_items_list.append(item) # At the beginning we'll only show args.t items. Nobody's gonna scroll through thousands of them. for item in all_items_list[:args.t]: menu.append(item) # optional user-defined menu from default or custom template (see args) if args.append or args.af: separator = Gtk.SeparatorMenuItem() separator.set_property("name", "separator") menu.append(separator) appendix = load_json(build_from_file) for entry in appendix: name = entry["name"] exec = entry["exec"] icon = entry["icon"] hbox = Gtk.HBox() label = Gtk.Label() label.set_text(name) image = None if icon.startswith('/'): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( icon, args.s, args.s) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pass else: try: if icon.endswith('.svg') or icon.endswith('.png'): icon = entry.icon.split('.')[0] pixbuf = icon_theme.load_icon( icon, args.s, Gtk.IconLookupFlags.FORCE_SIZE) image = Gtk.Image.new_from_pixbuf(pixbuf) except: pass if image: hbox.pack_start(image, False, False, 10) if name: hbox.pack_start(label, False, False, 0) item = Gtk.MenuItem() item.set_property("name", "item") item.add(hbox) item.connect('activate', launch, exec) # do not cache! menu.append(item) menu.connect("hide", win.die) menu.set_property("reserve_toggle_size", False) menu.show_all() return menu