~m-buck/+junk/gtk-desktop-info

« back to all changes in this revision

Viewing changes to gtk_desktop_info.py

  • Committer: Mark Buck (Kaivalagi)
  • Date: 2009-06-19 17:13:00 UTC
  • Revision ID: m_buck@hotmail.com-20090619171300-5cbhr90xwg62z27y
Added --backgroundblend and --backgroundcolour options for visual seperation of output from wallpaper if required, Fixed song length output in the rhythmbox plugin when songs are an hour long or more, Added copy option to right click, enabling the copying of html content to the clipboard for testing, Moved common functions into a plugin_common module

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
# -*- coding: utf-8 -*-
3
3
###############################################################################
4
4
# gtk-desktop-info.py is a gtk windows app used to to display various pieces of
5
 
# information, using a variety of plugins. It outputs in html format to a 
 
5
# information, using a variety of plugins. It outputs in html format to a
6
6
# webkit view with a 'transparent' background
7
7
#
8
8
#  Author: Kaivalagi
23
23
import webkit
24
24
 
25
25
app_name = __file__.replace(os.path.dirname (__file__) + "/", "").replace(".pyc","").replace(".py", "").replace("_","-")
26
 
app_path = os.path.dirname(os.path.abspath(__file__)) 
 
26
app_path = os.path.dirname(os.path.abspath(__file__))
27
27
 
28
28
plugin = None
29
29
background = None
39
39
        self.parser.add_option("--plugin", dest="plugin", default="shell", type="string", metavar="NAME", help=u"default:[%default] The plugin to use, the following are currently supported: deluge, email, exaile, feedparser, forecast, googlecalendar, null, pidgin, processinfo, rhythmbox, shell, tomboy, twitter")
40
40
        self.parser.add_option("--css", dest="css", type="string", metavar="FILE", help=u"Override the css stylesheet file path to use for html output")
41
41
        self.parser.add_option("--colour", dest="colour", type="string", default="default", metavar="NAME", help=u"default:[%default] Override the default css stylesheet colour, if css is overridden this option will not apply. Colour names supported are: default, white, black, green, blue")
42
 
        self.parser.add_option("--noheader", dest="noheader", default=False, action="store_true", help=u"Turn off header output. This will override any header template setting to be nothing")        
 
42
        self.parser.add_option("--noheader", dest="noheader", default=False, action="store_true", help=u"Turn off header output. This will override any header template setting to be nothing")
43
43
        self.parser.add_option("--headertemplate", dest="headertemplate", type="string", metavar="FILE", help=u"Override the header template for the plugin, default or config based template ignored.")
44
44
        self.parser.add_option("--template", dest="template", type="string", metavar="FILE", help=u"Override the template for the plugin, default or config based template ignored.")
45
45
        self.parser.add_option("--config", dest="config", type="string", metavar="FILE", help=u"Override the config for the plugin.")
50
50
        self.parser.add_option("--y", dest="y", type="int", default=0, metavar="NUMBER", help=u"default:[%default] The required vertical placement from the top")
51
51
        self.parser.add_option("--allworkspaces", dest="allworkspaces", default=False, action="store_true", help=u"If set output will be shown on all workspaces, rather than the current one")
52
52
        self.parser.add_option("--wallpaper", dest="wallpaper", type="string", metavar="FILE", help=u"Override the wallpaper filepath in case it isn't found automatically")
53
 
        self.parser.add_option("--noresize", dest="noresize", default=False, action="store_true", help=u"Turn off resizing of the wallpaper used/found. This will override default resizing to screen res, allowing the app to have an alternative background just for itself etc")                
 
53
        self.parser.add_option("--noresize", dest="noresize", default=False, action="store_true", help=u"Turn off resizing of the wallpaper used/found. This will override default resizing to screen res, allowing the app to have an alternative background just for itself etc")
54
54
        self.parser.add_option("--windowmanager", dest="windowmanager", type="string", default="gnome", metavar="NAME", help=u"default:[%default] Tell the app which windows manager is in use, options are gnome, kde3, kde4, xfce4, compiz (compiz option is not implemented yet!)")
 
