Source code for dragonfly.windows.base_window

#
# This file is part of Dragonfly.
# (c) Copyright 2007, 2008 by Christo Butcher
# Licensed under the LGPL.
#
#   Dragonfly is free software: you can redistribute it and/or modify it
#   under the terms of the GNU Lesser General Public License as published
#   by the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   Dragonfly 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
#   Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public
#   License along with Dragonfly.  If not, see
#   <http://www.gnu.org/licenses/>.
#

"""
Base Window class
============================================================================

"""

# pylint: disable=W0622,R0904
# Suppress warnings about redefining the built-in 'id' function and too many
# public methods.

from locale                          import getpreferredencoding

from six                             import (string_types, integer_types,
                                             binary_type)

from dragonfly.windows.monitor       import monitors
from dragonfly.windows.rectangle     import unit
from dragonfly.windows.window_movers import window_movers

#===========================================================================


[docs] class BaseWindow(object): """ The base Window class for controlling and placing windows. """ #----------------------------------------------------------------------- # Class attributes to retrieve existing Window objects. _windows_by_name = {} _windows_by_id = {} #----------------------------------------------------------------------- # Class methods to create new Window objects.
[docs] @classmethod def get_window(cls, id): """ Get a :class:`Window` object given a window id. Given the same id, this method will return the same object. """ if id in cls._windows_by_id: window = cls._windows_by_id[id] else: window = cls(id) cls._windows_by_id[id] = window return window
[docs] @classmethod def get_foreground(cls): """ Get the foreground window. """ raise NotImplementedError()
[docs] @classmethod def get_matching_windows(cls, executable=None, title=None): """ Find windows with a matching executable or title. Window searches are case-insensitive. If neither parameter is be specified, then it is effectively the same as calling :meth:`get_all_windows`. :param executable: -- part of the filename of the application's executable to which the target window belongs; not case sensitive. :param title: -- part of the title of the target window; not case sensitive. :type executable: str :type title: str :rtype: list """ # Make window searches case-insensitive. if executable: executable = executable.lower() if title: title = title.lower() matching = [] for window in cls.get_all_windows(): if executable: if window.executable.lower().find(executable) == -1: continue if title: if window.title.lower().find(title) == -1: continue matching.append(window) return matching
[docs] @classmethod def get_all_windows(cls): """ Get a list of all windows. """ raise NotImplementedError()
#----------------------------------------------------------------------- # Methods for initialization and introspection. def __init__(self, id): self._id = None self.id = id self._names = set() def __repr__(self): args = list(self._names) return "%s(%s)" % (self.__class__.__name__, ", ".join(args)) #----------------------------------------------------------------------- # Methods that control attribute access. @property def id(self): """ Protected access to id attribute. """ return self._id @id.setter def id(self, value): self._set_id(value) def _set_id(self, id): if not isinstance(id, integer_types): raise TypeError("Window id/handle must be integer or long," " but received {0!r}".format(id)) self._id = id self._windows_by_id[id] = self # The 'handle' and '_handle' attributes have been kept in for backwards- # compatibility. They just reference the BaseWindow 'id' property. handle = property(fget=lambda self: self._id, fset=_set_id, doc="Protected access to handle attribute.") _handle = property(fget=lambda self: self._id) def _get_name(self): if not self._names: return None for name in self._names: return name def _set_name(self, name): assert isinstance(name, string_types) self._names.add(name) self._windows_by_name[name] = self name = property(fget=_get_name, fset=_set_name, doc="Protected access to name attribute.") #----------------------------------------------------------------------- # Methods and properties for window attributes. def _get_window_text(self): # Method to get the window title. raise NotImplementedError() def _get_class_name(self): # Method to get the window class name. raise NotImplementedError() def _get_window_module(self): # Method to get the window executable. raise NotImplementedError() def _get_window_pid(self): # Method to get the window process ID. raise NotImplementedError() @property def title(self): """ Read-only access to the window's title. """ window_text = self._get_window_text() # PY2 if isinstance(window_text, binary_type): return window_text.decode(getpreferredencoding()) else: return window_text @property def classname(self): """ Read-only access to the window's class name. """ return self._get_class_name() #: Alias of :attr:`classname`. cls_name = classname @property def executable(self): """ Read-only access to the window's executable. """ return self._get_window_module() @property def pid(self): """ Read-only access to the window's process ID. This will be the PID of the window's process, not any subprocess. If the window has no associated process id, this will return ``None``. :returns: pid :rtype: int | None """ return self._get_window_pid() @property def is_minimized(self): """ Whether the window is currently minimized. """ raise NotImplementedError() @property def is_maximized(self): """ Whether the window is currently maximized. """ raise NotImplementedError() @property def is_visible(self): """ Whether the window is currently visible. This may be indeterminable for some windows. """ raise NotImplementedError()
[docs] def matches(self, context): """ Whether the window matches the given context. """ return context.matches(self.executable, self.title, self.handle)
#----------------------------------------------------------------------- # Methods related to window geometry.
[docs] def get_position(self): """ Method to get the window's position as a :class:`Rectangle` object. :returns: window position :rtype: Rectangle """ raise NotImplementedError()
[docs] def set_position(self, rectangle): """ Method to set the window's position using a :class:`Rectangle` object. :param rectangle: window position :type rectangle: Rectangle """ raise NotImplementedError()
[docs] def get_containing_monitor(self): """ Method to get the :class:`Monitor` containing the window. This checks which monitor contains the center of the window. :returns: containing monitor :rtype: Monitor """ center = self.get_position().center for monitor in monitors: if monitor.rectangle.contains(center): return monitor # Fall through, return first monitor. return monitors[0]
[docs] def get_normalized_position(self): """ Method to get the window's normalized position. This is useful when working with multiple monitors. :returns: normalized position :rtype: Rectangle """ monitor = self.get_containing_monitor() rectangle = self.get_position() rectangle.renormalize(monitor.rectangle, unit) return rectangle
[docs] def set_normalized_position(self, rectangle, monitor=None): """ Method to get the window's normalized position. This is useful when working with multiple monitors. :param rectangle: window position :type rectangle: Rectangle :param monitor: monitor to normalize to (default: the first one). :type monitor: Monitor """ if not monitor: monitor = self.get_containing_monitor() rectangle.renormalize(unit, monitor.rectangle) self.set_position(rectangle)
#----------------------------------------------------------------------- # Methods for miscellaneous window control.
[docs] def minimize(self): """ Minimize the window (if possible). """ raise NotImplementedError()
[docs] def maximize(self): """ Maximize the window (if possible). """ raise NotImplementedError()
[docs] def close(self): """ Close the window (if possible). """ raise NotImplementedError()
[docs] def restore(self): """ Restore the window if it is minimized or maximized. """ raise NotImplementedError()
[docs] def set_foreground(self): """ Set the window as the foreground (active) window. """ raise NotImplementedError()
[docs] def set_focus(self): """ Set the window as the active window without raising it. *Note*: this method will behave like :meth:`set_foreground()` in environments where this isn't possible. """ raise NotImplementedError()
[docs] def move(self, rectangle, animate=None): """ Move the window, optionally animating its movement. :param rectangle: new window position and size :param animate: name of window mover :type rectangle: Rectangle :type animate: str """ if not animate: self.set_position(rectangle) else: try: window_mover = window_movers[animate] except KeyError: # If the given window mover name isn't found, don't animate. self.set_position(rectangle) else: window_mover.move_window(self, self.get_position(), rectangle)