~ubuntu-branches/ubuntu/utopic/dogtail/utopic

« back to all changes in this revision

Viewing changes to dogtail/utils.py

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2006-12-21 13:33:47 UTC
  • mfrom: (1.2.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20061221133347-xo9jg11afp5plcka
Tags: upstream-0.6.1
ImportĀ upstreamĀ versionĀ 0.6.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
"""
7
7
 
8
8
__author__ = """Ed Rousseau <rousseau@redhat.com>,
9
 
Zack Cerza <zcerza@redhat.com, 
 
9
Zack Cerza <zcerza@redhat.com,
10
10
David Malcolm <dmalcolm@redhat.com>
11
11
"""
12
12
 
18
18
from logging import TimeStamp
19
19
from errors import DependencyNotFoundError
20
20
 
21
 
def screenshot(windowname = 'root', file = 'screenshot.png', timeStamp = True, args = ''):
22
 
        """
23
 
        This function wraps the ImageMagick import command to take a screenshot. 
24
 
        
25
 
        The file argument may be specified as 'foo', 'foo.png', or using any other 
26
 
        extension that ImageMagick supports. PNG is the default.
27
 
        
28
 
        By default, screenshot filenames are in the format of foo_YYYYMMDD-hhmmss.png .
29
 
        The timeStamp argument may be set to False to name the file foo.png.
30
 
        """
31
 
        IMVer = os.popen('import -version').readline()
32
 
        IMVer = re.match('Version: ImageMagick ([0-9\.]+) .*', IMVer)
33
 
        if IMVer is None: 
34
 
                raise DependencyNotFoundError, "ImageMagick"
35
 
        
36
 
        # config is supposed to create this for us. If it's not there, bail.
37
 
        assert os.path.isdir(config.scratchDir)
38
 
        
39
 
        if windowname == '':
40
 
                windowname = "root"
41
 
 
42
 
        baseName = ''.join(file.split('.')[0:-1])
43
 
        fileExt = file.split('.')[-1]
44
 
        if not baseName: 
45
 
                baseName = file
46
 
                fileExt = 'png'
47
 
        
48
 
        if timeStamp:
49
 
                ts = TimeStamp()
50
 
                newFile = ts.fileStamp(baseName) + '.' + fileExt
51
 
                path = config.scratchDir + newFile
52
 
        else:
53
 
                newFile = baseName + '.' + fileExt
54
 
                path = config.scratchDir + newFile
55
 
        
56
 
        print path
57
 
        
58
 
        # Generate the command and redirect STDERR to STDOUT
59
 
        # This really needs window manipulation and pyspi state binding to be done
60
 
        # to actually be really useful
61
 
        answer = []
62
 
        cmd = "import -window '%s' %s %s 2>&1" % (windowname, path, args)
63
 
        answer = os.popen(cmd).readlines()
64
 
 
65
 
        # If successful we should get nothing back. If not something went wrong
66
 
        # and our mouse is now probably unusable
67
 
        if answer:
68
 
                raise ValueError, "Screenshot failed: " + answer[-1]
69
 
        assert os.path.exists(path)
70
 
        logger.log("Screenshot taken: " + path)
71
 
        return path
 
21
def screenshot(file = 'screenshot.png', timeStamp = True):
 
22
    """
 
23
    This function wraps the ImageMagick import command to take a screenshot.
 
24
 
 
25
    The file argument may be specified as 'foo', 'foo.png', or using any other
 
26
    extension that ImageMagick supports. PNG is the default.
 
27
 
 
28
    By default, screenshot filenames are in the format of foo_YYYYMMDD-hhmmss.png .
 
29
    The timeStamp argument may be set to False to name the file foo.png.
 
30
    """
 
31
    if not isinstance(timeStamp, bool):
 
32
        raise TypeError, "timeStampt must be True or False"
 
33
    # config is supposed to create this for us. If it's not there, bail.
 
34
    assert os.path.isdir(config.scratchDir)
 
35
 
 
36
    baseName = ''.join(file.split('.')[0:-1])
 
37
    fileExt = file.split('.')[-1].lower()
 
38
    if not baseName:
 
39
        baseName = file
 
40
        fileExt = 'png'
 
41
 
 
42
    if timeStamp:
 
43
        ts = TimeStamp()
 
44
        newFile = ts.fileStamp(baseName) + '.' + fileExt
 
45
        path = config.scratchDir + newFile
 
46
    else:
 
47
        newFile = baseName + '.' + fileExt
 
48
        path = config.scratchDir + newFile
 
49
 
 
50
    import gtk.gdk
 
51
    import gobject
 
52
    rootWindow = gtk.gdk.get_default_root_window()
 
53
    geometry = rootWindow.get_geometry()
 
54
    pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, geometry[2], \
 
55
            geometry[3])
 
56
    gtk.gdk.Pixbuf.get_from_drawable(pixbuf, rootWindow, \
 
57
            rootWindow.get_colormap(), 0, 0, 0, 0, geometry[2], geometry[3])
 
58
    # gtk.gdk.Pixbuf.save() needs 'jpeg' and not 'jpg'
 
59
    if fileExt == 'jpg': fileExt = 'jpeg'
 
60
    try: pixbuf.save(path, fileExt)
 
61
    except gobject.GError:
 
62
        raise ValueError, "Failed to save screenshot in %s format" % fileExt
 
63
    assert os.path.exists(path)
 
64
    logger.log("Screenshot taken: " + path)
 
65
    return path
72
66
 
73
67
def run(string, timeout=config.runTimeout, interval=config.runInterval, desktop=None, dumb=False, appName=''):
74
 
        """
75
 
        Runs an application. [For simple command execution such as 'rm *', use os.popen() or os.system()]
76
 
        If dumb is omitted or is False, polls at interval seconds until the application is finished starting, or until timeout is reached.
77
 
        If dumb is True, returns when timeout is reached.
78
 
        """
79
 
        from os import environ, spawnvpe, P_NOWAIT
80
 
        if not desktop: from tree import root as desktop
81
 
        args = string.split()
82
 
        name = args[0]
83
 
        pid = spawnvpe (P_NOWAIT, name, args, environ)
84
 
 
85
 
        if not appName:
86
 
                appName=args[0]
87
 
        
88
 
        if dumb:
89
 
                # We're starting a non-AT-SPI-aware application. Disable startup detection.
90
 
                doDelay(timeout)
91
 
        else:
92
 
                # Startup detection code
93
 
                # The timing here is not totally precise, but it's good enough for now.
94
 
                time = 0
95
 
                while time < timeout:
96
 
                        time = time + interval
97
 
                        try:
98
 
                                for child in desktop.children[::-1]:
99
 
                                        if child.name == appName:
100
 
                                                for grandchild in child.children:
101
 
                                                        if grandchild.roleName == 'frame':
102
 
                                                                from procedural import focus
103
 
                                                                focus.application.node = child
104
 
                                                                doDelay(interval)
105
 
                                                                return pid
106
 
                        except AttributeError: pass
107
 
                        doDelay(interval)
108
 
        return pid
 
68
    """
 
69
    Runs an application. [For simple command execution such as 'rm *', use os.popen() or os.system()]
 
70
    If dumb is omitted or is False, polls at interval seconds until the application is finished starting, or until timeout is reached.
 
71
    If dumb is True, returns when timeout is reached.
 
72
    """
 
73
    from os import environ, spawnvpe, P_NOWAIT
 
74
    if not desktop: from tree import root as desktop
 
75
    args = string.split()
 
76
    name = args[0]
 
77
    environ['GTK_MODULES'] = 'gail:atk-bridge'
 
78
    pid = spawnvpe (P_NOWAIT, name, args, environ)
 
79
 
 
80
    if not appName:
 
81
        appName=args[0]
 
82
 
 
83
    if dumb:
 
84
        # We're starting a non-AT-SPI-aware application. Disable startup detection.
 
85
        doDelay(timeout)
 
86
    else:
 
87
        # Startup detection code
 
88
        # The timing here is not totally precise, but it's good enough for now.
 
89
        time = 0
 
90
        while time < timeout:
 
91
            time = time + interval
 
92
            try:
 
93
                for child in desktop.children[::-1]:
 
94
                    if child.name == appName:
 
95
                        for grandchild in child.children:
 
96
                            if grandchild.roleName == 'frame':
 
97
                                from procedural import focus
 
98
                                focus.application.node = child
 
99
                                doDelay(interval)
 
100
                                return pid
 
101
            except AttributeError: pass
 
102
            doDelay(interval)
 
103
    return pid
109
104
 
110
105
def doDelay(delay=None):
111
 
        """
112
 
        Utility function to insert a delay (with logging and a configurable
113
 
        default delay)
114
 
        """
115
 
        if delay is None:
116
 
                delay = config.defaultDelay
117
 
        if config.debugSleep:
118
 
                logger.log("sleeping for %f"%delay)
119
 
        sleep(delay)
 
106
    """
 
107
    Utility function to insert a delay (with logging and a configurable
 
108
    default delay)
 
109
    """
 
110
    if delay is None:
 
111
        delay = config.defaultDelay
 
112
    if config.debugSleep:
 
113
        logger.log("sleeping for %f" % delay)
 
114
    sleep(delay)
120
115
 
121
116
class Blinker:
122
 
        INTERVAL_MS = 200
123
 
 
124
 
        def __init__(self, x, y, w, h, count = 2):
125
 
                import gobject
126
 
                import gtk.gdk
127
 
                self.count = count
128
 
                self.x = x
129
 
                self.y = y
130
 
                self.w = w
131
 
                self.h = h
132
 
                self.timeout_handler_id = gobject.timeout_add (Blinker.INTERVAL_MS, self.blinkDrawRectangle)
133
 
                gtk.main()
134
 
 
135
 
        def blinkDrawRectangle (self):
136
 
                import gtk.gdk
137
 
                display = gtk.gdk.display_get_default()
138
 
                screen = display.get_default_screen()
139
 
                rootWindow = screen.get_root_window()
140
 
                gc = rootWindow.new_gc()
141
 
 
142
 
                gc.set_subwindow (gtk.gdk.INCLUDE_INFERIORS)
143
 
                gc.set_function (gtk.gdk.INVERT)
144
 
                gc.set_line_attributes (3, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
145
 
                rootWindow.draw_rectangle (gc, False, self.x, self.y, self.w, self.h);
146
 
 
147
 
                self.count-=1
148
 
 
149
 
                if self.count <= 0:
150
 
                        gtk.main_quit()
151
 
                        return False
152
 
                
153
 
                return True
154
 
                
 
117
    INTERVAL_MS = 200
 
118
 
 
119
    def __init__(self, x, y, w, h, count = 2):
 
120
        import gobject
 
121
        import gtk.gdk
 
122
        self.count = count
 
123
        self.x = x
 
124
        self.y = y
 
125
        self.w = w
 
126
        self.h = h
 
127
        self.timeout_handler_id = gobject.timeout_add (Blinker.INTERVAL_MS, self.blinkDrawRectangle)
 
128
        gtk.main()
 
129
 
 
130
    def blinkDrawRectangle (self):
 
131
        import gtk.gdk
 
132
        display = gtk.gdk.display_get_default()
 
133
        screen = display.get_default_screen()
 
134
        rootWindow = screen.get_root_window()
 
135
        gc = rootWindow.new_gc()
 
136
 
 
137
        gc.set_subwindow (gtk.gdk.INCLUDE_INFERIORS)
 
138
        gc.set_function (gtk.gdk.INVERT)
 
139
        gc.set_line_attributes (3, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER)
 
140
        rootWindow.draw_rectangle (gc, False, self.x, self.y, self.w, self.h);
 
141
 
 
142
        self.count-=1
 
143
 
 
144
        if self.count <= 0:
 
145
            gtk.main_quit()
 
146
            return False
 
147
 
 
148
        return True
 
149
 
 
150
 
 
151
import sys
 
152
import gconf
 
153
gconfClient = gconf.client_get_default()
 
154
a11yGConfKey = '/desktop/gnome/interface/accessibility'
 
155
 
 
156
def isA11yEnabled():
 
157
    """
 
158
    Checks if accessibility is enabled via gconf.
 
159
    """
 
160
    return gconfClient.get_bool(a11yGConfKey)
 
161
 
 
162
def bailBecauseA11yIsDisabled():
 
163
    logger.log("Dogtail requires that Assistive Technology support be enabled. Aborting...")
 
164
    sys.exit(1)
 
165
 
 
166
def enableA11y():
 
167
    """
 
168
    Enables accessibility via gconf.
 
169
    """
 
170
    return gconfClient.set_bool(a11yGConfKey, True)
 
171
 
 
172
def checkForA11y():
 
173
    """
 
174
    Checks if accessibility is enabled, and halts execution if it is not.
 
175
    """
 
176
    if not isA11yEnabled(): bailBecauseA11yIsDisabled()
 
177
 
 
178
def checkForA11yInteractively():
 
179
    """
 
180
    Checks if accessibility is enabled, and presents a dialog prompting the
 
181
    user if it should be enabled if it is not already, then halts execution.
 
182
    """
 
183
    if isA11yEnabled(): return
 
184
    import gtk
 
185
    dialog = gtk.Dialog('Enable Assistive Technology Support?',
 
186
                     None,
 
187
                     gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
 
188
                     (gtk.STOCK_QUIT, gtk.RESPONSE_CLOSE, 
 
189
                         "_Enable", gtk.RESPONSE_ACCEPT))
 
190
    question = """Dogtail requires that Assistive Technology Support be enabled for it to function. Would you like to enable Assistive Technology support now?
 
191
 
 
192
Note that you will have to log out for the change to fully take effect.
 
193
    """.strip()
 
194
    dialog.set_default_response(gtk.RESPONSE_ACCEPT)
 
195
    questionLabel = gtk.Label(question)
 
196
    questionLabel.set_line_wrap(True)
 
197
    dialog.vbox.pack_start(questionLabel)
 
198
    dialog.show_all()
 
199
    result = dialog.run()
 
200
    if result == gtk.RESPONSE_ACCEPT:
 
201
        logger.log("Enabling accessibility...")
 
202
        enableA11y()
 
203
    elif result == gtk.RESPONSE_CLOSE:
 
204
       bailBecauseA11yIsDisabled()
 
205
    dialog.destroy()
 
206