55
        self.parser.add_option("--backgroundblend", dest="backgroundblend", default="0", type="int", metavar="NUMBER", help=u"default:[%default] The optional blending ratio for the backgroundcolour which is set. The default of 0 does no blending, the maximum of 100 will blend \"out\" all the wallpaper. Use this option to give some visual separation to the output from the wallpaper.")
 
56
        self.parser.add_option("--backgroundcolour", dest="backgroundcolour", default="0,0,0", type="string", metavar="RGBValues", help=u"default:[%default] The optional colours to use for blending into the background. They should be input in the form \"R,G,B\". Not used if --backgroundblend=0")
55
57
        self.parser.add_option("--verbose", dest="verbose", default=False, action="store_true", help=u"Outputs verbose info to the terminal (and logfile if setup) for both the main app and associated plugins")
56
58
        self.parser.add_option("--version", dest="version", default=False, action="store_true", help=u"Displays the version of the script.")
57
 
        self.parser.add_option("--logfile", dest="logfile", type="string", metavar="FILE", help=u"If a filepath is set, the main app and any associated plugins log errors to the filepath. If --verbose is set, verbose info will be logged to file.")                
 
59
        self.parser.add_option("--logfile", dest="logfile", type="string", metavar="FILE", help=u"If a filepath is set, the main app and any associated plugins log errors to the filepath. If --verbose is set, verbose info will be logged to file.")
58
60
 
59
61
    def parse_args(self):
60
62
        (options, args) = self.parser.parse_args()
62
64
 
63
65
    def print_help(self):
64
66
        return self.parser.print_help()
65
 
    
 
67
 
66
68
 
67
69
class BrowserPage(webkit.WebView):
68
70
    def __init__(self):
69
71
        webkit.WebView.__init__(self)
70
72
 
71
73
class GTKDesktopInfo(gtk.Window):
72
 
    
 
74
 
73
75
    options = None
74
76
    browser = None
75
77
    background = None
79
81
    logger = None
80
82
    windowposition_x = None
81
83
    windowposition_y = None
82
 
    
 
84
 
83
85
    def __init__(self, options):
84
86
        gtk.Window.__init__(self)
85
 
        
 
87
 
86
88
        self.options = options
87
89
 
88
90
        self.createLogger()
89
91
 
90
92
        # get a crop of the background image to use for the html background
91
93
        self.generateBackground(self.options.x, self.options.y, self.options.width, self.options.height)
92
 
        
 
94
 
93
95
        self.logger.info("Using generated background image: "+self.background)
94
 
        
 
96
 
95
97
        # get plugin and html browser objects
96
98
        self.browser = BrowserPage()
97
99
        self.browser.connect("populate-popup", self.displayPopup)
98
100
        self.connect("configure-event",self.windowConfigure)
99
 
        
 
101
 
100
102
        if self.options.url == None:
101
103
            self.plugin = self.getPlugin()
102
104
 
105
107
                self.htmloverride = "<h1>Plugin %s not found</h1>"%self.options.plugin
106
108
            else:
107
109
                self.htmloverride = None
108
 
        
 
110
 
109
111
            # setup default css for plugin if required, applying the colour option
110
112
            if self.options.css == None:
111
113
                self.options.css = app_path+"/style/plugin_"+self.options.plugin+"_"+self.options.colour+".css"
112
114
        else:
113
115
            self.htmloverride = None
114
 
        
 
116
 
115
117
        self.updateInfo()
116
 
        
 
118
 
117
119
        self.logger.info("Outputting the following HTML:\n%s"%self.html)
118
 
        
 
120
 
119
121
        # setup the window and show
120
122
        #infowindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
121
123
        self.add(self.browser)
122
 
        
 
124
 
123
125
        self.set_keep_below(True)
124
126
        self.set_decorated(False)
125
127
        self.set_skip_pager_hint(True)
126
128
        self.set_skip_taskbar_hint(True)
127
129
        self.set_double_buffered(True)
128
 
        
 
130
 
129
131
        self.set_default_size(self.options.width, self.options.height)
