feat(SublimeText2.EditorPackages): cache packages
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple desktop integration for Python. This module provides desktop environment
|
||||
detection and resource opening support for a selection of common and
|
||||
standardised desktop environments.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>
|
||||
|
||||
This program 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.
|
||||
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------
|
||||
|
||||
Desktop Detection
|
||||
-----------------
|
||||
|
||||
To detect a specific desktop environment, use the get_desktop function.
|
||||
To detect whether the desktop environment is standardised (according to the
|
||||
proposed DESKTOP_LAUNCH standard), use the is_standard function.
|
||||
|
||||
Opening URLs
|
||||
------------
|
||||
|
||||
To open a URL in the current desktop environment, relying on the automatic
|
||||
detection of that environment, use the desktop.open function as follows:
|
||||
|
||||
desktop.open("http://www.python.org")
|
||||
|
||||
To override the detected desktop, specify the desktop parameter to the open
|
||||
function as follows:
|
||||
|
||||
desktop.open("http://www.python.org", "KDE") # Insists on KDE
|
||||
desktop.open("http://www.python.org", "GNOME") # Insists on GNOME
|
||||
desktop.open("http://www.python.org", "MATE") # Insists on MATE
|
||||
|
||||
Without overriding using the desktop parameter, the open function will attempt
|
||||
to use the "standard" desktop opening mechanism which is controlled by the
|
||||
DESKTOP_LAUNCH environment variable as described below.
|
||||
|
||||
The DESKTOP_LAUNCH Environment Variable
|
||||
---------------------------------------
|
||||
|
||||
The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate,
|
||||
as shown in some of the following examples:
|
||||
|
||||
DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in
|
||||
their entirety in a KDE message box.
|
||||
(Command "kdialog" plus parameter.)
|
||||
DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to
|
||||
open URLs.
|
||||
(Command "my opener", no parameters.)
|
||||
DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to
|
||||
open URLs.
|
||||
(Command "my opener" plus parameter.)
|
||||
|
||||
Details of the DESKTOP_LAUNCH environment variable convention can be found here:
|
||||
http://lists.freedesktop.org/archives/xdg/2004-August/004489.html
|
||||
|
||||
Other Modules
|
||||
-------------
|
||||
|
||||
The desktop.dialog module provides support for opening dialogue boxes.
|
||||
The desktop.windows module permits the inspection of desktop windows.
|
||||
"""
|
||||
|
||||
__version__ = "0.4"
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Provide suitable process creation functions.
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
def _run(cmd, shell, wait):
|
||||
opener = subprocess.Popen(cmd, shell=shell)
|
||||
if wait: opener.wait()
|
||||
return opener.pid
|
||||
|
||||
def _readfrom(cmd, shell):
|
||||
opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
opener.stdin.close()
|
||||
return opener.stdout.read()
|
||||
|
||||
def _status(cmd, shell):
|
||||
opener = subprocess.Popen(cmd, shell=shell)
|
||||
opener.wait()
|
||||
return opener.returncode == 0
|
||||
|
||||
except ImportError:
|
||||
import popen2
|
||||
def _run(cmd, shell, wait):
|
||||
opener = popen2.Popen3(cmd)
|
||||
if wait: opener.wait()
|
||||
return opener.pid
|
||||
|
||||
def _readfrom(cmd, shell):
|
||||
opener = popen2.Popen3(cmd)
|
||||
opener.tochild.close()
|
||||
opener.childerr.close()
|
||||
return opener.fromchild.read()
|
||||
|
||||
def _status(cmd, shell):
|
||||
opener = popen2.Popen3(cmd)
|
||||
opener.wait()
|
||||
return opener.poll() == 0
|
||||
|
||||
import commands
|
||||
|
||||
# Private functions.
|
||||
|
||||
def _get_x11_vars():
|
||||
|
||||
"Return suitable environment definitions for X11."
|
||||
|
||||
if not os.environ.get("DISPLAY", "").strip():
|
||||
return "DISPLAY=:0.0 "
|
||||
else:
|
||||
return ""
|
||||
|
||||
def _is_xfce():
|
||||
|
||||
"Return whether XFCE is in use."
|
||||
|
||||
# XFCE detection involves testing the output of a program.
|
||||
|
||||
try:
|
||||
return _readfrom(_get_x11_vars() + "xprop -root _DT_SAVE_MODE", shell=1).strip().endswith(' = "xfce4"')
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
def _is_x11():
|
||||
|
||||
"Return whether the X Window System is in use."
|
||||
|
||||
return os.environ.has_key("DISPLAY")
|
||||
|
||||
# Introspection functions.
|
||||
|
||||
def get_desktop():
|
||||
|
||||
"""
|
||||
Detect the current desktop environment, returning the name of the
|
||||
environment. If no environment could be detected, None is returned.
|
||||
"""
|
||||
|
||||
if os.environ.has_key("KDE_FULL_SESSION") or \
|
||||
os.environ.has_key("KDE_MULTIHEAD"):
|
||||
return "KDE"
|
||||
elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \
|
||||
os.environ.has_key("GNOME_KEYRING_SOCKET"):
|
||||
return "GNOME"
|
||||
elif os.environ.has_key("MATE_DESKTOP_SESSION_ID") or \
|
||||
os.environ.has_key("MATE_KEYRING_SOCKET"):
|
||||
return "MATE"
|
||||
elif sys.platform == "darwin":
|
||||
return "Mac OS X"
|
||||
elif hasattr(os, "startfile"):
|
||||
return "Windows"
|
||||
elif _is_xfce():
|
||||
return "XFCE"
|
||||
|
||||
# KDE, GNOME, MATE and XFCE run on X11, so we have to test for X11 last.
|
||||
|
||||
if _is_x11():
|
||||
return "X11"
|
||||
else:
|
||||
return None
|
||||
|
||||
def use_desktop(desktop):
|
||||
|
||||
"""
|
||||
Decide which desktop should be used, based on the detected desktop and a
|
||||
supplied 'desktop' argument (which may be None). Return an identifier
|
||||
indicating the desktop type as being either "standard" or one of the results
|
||||
from the 'get_desktop' function.
|
||||
"""
|
||||
|
||||
# Attempt to detect a desktop environment.
|
||||
|
||||
detected = get_desktop()
|
||||
|
||||
# Start with desktops whose existence can be easily tested.
|
||||
|
||||
if (desktop is None or desktop == "standard") and is_standard():
|
||||
return "standard"
|
||||
elif (desktop is None or desktop == "Windows") and detected == "Windows":
|
||||
return "Windows"
|
||||
|
||||
# Test for desktops where the overriding is not verified.
|
||||
|
||||
elif (desktop or detected) == "KDE":
|
||||
return "KDE"
|
||||
elif (desktop or detected) == "GNOME":
|
||||
return "GNOME"
|
||||
elif (desktop or detected) == "MATE":
|
||||
return "MATE"
|
||||
elif (desktop or detected) == "XFCE":
|
||||
return "XFCE"
|
||||
elif (desktop or detected) == "Mac OS X":
|
||||
return "Mac OS X"
|
||||
elif (desktop or detected) == "X11":
|
||||
return "X11"
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_standard():
|
||||
|
||||
"""
|
||||
Return whether the current desktop supports standardised application
|
||||
launching.
|
||||
"""
|
||||
|
||||
return os.environ.has_key("DESKTOP_LAUNCH")
|
||||
|
||||
# Activity functions.
|
||||
|
||||
def open(url, desktop=None, wait=0):
|
||||
|
||||
"""
|
||||
Open the 'url' in the current desktop's preferred file browser. If the
|
||||
optional 'desktop' parameter is specified then attempt to use that
|
||||
particular desktop environment's mechanisms to open the 'url' instead of
|
||||
guessing or detecting which environment is being used.
|
||||
|
||||
Suggested values for 'desktop' are "standard", "KDE", "GNOME", "GNOME",
|
||||
"XFCE", "Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH
|
||||
environment variable to open the specified 'url'. DESKTOP_LAUNCH should
|
||||
be a command, possibly followed by arguments, and must have any special
|
||||
characters shell-escaped.
|
||||
|
||||
The process identifier of the "opener" (ie. viewer, editor, browser or
|
||||
program) associated with the 'url' is returned by this function. If the
|
||||
process identifier cannot be determined, None is returned.
|
||||
|
||||
An optional 'wait' parameter is also available for advanced usage and, if
|
||||
'wait' is set to a true value, this function will wait for the launching
|
||||
mechanism to complete before returning (as opposed to immediately returning
|
||||
as is the default behaviour).
|
||||
"""
|
||||
|
||||
# Decide on the desktop environment in use.
|
||||
|
||||
desktop_in_use = use_desktop(desktop)
|
||||
|
||||
if desktop_in_use == "standard":
|
||||
arg = "".join([os.environ["DESKTOP_LAUNCH"], commands.mkarg(url)])
|
||||
return _run(arg, 1, wait)
|
||||
|
||||
elif desktop_in_use == "Windows":
|
||||
# NOTE: This returns None in current implementations.
|
||||
return os.startfile(url)
|
||||
|
||||
elif desktop_in_use == "KDE":
|
||||
cmd = ["xdg-open", url]
|
||||
|
||||
elif desktop_in_use == "GNOME":
|
||||
cmd = ["xdg-open", url]
|
||||
|
||||
elif desktop_in_use == "MATE":
|
||||
cmd = ["xdg-open", url]
|
||||
|
||||
elif desktop_in_use == "XFCE":
|
||||
cmd = ["xdg-open", url]
|
||||
|
||||
elif desktop_in_use == "Mac OS X":
|
||||
cmd = ["open", url]
|
||||
|
||||
elif desktop_in_use == "X11" and os.environ.has_key("BROWSER"):
|
||||
cmd = [os.environ["BROWSER"], url]
|
||||
|
||||
# Finish with an error where no suitable desktop was identified.
|
||||
|
||||
else:
|
||||
raise OSError, "Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use
|
||||
|
||||
return _run(cmd, 0, wait)
|
||||
|
||||
# vim: tabstop=4 expandtab shiftwidth=4
|
@@ -0,0 +1,551 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple desktop dialogue box support for Python.
|
||||
|
||||
Copyright (C) 2007, 2009 Paul Boddie <paul@boddie.org.uk>
|
||||
|
||||
This program 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.
|
||||
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------
|
||||
|
||||
Opening Dialogue Boxes (Dialogs)
|
||||
--------------------------------
|
||||
|
||||
To open a dialogue box (dialog) in the current desktop environment, relying on
|
||||
the automatic detection of that environment, use the appropriate dialogue box
|
||||
class:
|
||||
|
||||
question = desktop.dialog.Question("Are you sure?")
|
||||
result = question.open()
|
||||
|
||||
To override the detected desktop, specify the desktop parameter to the open
|
||||
function as follows:
|
||||
|
||||
question.open("KDE") # Insists on KDE
|
||||
question.open("GNOME") # Insists on GNOME
|
||||
question.open("MATE") # Insists on MATE
|
||||
|
||||
The dialogue box options are documented in each class's docstring.
|
||||
|
||||
Available dialogue box classes are listed in the desktop.dialog.available
|
||||
attribute.
|
||||
|
||||
Supported desktop environments are listed in the desktop.dialog.supported
|
||||
attribute.
|
||||
"""
|
||||
|
||||
from desktop import use_desktop, _run, _readfrom, _status
|
||||
|
||||
class _wrapper:
|
||||
def __init__(self, handler):
|
||||
self.handler = handler
|
||||
|
||||
class _readvalue(_wrapper):
|
||||
def __call__(self, cmd, shell):
|
||||
return self.handler(cmd, shell).strip()
|
||||
|
||||
class _readinput(_wrapper):
|
||||
def __call__(self, cmd, shell):
|
||||
return self.handler(cmd, shell)[:-1]
|
||||
|
||||
class _readvalues_kdialog(_wrapper):
|
||||
def __call__(self, cmd, shell):
|
||||
result = self.handler(cmd, shell).strip().strip('"')
|
||||
if result:
|
||||
return result.split('" "')
|
||||
else:
|
||||
return []
|
||||
|
||||
class _readvalues_zenity(_wrapper):
|
||||
def __call__(self, cmd, shell):
|
||||
result = self.handler(cmd, shell).strip()
|
||||
if result:
|
||||
return result.split("|")
|
||||
else:
|
||||
return []
|
||||
|
||||
class _readvalues_Xdialog(_wrapper):
|
||||
def __call__(self, cmd, shell):
|
||||
result = self.handler(cmd, shell).strip()
|
||||
if result:
|
||||
return result.split("/")
|
||||
else:
|
||||
return []
|
||||
|
||||
# Dialogue parameter classes.
|
||||
|
||||
class String:
|
||||
|
||||
"A generic parameter."
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def convert(self, value, program):
|
||||
return [value or ""]
|
||||
|
||||
class Strings(String):
|
||||
|
||||
"Multiple string parameters."
|
||||
|
||||
def convert(self, value, program):
|
||||
return value or []
|
||||
|
||||
class StringPairs(String):
|
||||
|
||||
"Multiple string parameters duplicated to make identifiers."
|
||||
|
||||
def convert(self, value, program):
|
||||
l = []
|
||||
for v in value:
|
||||
l.append(v)
|
||||
l.append(v)
|
||||
return l
|
||||
|
||||
class StringKeyword:
|
||||
|
||||
"A keyword parameter."
|
||||
|
||||
def __init__(self, keyword, name):
|
||||
self.keyword = keyword
|
||||
self.name = name
|
||||
|
||||
def convert(self, value, program):
|
||||
return [self.keyword + "=" + (value or "")]
|
||||
|
||||
class StringKeywords:
|
||||
|
||||
"Multiple keyword parameters."
|
||||
|
||||
def __init__(self, keyword, name):
|
||||
self.keyword = keyword
|
||||
self.name = name
|
||||
|
||||
def convert(self, value, program):
|
||||
l = []
|
||||
for v in value or []:
|
||||
l.append(self.keyword + "=" + v)
|
||||
return l
|
||||
|
||||
class Integer(String):
|
||||
|
||||
"An integer parameter."
|
||||
|
||||
defaults = {
|
||||
"width" : 40,
|
||||
"height" : 15,
|
||||
"list_height" : 10
|
||||
}
|
||||
scale = 8
|
||||
|
||||
def __init__(self, name, pixels=0):
|
||||
String.__init__(self, name)
|
||||
if pixels:
|
||||
self.factor = self.scale
|
||||
else:
|
||||
self.factor = 1
|
||||
|
||||
def convert(self, value, program):
|
||||
if value is None:
|
||||
value = self.defaults[self.name]
|
||||
return [str(int(value) * self.factor)]
|
||||
|
||||
class IntegerKeyword(Integer):
|
||||
|
||||
"An integer keyword parameter."
|
||||
|
||||
def __init__(self, keyword, name, pixels=0):
|
||||
Integer.__init__(self, name, pixels)
|
||||
self.keyword = keyword
|
||||
|
||||
def convert(self, value, program):
|
||||
if value is None:
|
||||
value = self.defaults[self.name]
|
||||
return [self.keyword + "=" + str(int(value) * self.factor)]
|
||||
|
||||
class Boolean(String):
|
||||
|
||||
"A boolean parameter."
|
||||
|
||||
values = {
|
||||
"kdialog" : ["off", "on"],
|
||||
"zenity" : ["FALSE", "TRUE"],
|
||||
"Xdialog" : ["off", "on"]
|
||||
}
|
||||
|
||||
def convert(self, value, program):
|
||||
values = self.values[program]
|
||||
if value:
|
||||
return [values[1]]
|
||||
else:
|
||||
return [values[0]]
|
||||
|
||||
class MenuItemList(String):
|
||||
|
||||
"A menu item list parameter."
|
||||
|
||||
def convert(self, value, program):
|
||||
l = []
|
||||
for v in value:
|
||||
l.append(v.value)
|
||||
l.append(v.text)
|
||||
return l
|
||||
|
||||
class ListItemList(String):
|
||||
|
||||
"A radiolist/checklist item list parameter."
|
||||
|
||||
def __init__(self, name, status_first=0):
|
||||
String.__init__(self, name)
|
||||
self.status_first = status_first
|
||||
|
||||
def convert(self, value, program):
|
||||
l = []
|
||||
for v in value:
|
||||
boolean = Boolean(None)
|
||||
status = boolean.convert(v.status, program)
|
||||
if self.status_first:
|
||||
l += status
|
||||
l.append(v.value)
|
||||
l.append(v.text)
|
||||
if not self.status_first:
|
||||
l += status
|
||||
return l
|
||||
|
||||
# Dialogue argument values.
|
||||
|
||||
class MenuItem:
|
||||
|
||||
"A menu item which can also be used with radiolists and checklists."
|
||||
|
||||
def __init__(self, value, text, status=0):
|
||||
self.value = value
|
||||
self.text = text
|
||||
self.status = status
|
||||
|
||||
# Dialogue classes.
|
||||
|
||||
class Dialogue:
|
||||
|
||||
commands = {
|
||||
"KDE" : "kdialog",
|
||||
"GNOME" : "zenity",
|
||||
"MATE" : "zenity",
|
||||
"XFCE" : "zenity", # NOTE: Based on observations with Xubuntu.
|
||||
"X11" : "Xdialog"
|
||||
}
|
||||
|
||||
def open(self, desktop=None):
|
||||
|
||||
"""
|
||||
Open a dialogue box (dialog) using a program appropriate to the desktop
|
||||
environment in use.
|
||||
|
||||
If the optional 'desktop' parameter is specified then attempt to use
|
||||
that particular desktop environment's mechanisms to open the dialog
|
||||
instead of guessing or detecting which environment is being used.
|
||||
|
||||
Suggested values for 'desktop' are "standard", "KDE", "GNOME",
|
||||
"MATE", "Mac OS X", "Windows".
|
||||
|
||||
The result of the dialogue interaction may be a string indicating user
|
||||
input (for Input, Password, Menu, Pulldown), a list of strings
|
||||
indicating selections of one or more items (for RadioList, CheckList),
|
||||
or a value indicating true or false (for Question, Warning, Message,
|
||||
Error).
|
||||
|
||||
Where a string value may be expected but no choice is made, an empty
|
||||
string may be returned. Similarly, where a list of values is expected
|
||||
but no choice is made, an empty list may be returned.
|
||||
"""
|
||||
|
||||
# Decide on the desktop environment in use.
|
||||
|
||||
desktop_in_use = use_desktop(desktop)
|
||||
|
||||
# Get the program.
|
||||
|
||||
try:
|
||||
program = self.commands[desktop_in_use]
|
||||
except KeyError:
|
||||
raise OSError, "Desktop '%s' not supported (no known dialogue box command could be suggested)" % desktop_in_use
|
||||
|
||||
# The handler is one of the functions communicating with the subprocess.
|
||||
# Some handlers return boolean values, others strings.
|
||||
|
||||
handler, options = self.info[program]
|
||||
|
||||
cmd = [program]
|
||||
for option in options:
|
||||
if isinstance(option, str):
|
||||
cmd.append(option)
|
||||
else:
|
||||
value = getattr(self, option.name, None)
|
||||
cmd += option.convert(value, program)
|
||||
|
||||
return handler(cmd, 0)
|
||||
|
||||
class Simple(Dialogue):
|
||||
def __init__(self, text, width=None, height=None):
|
||||
self.text = text
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
class Question(Simple):
|
||||
|
||||
"""
|
||||
A dialogue asking a question and showing response buttons.
|
||||
Options: text, width (in characters), height (in characters)
|
||||
Response: a boolean value indicating an affirmative response (true) or a
|
||||
negative response
|
||||
"""
|
||||
|
||||
name = "question"
|
||||
info = {
|
||||
"kdialog" : (_status, ["--yesno", String("text")]),
|
||||
"zenity" : (_status, ["--question", StringKeyword("--text", "text")]),
|
||||
"Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]),
|
||||
}
|
||||
|
||||
class Warning(Simple):
|
||||
|
||||
"""
|
||||
A dialogue asking a question and showing response buttons.
|
||||
Options: text, width (in characters), height (in characters)
|
||||
Response: a boolean value indicating an affirmative response (true) or a
|
||||
negative response
|
||||
"""
|
||||
|
||||
name = "warning"
|
||||
info = {
|
||||
"kdialog" : (_status, ["--warningyesno", String("text")]),
|
||||
"zenity" : (_status, ["--warning", StringKeyword("--text", "text")]),
|
||||
"Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]),
|
||||
}
|
||||
|
||||
class Message(Simple):
|
||||
|
||||
"""
|
||||
A message dialogue.
|
||||
Options: text, width (in characters), height (in characters)
|
||||
Response: a boolean value indicating an affirmative response (true) or a
|
||||
negative response
|
||||
"""
|
||||
|
||||
name = "message"
|
||||
info = {
|
||||
"kdialog" : (_status, ["--msgbox", String("text")]),
|
||||
"zenity" : (_status, ["--info", StringKeyword("--text", "text")]),
|
||||
"Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]),
|
||||
}
|
||||
|
||||
class Error(Simple):
|
||||
|
||||
"""
|
||||
An error dialogue.
|
||||
Options: text, width (in characters), height (in characters)
|
||||
Response: a boolean value indicating an affirmative response (true) or a
|
||||
negative response
|
||||
"""
|
||||
|
||||
name = "error"
|
||||
info = {
|
||||
"kdialog" : (_status, ["--error", String("text")]),
|
||||
"zenity" : (_status, ["--error", StringKeyword("--text", "text")]),
|
||||
"Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]),
|
||||
}
|
||||
|
||||
class Menu(Simple):
|
||||
|
||||
"""
|
||||
A menu of options, one of which being selectable.
|
||||
Options: text, width (in characters), height (in characters),
|
||||
list_height (in items), items (MenuItem objects)
|
||||
Response: a value corresponding to the chosen item
|
||||
"""
|
||||
|
||||
name = "menu"
|
||||
info = {
|
||||
"kdialog" : (_readvalue(_readfrom), ["--menu", String("text"), MenuItemList("items")]),
|
||||
"zenity" : (_readvalue(_readfrom), ["--list", StringKeyword("--text", "text"), StringKeywords("--column", "titles"),
|
||||
MenuItemList("items")]
|
||||
),
|
||||
"Xdialog" : (_readvalue(_readfrom), ["--stdout", "--menubox",
|
||||
String("text"), Integer("height"), Integer("width"), Integer("list_height"), MenuItemList("items")]
|
||||
),
|
||||
}
|
||||
item = MenuItem
|
||||
number_of_titles = 2
|
||||
|
||||
def __init__(self, text, titles, items=None, width=None, height=None, list_height=None):
|
||||
|
||||
"""
|
||||
Initialise a menu with the given heading 'text', column 'titles', and
|
||||
optional 'items' (which may be added later), 'width' (in characters),
|
||||
'height' (in characters) and 'list_height' (in items).
|
||||
"""
|
||||
|
||||
Simple.__init__(self, text, width, height)
|
||||
self.titles = ([""] * self.number_of_titles + titles)[-self.number_of_titles:]
|
||||
self.items = items or []
|
||||
self.list_height = list_height
|
||||
|
||||
def add(self, *args, **kw):
|
||||
|
||||
"""
|
||||
Add an item, passing the given arguments to the appropriate item class.
|
||||
"""
|
||||
|
||||
self.items.append(self.item(*args, **kw))
|
||||
|
||||
class RadioList(Menu):
|
||||
|
||||
"""
|
||||
A list of radio buttons, one of which being selectable.
|
||||
Options: text, width (in characters), height (in characters),
|
||||
list_height (in items), items (MenuItem objects), titles
|
||||
Response: a list of values corresponding to chosen items (since some
|
||||
programs, eg. zenity, appear to support multiple default
|
||||
selections)
|
||||
"""
|
||||
|
||||
name = "radiolist"
|
||||
info = {
|
||||
"kdialog" : (_readvalues_kdialog(_readfrom), ["--radiolist", String("text"), ListItemList("items")]),
|
||||
"zenity" : (_readvalues_zenity(_readfrom),
|
||||
["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"),
|
||||
ListItemList("items", 1)]
|
||||
),
|
||||
"Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--radiolist",
|
||||
String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")]
|
||||
),
|
||||
}
|
||||
number_of_titles = 3
|
||||
|
||||
class CheckList(Menu):
|
||||
|
||||
"""
|
||||
A list of checkboxes, many being selectable.
|
||||
Options: text, width (in characters), height (in characters),
|
||||
list_height (in items), items (MenuItem objects), titles
|
||||
Response: a list of values corresponding to chosen items
|
||||
"""
|
||||
|
||||
name = "checklist"
|
||||
info = {
|
||||
"kdialog" : (_readvalues_kdialog(_readfrom), ["--checklist", String("text"), ListItemList("items")]),
|
||||
"zenity" : (_readvalues_zenity(_readfrom),
|
||||
["--list", "--checklist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"),
|
||||
ListItemList("items", 1)]
|
||||
),
|
||||
"Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--checklist",
|
||||
String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")]
|
||||
),
|
||||
}
|
||||
number_of_titles = 3
|
||||
|
||||
class Pulldown(Menu):
|
||||
|
||||
"""
|
||||
A pull-down menu of options, one of which being selectable.
|
||||
Options: text, width (in characters), height (in characters),
|
||||
items (list of values)
|
||||
Response: a value corresponding to the chosen item
|
||||
"""
|
||||
|
||||
name = "pulldown"
|
||||
info = {
|
||||
"kdialog" : (_readvalue(_readfrom), ["--combobox", String("text"), Strings("items")]),
|
||||
"zenity" : (_readvalue(_readfrom),
|
||||
["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"),
|
||||
StringPairs("items")]
|
||||
),
|
||||
"Xdialog" : (_readvalue(_readfrom),
|
||||
["--stdout", "--combobox", String("text"), Integer("height"), Integer("width"), Strings("items")]),
|
||||
}
|
||||
item = unicode
|
||||
number_of_titles = 2
|
||||
|
||||
class Input(Simple):
|
||||
|
||||
"""
|
||||
An input dialogue, consisting of an input field.
|
||||
Options: text, input, width (in characters), height (in characters)
|
||||
Response: the text entered into the dialogue by the user
|
||||
"""
|
||||
|
||||
name = "input"
|
||||
info = {
|
||||
"kdialog" : (_readinput(_readfrom),
|
||||
["--inputbox", String("text"), String("data")]),
|
||||
"zenity" : (_readinput(_readfrom),
|
||||
["--entry", StringKeyword("--text", "text"), StringKeyword("--entry-text", "data")]),
|
||||
"Xdialog" : (_readinput(_readfrom),
|
||||
["--stdout", "--inputbox", String("text"), Integer("height"), Integer("width"), String("data")]),
|
||||
}
|
||||
|
||||
def __init__(self, text, data="", width=None, height=None):
|
||||
Simple.__init__(self, text, width, height)
|
||||
self.data = data
|
||||
|
||||
class Password(Input):
|
||||
|
||||
"""
|
||||
A password dialogue, consisting of a password entry field.
|
||||
Options: text, width (in characters), height (in characters)
|
||||
Response: the text entered into the dialogue by the user
|
||||
"""
|
||||
|
||||
name = "password"
|
||||
info = {
|
||||
"kdialog" : (_readinput(_readfrom),
|
||||
["--password", String("text")]),
|
||||
"zenity" : (_readinput(_readfrom),
|
||||
["--entry", StringKeyword("--text", "text"), "--hide-text"]),
|
||||
"Xdialog" : (_readinput(_readfrom),
|
||||
["--stdout", "--password", "--inputbox", String("text"), Integer("height"), Integer("width")]),
|
||||
}
|
||||
|
||||
class TextFile(Simple):
|
||||
|
||||
"""
|
||||
A text file input box.
|
||||
Options: filename, text, width (in characters), height (in characters)
|
||||
Response: any text returned by the dialogue program (typically an empty
|
||||
string)
|
||||
"""
|
||||
|
||||
name = "textfile"
|
||||
info = {
|
||||
"kdialog" : (_readfrom, ["--textbox", String("filename"), Integer("width", pixels=1), Integer("height", pixels=1)]),
|
||||
"zenity" : (_readfrom, ["--text-info", StringKeyword("--filename", "filename"), IntegerKeyword("--width", "width", pixels=1),
|
||||
IntegerKeyword("--height", "height", pixels=1)]
|
||||
),
|
||||
"Xdialog" : (_readfrom, ["--stdout", "--textbox", String("filename"), Integer("height"), Integer("width")]),
|
||||
}
|
||||
|
||||
def __init__(self, filename, text="", width=None, height=None):
|
||||
Simple.__init__(self, text, width, height)
|
||||
self.filename = filename
|
||||
|
||||
# Available dialogues.
|
||||
|
||||
available = [Question, Warning, Message, Error, Menu, CheckList, RadioList, Input, Password, Pulldown, TextFile]
|
||||
|
||||
# Supported desktop environments.
|
||||
|
||||
supported = Dialogue.commands.keys()
|
||||
|
||||
# vim: tabstop=4 expandtab shiftwidth=4
|
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple desktop window enumeration for Python.
|
||||
|
||||
Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>
|
||||
|
||||
This program 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.
|
||||
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------
|
||||
|
||||
Finding Open Windows on the Desktop
|
||||
-----------------------------------
|
||||
|
||||
To obtain a list of windows, use the desktop.windows.list function as follows:
|
||||
|
||||
windows = desktop.windows.list()
|
||||
|
||||
To obtain the root window, typically the desktop background, use the
|
||||
desktop.windows.root function as follows:
|
||||
|
||||
root = desktop.windows.root()
|
||||
|
||||
Each window object can be inspected through a number of methods. For example:
|
||||
|
||||
name = window.name()
|
||||
width, height = window.size()
|
||||
x, y = window.position()
|
||||
child_windows = window.children()
|
||||
|
||||
See the desktop.windows.Window class for more information.
|
||||
"""
|
||||
|
||||
from desktop import _is_x11, _get_x11_vars, _readfrom, use_desktop
|
||||
import re
|
||||
|
||||
# System functions.
|
||||
|
||||
def _xwininfo(identifier, action):
|
||||
if identifier is None:
|
||||
args = "-root"
|
||||
else:
|
||||
args = "-id " + identifier
|
||||
|
||||
s = _readfrom(_get_x11_vars() + "xwininfo %s -%s" % (args, action), shell=1)
|
||||
|
||||
# Return a mapping of keys to values for the "stats" action.
|
||||
|
||||
if action == "stats":
|
||||
d = {}
|
||||
for line in s.split("\n"):
|
||||
fields = line.split(":")
|
||||
if len(fields) < 2:
|
||||
continue
|
||||
key, value = fields[0].strip(), ":".join(fields[1:]).strip()
|
||||
d[key] = value
|
||||
|
||||
return d
|
||||
|
||||
# Otherwise, return the raw output.
|
||||
|
||||
else:
|
||||
return s
|
||||
|
||||
def _get_int_properties(d, properties):
|
||||
results = []
|
||||
for property in properties:
|
||||
results.append(int(d[property]))
|
||||
return results
|
||||
|
||||
# Finder functions.
|
||||
|
||||
def find_all(name):
|
||||
return 1
|
||||
|
||||
def find_named(name):
|
||||
return name is not None
|
||||
|
||||
def find_by_name(name):
|
||||
return lambda n, t=name: n == t
|
||||
|
||||
# Window classes.
|
||||
# NOTE: X11 is the only supported desktop so far.
|
||||
|
||||
class Window:
|
||||
|
||||
"A window on the desktop."
|
||||
|
||||
_name_pattern = re.compile(r':\s+\(.*?\)\s+[-0-9x+]+\s+[-0-9+]+$')
|
||||
_absent_names = "(has no name)", "(the root window) (has no name)"
|
||||
|
||||
def __init__(self, identifier):
|
||||
|
||||
"Initialise the window with the given 'identifier'."
|
||||
|
||||
self.identifier = identifier
|
||||
|
||||
# Finder methods (from above).
|
||||
|
||||
self.find_all = find_all
|
||||
self.find_named = find_named
|
||||
self.find_by_name = find_by_name
|
||||
|
||||
def __repr__(self):
|
||||
return "Window(%r)" % self.identifier
|
||||
|
||||
# Methods which deal with the underlying commands.
|
||||
|
||||
def _get_handle_and_name(self, text):
|
||||
fields = text.strip().split(" ")
|
||||
handle = fields[0]
|
||||
|
||||
# Get the "<name>" part, stripping off the quotes.
|
||||
|
||||
name = " ".join(fields[1:])
|
||||
if len(name) > 1 and name[0] == '"' and name[-1] == '"':
|
||||
name = name[1:-1]
|
||||
|
||||
if name in self._absent_names:
|
||||
return handle, None
|
||||
else:
|
||||
return handle, name
|
||||
|
||||
def _get_this_handle_and_name(self, line):
|
||||
fields = line.split(":")
|
||||
return self._get_handle_and_name(":".join(fields[1:]))
|
||||
|
||||
def _get_descendant_handle_and_name(self, line):
|
||||
match = self._name_pattern.search(line)
|
||||
if match:
|
||||
return self._get_handle_and_name(line[:match.start()].strip())
|
||||
else:
|
||||
raise OSError, "Window information from %r did not contain window details." % line
|
||||
|
||||
def _descendants(self, s, fn):
|
||||
handles = []
|
||||
adding = 0
|
||||
for line in s.split("\n"):
|
||||
if line.endswith("child:") or line.endswith("children:"):
|
||||
if not adding:
|
||||
adding = 1
|
||||
elif adding and line:
|
||||
handle, name = self._get_descendant_handle_and_name(line)
|
||||
if fn(name):
|
||||
handles.append(handle)
|
||||
return [Window(handle) for handle in handles]
|
||||
|
||||
# Public methods.
|
||||
|
||||
def children(self, all=0):
|
||||
|
||||
"""
|
||||
Return a list of windows which are children of this window. If the
|
||||
optional 'all' parameter is set to a true value, all such windows will
|
||||
be returned regardless of whether they have any name information.
|
||||
"""
|
||||
|
||||
s = _xwininfo(self.identifier, "children")
|
||||
return self._descendants(s, all and self.find_all or self.find_named)
|
||||
|
||||
def descendants(self, all=0):
|
||||
|
||||
"""
|
||||
Return a list of windows which are descendants of this window. If the
|
||||
optional 'all' parameter is set to a true value, all such windows will
|
||||
be returned regardless of whether they have any name information.
|
||||
"""
|
||||
|
||||
s = _xwininfo(self.identifier, "tree")
|
||||
return self._descendants(s, all and self.find_all or self.find_named)
|
||||
|
||||
def find(self, callable):
|
||||
|
||||
"""
|
||||
Return windows using the given 'callable' (returning a true or a false
|
||||
value when invoked with a window name) for descendants of this window.
|
||||
"""
|
||||
|
||||
s = _xwininfo(self.identifier, "tree")
|
||||
return self._descendants(s, callable)
|
||||
|
||||
def name(self):
|
||||
|
||||
"Return the name of the window."
|
||||
|
||||
d = _xwininfo(self.identifier, "stats")
|
||||
|
||||
# Format is 'xwininfo: Window id: <handle> "<name>"
|
||||
|
||||
return self._get_this_handle_and_name(d["xwininfo"])[1]
|
||||
|
||||
def size(self):
|
||||
|
||||
"Return a tuple containing the width and height of this window."
|
||||
|
||||
d = _xwininfo(self.identifier, "stats")
|
||||
return _get_int_properties(d, ["Width", "Height"])
|
||||
|
||||
def position(self):
|
||||
|
||||
"Return a tuple containing the upper left co-ordinates of this window."
|
||||
|
||||
d = _xwininfo(self.identifier, "stats")
|
||||
return _get_int_properties(d, ["Absolute upper-left X", "Absolute upper-left Y"])
|
||||
|
||||
def displayed(self):
|
||||
|
||||
"""
|
||||
Return whether the window is displayed in some way (but not necessarily
|
||||
visible on the current screen).
|
||||
"""
|
||||
|
||||
d = _xwininfo(self.identifier, "stats")
|
||||
return d["Map State"] != "IsUnviewable"
|
||||
|
||||
def visible(self):
|
||||
|
||||
"Return whether the window is displayed and visible."
|
||||
|
||||
d = _xwininfo(self.identifier, "stats")
|
||||
return d["Map State"] == "IsViewable"
|
||||
|
||||
def list(desktop=None):
|
||||
|
||||
"""
|
||||
Return a list of windows for the current desktop. If the optional 'desktop'
|
||||
parameter is specified then attempt to use that particular desktop
|
||||
environment's mechanisms to look for windows.
|
||||
"""
|
||||
|
||||
root_window = root(desktop)
|
||||
window_list = [window for window in root_window.descendants() if window.displayed()]
|
||||
window_list.insert(0, root_window)
|
||||
return window_list
|
||||
|
||||
def root(desktop=None):
|
||||
|
||||
"""
|
||||
Return the root window for the current desktop. If the optional 'desktop'
|
||||
parameter is specified then attempt to use that particular desktop
|
||||
environment's mechanisms to look for windows.
|
||||
"""
|
||||
|
||||
# NOTE: The desktop parameter is currently ignored and X11 is tested for
|
||||
# NOTE: directly.
|
||||
|
||||
if _is_x11():
|
||||
return Window(None)
|
||||
else:
|
||||
raise OSError, "Desktop '%s' not supported" % use_desktop(desktop)
|
||||
|
||||
def find(callable, desktop=None):
|
||||
|
||||
"""
|
||||
Find and return windows using the given 'callable' for the current desktop.
|
||||
If the optional 'desktop' parameter is specified then attempt to use that
|
||||
particular desktop environment's mechanisms to look for windows.
|
||||
"""
|
||||
|
||||
return root(desktop).find(callable)
|
||||
|
||||
# vim: tabstop=4 expandtab shiftwidth=4
|
Reference in New Issue
Block a user