class Command: """ Args: action: a function to invoke when the command is activated. label: a name for the command. shortcut: (optional) a key combination that can be used to invoke the command. tooltip: (optional) a short description for what the command will do. icon: (optional) a path to an icon resource to decorate the command. group: (optional) a Group object describing a collection of similar commands. If no group is specified, a default "Command" group will be used. section: (optional) an integer providing a sub-grouping. If no section is specified, the command will be allocated to section 0 within the group. order: (optional) an integer indicating where a command falls within a section. If a Command doesn't have an order, it will be sorted alphabetically by label within its section. enabled: whether to enable the command or not. """ def __init__(self, action, label, shortcut=None, tooltip=None, icon=None, group=None, section=None, order=None, enabled=True, factory=None): self.factory = factory self.action = wrapped_handler(self, action) self.label = label self.shortcut = shortcut self.tooltip = tooltip self.icon = icon self.group = group if group else Group.COMMANDS self.section = section if section else 0 self.order = order if order else 0 self._impl = None self.enabled = enabled and self.action is not None def bind(self, factory): self.factory = factory if self._impl is None: self._impl = self.factory.Command(interface=self) if self._icon: self._icon.bind(self.factory) return self._impl @property def enabled(self): return self._enabled @enabled.setter def enabled(self, value): self._enabled = value if self._impl is not None: self._impl.set_enabled(value) @property def icon(self): """ The Icon for the app. :returns: A ``toga.Icon`` instance for the app's icon. """ return self._icon @icon.setter def icon(self, icon_or_name): if isinstance(icon_or_name, Icon) or icon_or_name is None: self._icon = icon_or_name else: self._icon = Icon(icon_or_name) if self._icon and self.factory: self._icon.bind(self.factory)
class App: """ The App is the top level of any GUI program. It is the manager of all the other bits of the GUI app: the main window and events that window generates like user input. When you create an App you need to provide it a name, an id for uniqueness (by convention, the identifier is a reversed domain name.) and an optional startup function which should run once the App has initialised. The startup function typically constructs some initial user interface. If the name and app_id are *not* provided, the application will attempt to find application metadata. This process will determine the module in which the App class is defined, and look for a ``.dist-info`` file matching that name. Once the app is created you should invoke the main_loop() method, which will hand over execution of your program to Toga to make the App interface do its thing. The absolute minimum App would be:: >>> app = toga.App(name='Empty App', app_id='org.beeware.empty') >>> app.main_loop() :param formal_name: The formal name of the application. Will be derived from packaging metadata if not provided. :param app_id: The unique application identifier. This will usually be a reversed domain name, e.g. 'org.beeware.myapp'. Will be derived from packaging metadata if not provided. :param app_name: The name of the Python module containing the app. Will be derived from the module defining the instance of the App class if not provided. :param id: The DOM identifier for the app (optional) :param icon: Identifier for the application's icon. :param author: The person or organization to be credited as the author of the application. Will be derived from application metadata if not provided. :param version: The version number of the app. Will be derived from packaging metadata if not provided. :param home_page: A URL for a home page for the app. Used in autogenerated help menu items. Will be derived from packaging metadata if not provided. :param description: A brief (one line) description of the app. Will be derived from packaging metadata if not provided. :param startup: The callback method before starting the app, typically to add the components. Must be a ``callable`` that expects a single argument of :class:`toga.App`. :param factory: A python module that is capable to return a implementation of this class with the same name. (optional & normally not needed) """ app = None def __init__( self, formal_name=None, app_id=None, app_name=None, id=None, icon=None, author=None, version=None, home_page=None, description=None, startup=None, on_exit=None, factory=None, ): # Keep an accessible copy of the app instance App.app = self # We need a module name to load app metadata. If an app_name has been # provided, we can set the app name now, and derive the module name # from there. if app_name: self._app_name = app_name else: self._app_name = sys.modules['__main__'].__package__ # During tests, and when running from a prompt, there won't be # a __main__ module. Fall back to a module that we know *does* # exist. if self._app_name is None: self._app_name = 'toga' # Load the app metdata (if it is available) # Apps packaged with Briefcase will have this metadata. try: self.metadata = importlib_metadata.metadata(self.module_name) except importlib_metadata.PackageNotFoundError: self.metadata = Message() # Now that we have metadata, we can fix the app name (in the case # where the app name and the module name differ - e.g., an app name # of `hello-world` will have a module name of `hello_world`). # We use the PEP566 key "Name", rather than "App-Name". if app_name is None and self.metadata['Name'] is not None: self._app_name = self.metadata['Name'] # If a name has been provided, use it; otherwise, look to # the module metadata. However, a name *must* be provided. if formal_name: self._formal_name = formal_name else: self._formal_name = self.metadata['Formal-Name'] if self._formal_name is None: raise RuntimeError('Toga application must have a formal name') # If an app_id has been provided, use it; otherwise, look to # the module metadata. However, an app_id *must* be provied if app_id: self._app_id = app_id else: self._app_id = self.metadata['App-ID'] if self._app_id is None: raise RuntimeError('Toga application must have an App ID') # If an author has been provided, use it; otherwise, look to # the module metadata. if author: self._author = author elif self.metadata['Author']: self._author = self.metadata['Author'] # If a version has been provided, use it; otherwise, look to # the module metadata. if version: self._version = version elif self.metadata['Version']: self._version = self.metadata['Version'] # If a home_page has been provided, use it; otherwise, look to # the module metadata. if home_page: self._home_page = home_page elif self.metadata['Home-page']: self._home_page = self.metadata['home_page'] # If a description has been provided, use it; otherwise, look to # the module metadata. if description: self._description = description elif self.metadata['description']: self._description = self.metadata['Summary'] # Set the application DOM ID; create an ID if one hasn't been provided. self._id = id if id else identifier(self) # Get a platform factory, and a paths instance from the factory. self.factory = get_platform_factory(factory) self.paths = self.factory.paths # If an icon (or icon name) has been explicitly provided, use it; # otherwise, the icon will be based on the app name. if icon: self.icon = icon else: self.icon = 'resources/{app_name}'.format(app_name=self.app_name) self.commands = CommandSet(factory=self.factory) self._startup_method = startup self._main_window = None self._on_exit = None self._full_screen_windows = None self._impl = self._create_impl() self.on_exit = on_exit def _create_impl(self): return self.factory.App(interface=self) @property def name(self): """ The formal name of the app. :returns: The formal name of the app, as a ``str``. """ return self._formal_name @property def formal_name(self): """ The formal name of the app. :returns: The formal name of the app, as a ``str``. """ return self._formal_name @property def app_name(self): """ The machine-readable, PEP508-compliant name of the app. :returns: The machine-readable app name, as a ``str``. """ return self._app_name @property def module_name(self): """ The module name for the app :returns: The module name for the app, as a ``str``. """ try: return self._app_name.replace('-', '_') except AttributeError: # If the app was created from an interactive prompt, # there won't be a module name. return None @property def app_id(self): """ The identifier for the app. This is a reversed domain name, often used for targetting resources, etc. :returns: The identifier as a ``str``. """ return self._app_id @property def author(self): """ The author of the app. This may be an organization name :returns: The author of the app, as a ``str``. """ return self._author @property def version(self): """ The version number of the app. :returns: The version numberof the app, as a ``str``. """ return self._version @property def home_page(self): """ The URL of a web page for the app. :returns: The URL of the app's home page, as a ``str``. """ return self._home_page @property def description(self): """ A brief description of the app. :returns: A brief description of the app, as a ``str``. """ return self._description @property def id(self): """ The DOM identifier for the app. This id can be used to target CSS directives. :returns: A DOM identifier for the app. """ return self._id @property def icon(self): """ The Icon for the app. :returns: A ``toga.Icon`` instance for the app's icon. """ return self._icon @icon.setter def icon(self, icon_or_name): if isinstance(icon_or_name, Icon): self._icon = icon_or_name else: self._icon = Icon(icon_or_name) self._icon.bind(self.factory) @property def main_window(self): """ The main windows for the app. :returns: The main Window of the app. """ return self._main_window @main_window.setter def main_window(self, window): self._main_window = window window.app = self self._impl.set_main_window(window) @property def current_window(self): """Return the currently active content window""" return self._impl.current_window().interface @property def is_full_screen(self): """Is the app currently in full screen mode?""" return self._full_screen_windows is not None def set_full_screen(self, *windows): """Make one or more windows full screen. Full screen is not the same as "maximized"; full screen mode is when all window borders and other chrome is no longer visible. Args: windows: The list of windows to go full screen, in order of allocation to screens. If the number of windows exceeds the number of available displays, those windows will not be visible. If no windows are specified, the app will exit full screen mode. """ if not windows: self.exit_full_screen() else: self._impl.enter_full_screen(windows) self._full_screen_windows = windows def exit_full_screen(self): """Exit full screen mode.""" if self.is_full_screen: self._impl.exit_full_screen(self._full_screen_windows) self._full_screen_windows = None def show_cursor(self): """Show cursor.""" self._impl.show_cursor() def hide_cursor(self): """Hide cursor from view.""" self._impl.hide_cursor() def startup(self): """ Create and show the main window for the application """ self.main_window = MainWindow(title=self.formal_name, factory=self.factory) if self._startup_method: self.main_window.content = self._startup_method(self) self.main_window.show() def main_loop(self): """ Invoke the application to handle user input. This method typically only returns once the application is exiting. """ # Modify signal handlers to make sure Ctrl-C is caught and handled. signal.signal(signal.SIGINT, signal.SIG_DFL) self._impl.main_loop() def exit(self): """ Quit the application gracefully. """ self._impl.exit() @property def on_exit(self): """The handler to invoke before the application exits. Returns: The function ``callable`` that is called on application exit. """ return self._on_exit @on_exit.setter def on_exit(self, handler): """Set the handler to invoke before the app exits. Args: handler (:obj:`callable`): The handler to invoke before the app exits. """ self._on_exit = wrapped_handler(self, handler) self._impl.set_on_exit(self._on_exit) def add_background_task(self, handler): self._impl.add_background_task(handler)