130
132
        self.set_gravity(gtk.gdk.GRAVITY_NORTH_WEST)
131
 
        
 
133
 
132
134
        self.move(self.options.x,self.options.y)
133
 
        
 
135
 
134
136
        # check to see whether we need to make the app "stick" on all workspaces or not
135
137
        if self.options.allworkspaces == True:
136
138
            self.stick()
137
 
            
 
139
 
138
140
        self.show_all()
139
 
        
 
141
 
140
142
        if self.options.interval > 0:
141
143
            # time to setup a refresh of the browser
142
144
            gobject.timeout_add(self.options.interval*1000, self.updateInfo)
143
145
 
144
146
    def updateInfo(self, widget=None):
145
 
                    
 
147
 
146
148
        if self.options.url != None:
147
149
            self.browser.open(self.options.url)
148
 
            return True          
 
150
            return True
149
151
        else:
150
152
            if self.htmloverride != None:
151
153
                self.browser.load_string(self.htmloverride, "text/html", "iso-8859-15", "about:")
152
154
                return False
153
155
            else:
154
 
                    
 
156
 
155
157
                pluginhtml = self.plugin.getHTML(self.options)
156
 
                
 
158
 
157
159
                if self.background != None:
158
160
                    self.html = u"<html>\n<head>\n<META HTTP-EQUIV='Pragma' CONTENT='no-cache'>\n<link type=\"text/css\" rel=\"stylesheet\" media=\"all\" href=\"file://"+self.options.css+"\"/>\n</head>\n<body background=\"file://"+self.background+"\" style=\"background-attachment:fixed;\">\n"+pluginhtml+"\n</body>\n</html>\n"
159
161
                else:
160
162
                    self.html = u"<html>\n<head>\n<META HTTP-EQUIV='Pragma' CONTENT='no-cache'>\n<link type=\"text/css\" rel=\"stylesheet\" media=\"all\" href=\"file://"+self.options.css+"\"/>\n</head>\n<body>\n"+pluginhtml+"\n</body>\n</html>\n"
161
 
                
 
163
 
162
164
                self.browser.load_string(self.html, "text/html", "iso-8859-15", "about:")
163
 
                
 
165
 
164
166
                return True
165
167
 
166
168
    def updateBackgroundAndInfo(self, widget=None):
167
169
        self.generateBackground(self.options.x,self.options.y, self.options.width, self.options.height)
168
170
        self.updateInfo(widget)
169
 
        
 
171
 
 
172
    def copyContentToClipboard(self, widget=None):
 
173
        clipboard = gtk.clipboard_get()
 
174
        clipboard.set_text(self.html)
 
175
        clipboard.store()
 
176
 
170
177
    def displayPopup(self, view, menu):
171
 
        
 
178
 
172
179
        # remove all menu items from the popup, we want our own only!
173
180
        items = menu.get_children()
174
181
        for item in items:
175
182
            menu.remove(item)
176
 
        
 
183
 
177
184
        # add menu items as required
178
185
        quititem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
179
186
        menu.append(quititem)
180
187
        quititem.connect('activate', self.destroy)
181
 
        
 
188
 
182
189
        reloaditem = gtk.ImageMenuItem(gtk.STOCK_REFRESH)
183
190
        menu.append(reloaditem)
184
191
        reloaditem.connect('activate', self.updateBackgroundAndInfo)
185
 
        
 
192
 
 
193
        copycontent = gtk.ImageMenuItem(gtk.STOCK_COPY)
 
194
        menu.append(copycontent)
 
195
        copycontent.connect('activate', self.copyContentToClipboard)
 
196
 
186
197
        menu.show_all()
187
198
 
188
199
    def windowConfigure(self, window, event):
189
 
        
 
200
 
190
201
        if (self.options.x,self.options.y) != window.get_position():
191
202
            (self.options.x,self.options.y) = window.get_position()
192
203
            output = "Window moved to %s,%s"%(self.options.x,self.options.y)
193
204
            print output
194
205
            self.logger.info(output)
195
206
            self.updateBackgroundAndInfo()
196
 
        
 
207
 
