def __init__(self, name, exec, icon=None, categories=None): self.name = name self.exec = exec self.icon = icon if categories: self.categories = categories.split(';')[:-1] if self.categories: for category in self.categories: main_category = additional_to_main(category) if main_category == 'AudioVideo' and self not in c_audio_video: c_audio_video.append(self) elif main_category == 'Development' and self not in c_development: c_development.append(self) elif main_category == 'Game' and self not in c_game: c_game.append(self) elif main_category == 'Graphics' and self not in c_graphics: c_graphics.append(self) elif main_category == 'Network' and self not in c_network: c_network.append(self) elif main_category == 'Office' and self not in c_office: c_office.append(self) elif (main_category == 'Science' or main_category == 'Education') and self not in c_science: c_science.append(self) elif main_category == 'Settings' and self not in c_settings: c_settings.append(self) elif main_category == 'System' and self not in c_system: c_system.append(self) elif main_category == 'Utility' and self not in c_utility: c_utility.append(self) if self not in c_audio_video and self not in c_development \ and self not in c_game and self not in c_graphics and self not in c_network \ and self not in c_office and self not in c_science and self not in c_settings \ and self not in c_system and self not in c_utility: c_other.append(self) for group in [ c_audio_video, c_development, c_game, c_graphics, c_network, c_office, c_science, c_settings, c_system, c_utility ]: group.sort(key=lambda x: x.name)
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()