197
208
    def generateBackground(self, x, y, width, height):
198
 
        
 
209
 
199
210
        try:
200
 
                  
 
211
 
201
212
            wallpaper = self.getWallpaperPath()
202
 
            
 
213
 
203
214
            if os.path.exists(wallpaper) == True:
204
215
                img = Image.open(wallpaper)
205
 
                
 
216
 
206
217
                if self.options.noresize == False:
207
218
                    # scale the image to the screen resolution if not the same size
208
 
                    # this will not cater for all situations, but until retrieving the 
 
219
                    # this will not cater for all situations, but until retrieving the
209
220
                    # root pixmap via Xlib is working this is the best option
210
221
                    disp = display.Display()
211
222
                    scr = disp.screen()
212
223
                    screensize = (scr.width_in_pixels, scr.height_in_pixels)
213
224
                    if img.size != screensize:
214
225
                        img = img.resize(screensize, Image.BICUBIC)
215
 
                
 
226
 
216
227
                    # crop the screen res based image and save for use
217
228
                    box = (x, y, x+width, y+height)
218
229
                else:
219
230
                    # crop the un-resized image based on the top left corner
220
231
                    box = (0, 0, width, height)
 
232
 
 
233
                img = img.crop(box)
 
234
 
 
235
                # get blending options and blend background into wallpaper
 
236
                # only blend if necessary
 
237
                
 
238
                try:
 
239
                    blend = self.options.backgroundblend / 100.0
 
240
                except:
 
241
                    self.logger.error("Failed to interpret backgroundblend option, using default of 0...")
 
242
                    blend = 0
221
243
                    
222
 
                newimg = img.crop(box)
 
244
                if blend != 0:    
 
245
           
 
246
                    try:
 
247
                        colours = self.options.backgroundcolour.split(",")
 
248
                        colour = (int(colours[0]),int(colours[1]),int(colours[2]))
 
249
                    except:
 
250
                        self.logger.error("Failed to interpret backgroundcolour option, using default of 0,0,0...")
 
251
                        colour = (0,0,0)
 
252
                      
 
253
                    mask = Image.new(img.mode, img.size, colour)
 
254
                    img = Image.blend(img, mask, blend)
 
255
 
 
256
                # save the img to use
223
257
                tempfile = "/tmp/"+self.options.plugin+"_"+str(uuid.uuid1())+".jpg"
224
 
                newimg.save(tempfile)
225
 
                
 
258
                img.save(tempfile)
 
259
 
226
260
                # delete the old background image if there
227
261
                if self.background != None and os.path.exists(self.background):
228
262
                    os.remove(self.background)
231
265
            else:
232
266
                self.logger.error("Invalid wallpaper, using nothing...")
233
267
                self.background = ""
234
 
                                
 
268
 
235
269
        except Exception, e:
236
270
            self.logger.error("Failed to setup a wallpaper, using nothing..."+"\n"+traceback.format_exc())
237
271
            self.background = ""
238
 
    
 
272
 
239
273
    def getWallpaperPath(self):
240
274
 
241
275
        if self.options.wallpaper != None:
242
276
            # handle home directory if passed
243
277
            return os.path.expanduser(self.options.wallpaper)
244
278
        else:
245
 
            
 
279
 
246
280
            # may need the screen number
247
281
            #disp = display.Display()
248
 
            #scr = disp.screen()            
249
 
            
 
282
            #scr = disp.screen()
 
283
 
250
284
            if self.options.windowmanager.lower() == "gnome":
251
285
                # use the nautilus wallpaper
252
286
                gconf_path_wallpaper = '/desktop/gnome/background/'
253
287
                client  = gconf.client_get_default()
254
288
                return client.get_string(gconf_path_wallpaper + 'picture_filename' )
255
 
            
 
289
 
256
290
            elif self.options.windowmanager.lower() == "kde3":
257
291
                return self.getShellCommandOutput("dcop kdesktop KBackgroundIface currentWallpaper 0")
258
 
            
 
292
 
259
293
            elif self.options.windowmanager.lower() == "kde4":
260
294
                return self.getShellCommandOutput("grep 'wallpaper=' ~/.kde/share/config/plasma-appletsrc | tail --bytes=+11")
261
 
                    
 
295
 
262
296
            elif self.options.windowmanager.lower() == "xfce4":
263
297
                wallpaper = ""
264
298
                desktopxml = minidom.parse(os.path.expanduser("~/.config/xfce4/mcs_settings/desktop.xml")).documentElement
267
301
                        wallpaper = option.attributes["value"].value
268
302
                        break
269
303
                return wallpaper
270
 
            
 
304
 
271
305
            elif self.options.windowmanager.lower() == "compiz":
272
306
                #TODO: implement compiz desktop wallpaper setting interrogation
273
307
                return ""
274
 
            
 
308
 
275
309
            else:
276
310
                self.logger.error("Invalid window manager option of \"%s\"set, using no background..."%self.options.windowmanager)
277
 
                return "" 
 
311
                return ""
278
312
 
279
313
    def getShellCommandOutput(self, shell_command):
280
314
 
281
315
        self.logger.info("Running shell command '%s'"%shell_command)
282
 
        
 
316
 
283
317
        proc = subprocess.Popen(shell_command,
284
318
                           shell=True,
285
319
                           stdout=subprocess.PIPE,
287
321
        output = proc.communicate()[0].rstrip("\n")
288
322
 
289
323
        return output
290
 
            
 
324
 
291
325
    def getPlugin(self):
292
 
        
 
326
 
293
327
        # get the plugin class as required
294
328
        try:
295
329
            plugin = __import__("plugin_"+ options.plugin)
301
335
            return plugin
302
336
 
303
337
    def createLogger(self):
304
 
    
 
338
 
305
339
        self.logger = logging.getLogger(app_name)
306
 
        
 
340
 
307
341
        self.logger.setLevel(logging.DEBUG)
308
342
        formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
309
 
        
 
343
 
310
344
        #create console handler and set level to info
311
345
        ch = logging.StreamHandler()
312
346
        if self.options.verbose == True:
317
351
        self.logger.addHandler(ch)
318
352
 
319
353
        if self.options.logfile != None:
320
 
                
 
354
 
321
355
            #create file handler and set level to debug
322
356
            fh = logging.FileHandler(self.options.logfile)
323
357
            if self.options.verbose == True:
324
358
                fh.setLevel(logging.DEBUG)
325
359
            else:
326
 
                fh.setLevel(logging.ERROR)        
 
360
                fh.setLevel(logging.ERROR)
327
361
            fh.setFormatter(formatter)
328
362
            self.logger.addHandler(fh)
329
363
 
330
364
    def main(self):
331
365
        gtk.main()
332
 
                    
 
366
 
333
367
    def destroy(self, widget, data=None):
334
368
        gtk.main_quit()
335
 
        
 
369
 
336
370
        logging.shutdown()
337
 
        
 
371
 
338
372
        # delete the background image
339
373
        if os.path.exists(self.background):
340
374
            os.remove(self.background)
341
 
            
 
375
 
342
376
if __name__ == "__main__":
343
 
    
 
377
 
344
378
    parser = CommandLineParser()
345
379
    (options, args) = parser.parse_args()
346
380
 
347
381
    if options.version == True:
348
 
        print >> sys.stdout,"gnome-desktop-info v.0.19"
 
382
        print >> sys.stdout,"gnome-desktop-info v.0.20"
349
383
    else:
350
384
        if options.verbose == True:
351
385
            print >> sys.stdout, "*** INITIAL OPTIONS:"
352
386
            print >> sys.stdout, "    url:", options.url
353
387
            print >> sys.stdout, "    plugin:", options.plugin
354
 
            print >> sys.stdout, "    css:", options.css            
 
388
            print >> sys.stdout, "    css:", options.css
355
389
            print >> sys.stdout, "    interval:", options.interval
356
390
            print >> sys.stdout, "    width:", options.width
357
391
            print >> sys.stdout, "    height:", options.height
363
397
        info = GTKDesktopInfo(options)
364
398
        info.main()
365
399
 
366
 
                
 
400