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

« back to all changes in this revision

Viewing changes to plugin_processinfo.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:
6
6
#
7
7
#  Author: Kaivalagi
8
8
# Created: 21/04/2009
9
 
from htmlentitydefs import name2codepoint, codepoint2name
10
9
from optparse import OptionParser
 
10
from plugin_common import getTypedValue
11
11
import codecs
12
 
import statgrab
13
12
import fileinput
14
13
import logging
15
14
import os
16
15
import re
17
16
import shutil
18
17
import socket
 
18
import statgrab
19
19
import subprocess
20
20
import traceback
21
21
 
39
39
 
40
40
    def __cmp__(self, other):
41
41
        return cmp(self.cpu, other.cpu)
42
 
    
 
42
 
43
43
    def __str__(self):
44
44
        return str(self.name + " - " + self.cpu + " - " + self.memory)
45
 
    
 
45
 
46
46
class Output:
47
 
    
 
47
 
48
48
    output = u""
49
49
    error = u""
50
50
    totalkilobytes = None
51
 
    
 
51
 
52
52
    def __init__(self, options):
53
53
        self.options = options
54
54
        self.logger = logging.getLogger(app_name+"."+module_name)
55
55
        self.loadConfigData()
56
 
            
 
56
 
57
57
    def loadConfigData(self):
58
 
        try:         
 
58
        try:
59
59
 
60
60
            self.config = ProcessInfoConfig()
61
 
            
 
61
 
62
62
            if self.options.config != None:
63
63
                # load the config based on options passed in from the main app
64
64
                configfilepath = self.options.config
65
65
            else:
66
66
                # load plugin config from home directory of the user
67
67
                configfilepath = os.path.join(os.path.expanduser('~'), ".config/"+app_name+"/"+module_name+".config")
68
 
                            
 
68
 
69
69
            if os.path.exists(configfilepath):
70
 
                
 
70
 
71
71
                self.logger.info("Loading config settings from \"%s\""%configfilepath)
72
 
                
 
72
 
73
73
                for line in fileinput.input(os.path.expanduser(configfilepath)):
74
74
                    line = line.strip()
75
75
                    if len(line) > 0 and line[0:1] != "#": # ignore commented lines or empty ones
76
 
    
 
76
 
77
77
                        name = line.split("=")[0].strip().upper() # config setting name on the left of =
78
78
                        value = line.split("=")[1].split("#")[0].strip() # config value on the right of = (minus any trailing comments)
79
 
    
 
79
 
80
80
                        if len(value) > 0:
81
81
                            if name == "HEADERTEMPLATE":
82
 
                                self.config.HEADERTEMPLATE = self.getTypedValue(value, "string")                            
 
82
                                self.config.HEADERTEMPLATE = getTypedValue(value, "string")
83
83
                            elif name == "TEMPLATE":
84
 
                                self.config.TEMPLATE = self.getTypedValue(value, "string")
 
84
                                self.config.TEMPLATE = getTypedValue(value, "string")
85
85
                            elif name == "LIMIT":
86
 
                                self.config.LIMIT = self.getTypedValue(value, "integer")
 
86
                                self.config.LIMIT = getTypedValue(value, "integer")
87
87
                            elif name == "SORTBY":
88
 
                                self.config.SORTBY = self.getTypedValue(value, "string")
 
88
                                self.config.SORTBY = getTypedValue(value, "string")
89
89
                            else:
90
90
                                self.logger.error("Unknown option in config file: " + name)
91
91
            else:
92
92
                self.logger.info("Config data file %s not found, using defaults and setting up config file for next time" % configfilepath)
93
 
                
 
93
 
94
94
                userconfigpath = os.path.join(os.path.expanduser('~'), ".config/"+app_name+"/")
95
95
                configsource = os.path.join(app_path, "config/"+module_name+".config")
96
 
                
 
96
 
97
97
                if os.path.exists(userconfigpath) == False:
98
98
                    os.makedirs(userconfigpath)
99
99
 
100
100
                shutil.copy(configsource, configfilepath)
101
 
                
 
101
 
102
102
        except Exception, e:
103
103
            self.logger.error(e.__str__()+"\n"+traceback.format_exc())
104
104
 
105
 
    def getTypedValue(self, value, expectedtype):
106
 
        
107
 
        try:
108
 
            if len(value.strip(" ")) == 0:
109
 
                return None
110
 
            
111
 
            elif value.lower() == "true":
112
 
                if expectedtype == "boolean":
113
 
                    return True
114
 
                else:
115
 
                    self.logger.error("Expected type was '%s', but the value '%s' was given"%(expectedtype, value))
116
 
                    
117
 
            elif value.lower() == "false":
118
 
                if expectedtype == "boolean":
119
 
                    return False
120
 
                else:
121
 
                    self.logger.error("Expected type was '%s', but the value '%s' was given"%(expectedtype, value))
122
 
                    
123
 
            elif self.isNumeric(value) == True:
124
 
                if expectedtype == "integer":
125
 
                    return int(value)
126
 
                else:
127
 
                    self.logger.error("Expected type was '%s', but the value '%s' was given"%(expectedtype, value))
128
 
                    
129
 
            else:
130
 
                return value
131
 
 
132
 
        except (TypeError, ValueError):
133
 
            self.logger.error("Cannot convert '%s' to expected type of '%s'"%(value,expectedtype))
134
 
            return value
135
 
 
136
105
    def BIGBANGgetOutputData(self):
137
106
        try:
138
 
                
 
107
 
139
108
            processInfoList = statgrab.sg_get_process_stats()
140
 
                        
 
109
 
141
110
            currentid = os.getpid()
142
111
            processInfoDataList = []
143
112
            for processInfo in processInfoList:
145
114
                id = processInfo['pid']
146
115
                cpu = processInfo['cpu_percent']
147
116
                memory = processInfo['proc_size']
148
 
                
 
117
 
149
118
                if currentid != id: # ignore info on this process
150
119
                    processInfoData = ProcessInfoData(name,str(id),self.formatCPU(cpu),self.formatMemory(memory))
151
120
                    processInfoDataList.append(processInfoData)
152
 
            
 
121
 
153
122
            if self.config.SORTBY == "CPU":
154
123
                processInfoDataList.sort(lambda x, y: cmp(x.cpu, y.cpu))
155
124
            elif self.config.SORTBY == "MEM":
156
125
                processInfoDataList.sort(lambda x, y: cmp(x.memory, y.memory))
157
126
            else:
158
127
                processInfoDataList.sort()
159
 
            
 
128
 
160
129
            processInfoDataList.reverse()
161
 
            
 
130
 
162
131
            return processInfoDataList
163
132
 
164
133
        except Exception, e:
165
 
            self.logger.error(e.__str__()+"\n"+traceback.format_exc())      
 
134
            self.logger.error(e.__str__()+"\n"+traceback.format_exc())
166
135
            return []
167
136
 
168
137
    def BIGBANGformatCPU(self, value, dp=2):
173
142
 
174
143
    def BIGBANGformatMemory(self, value):
175
144
        return str(value / 1024)+"KB"
176
 
                
 
145
 
177
146
    def getOutputData(self):
178
147
        try:
179
 
            
 
148
 
180
149
            processInfoDataList = []
181
 
            
 
150
 
182
151
            proc = subprocess.Popen("cat /proc/meminfo | grep MemTotal:", shell=True, stdout=subprocess.PIPE)
183
152
            self.totalkilobytes = float(proc.communicate()[0][10:].replace("kB","").strip(" "))
184
 
                    
 
153
 
185
154
            proc = subprocess.Popen("top -n 1 -b | sed '1,7d'", shell=True, stdout=subprocess.PIPE)
186
 
            lines = proc.communicate()[0].rstrip("\n").split("\n")            
 
155
            lines = proc.communicate()[0].rstrip("\n").split("\n")
187
156
 
188
157
            for line in lines:
189
158
                name = line[61:].strip(" ")
190
159
                id = int(line[:6].strip(" "))
191
160
                cpu = float(line[43:46].strip(" "))
192
161
                memory = float(line[47:50].strip(" "))
193
 
                
 
162
 
194
163
                if cpu > 0 or memory > 0:
195
164
                    processInfoData = ProcessInfoData(name,id,cpu,memory)
196
 
                    processInfoDataList.append(processInfoData)                
197
 
            
 
165
                    processInfoDataList.append(processInfoData)
 
166
 
198
167
            if self.config.SORTBY == "CPU":
199
168
                processInfoDataList.sort(lambda x, y: cmp(x.cpu, y.cpu))
200
169
            elif self.config.SORTBY == "MEM":
201
170
                processInfoDataList.sort(lambda x, y: cmp(x.memory, y.memory))
202
171
            else:
203
172
                processInfoDataList.sort()
204
 
            
 
173
 
205
174
            processInfoDataList.reverse()
206
 
            
 
175
 
207
176
            return processInfoDataList
208
177
 
209
178
        except Exception, e:
210
 
            self.logger.error(e.__str__()+"\n"+traceback.format_exc())      
 
179
            self.logger.error(e.__str__()+"\n"+traceback.format_exc())
211
180
            return []
212
181
 
213
182
    def formatMemory(self, percent, total):
214
 
        
 
183
 
215
184
        size = total * (percent / 100)
216
185
        unit = "KB"
217
 
        
 
186
 
218
187
        if size > 1024:
219
188
            size = size / 1024.0
220
189
            unit = "MB"
221
 
            
 
190
 
222
191
        return str(round(size,2))+unit
223
192
 
224
193
    def formatCPU(self, percent):
225
194
        return str(round(percent,2))+"%"
226
 
        
 
195
 
227
196
    def getOutputFromTemplate(self, template, name, id, cpu, memory):
228
 
        
 
197
 
229
198
        try:
230
 
            
 
199
 
231
200
            output = template
232
 
    
 
201
 
233
202
            if name == None or name.strip() == "":
234
203
                name = ""
235
204
            output = output.replace("[name]",name)
236
 
                
 
205
 
237
206
            if id == None or id.strip() == "":
238
207
                id = ""
239
208
            output = output.replace("[id]",id)
240
 
            
 
209
 
241
210
            if cpu == None or cpu.strip() == "":
242
211
                cpu = ""
243
212
            output = output.replace("[cpu]",cpu)
244
 
                
 
213
 
245
214
            if memory == None or memory.strip() == "":
246
215
                memory = ""
247
216
            output = output.replace("[memory]",memory)
249
218
            # get rid of any excess crlf's and add just one
250
219
            #output = output.rstrip(" \n")
251
220
            #output = output + "\n"
252
 
            
 
221
 
253
222
            return output.encode("utf-8")
254
 
        
 
223
 
255
224
        except Exception,e:
256
225
            self.logger.error(e.__str__()+"\n"+traceback.format_exc())
257
226
            return ""
258
 
    
 
227
 
259
228
    def getOutput(self):
260
229
 
261
230
        if self.options.noheader == True:
280
249
            headertemplate = inputfile.read()
281
250
        finally:
282
251
            inputfile.close()
283
 
            
 
252
 
284
253
        if self.options.template != None:
285
254
            templatefilepath = self.options.template
286
255
            self.logger.info("Using custom template file '%s'"%templatefilepath)
290
259
        else:
291
260
            templatefilepath = app_path+"/templates/processinfo.template"
292
261
            self.logger.info("Using default template")
293
 
             
 
262
 
294
263
        # load the file
295
264
        try:
296
265
            inputfile = codecs.open(os.path.expanduser(templatefilepath), encoding='utf-8')
302
271
            inputfile.close()
303
272
 
304
273
        output = headertemplate
305
 
        
 
274
 
306
275
        count = 0
307
276
        processInfoDataList = self.getOutputData()
308
 
        
 
277
 
309
278
        if len(processInfoDataList) == 0:
310
279
            output = "Failed to retrieve process information"
311
280
        else:
312
281
            for processInfoData in processInfoDataList:
313
282
                count += 1
314
283
                output = output + self.getOutputFromTemplate(template, processInfoData.name, str(processInfoData.id), self.formatCPU(processInfoData.cpu), self.formatMemory(processInfoData.memory, self.totalkilobytes))
315
 
    
 
284
 
316
285
                if self.config.LIMIT != None and count >= self.config.LIMIT:
317
 
                    break  
318
 
            
 
286
                    break
 
287
 
319
288
        return output.encode("utf-8")
320
289
 
321
 
    def getHTMLText(self,text):
322
 
        try:
323
 
            htmlentities = []               
324
 
            for char in text: #html:
325
 
                if ord(char) < 128:
326
 
                    htmlentities.append(char)
327
 
                else:
328
 
                    htmlentities.append('&%s;' % codepoint2name[ord(char)])
329
 
            html = "".join(htmlentities)
330
 
            
331
 
            html = html.replace("\n","<br>\n") # switch out new line for html breaks
332
 
            return html            
333
 
        except:
334
 
            return text
335
 
 
336
 
    def getCleanText(self,html):
337
 
        try:
338
 
            text = str(html)
339
 
            text = text.replace("\n","") # remove new lines from html
340
 
            text = text.replace("&apos;","'") # workaround for shitty xml codes not compliant with html
341
 
            text = text.replace("<br>","\n") # switch out html breaks for new line
342
 
            text = re.sub('<(.|\n)+?>','',text) # remove any html tags
343
 
            text =  re.sub('&(%s);' % '|'.join(name2codepoint), lambda m: chr(name2codepoint[m.group(1)]), text)
344
 
            return text            
345
 
        except:
346
 
            return html
347
 
    
348
 
    def isNumeric(self,value):
349
 
        try:
350
 
            temp = int(value)
351
 
            return True
352
 
        except:
353
 
            return False
354
 
 
355
290
def getHTML(options):
356
291
    output = Output(options)
357
292
    html = output.getOutput()
360
295
 
361
296
# to enable testing in isolation
362
297
if __name__ == "__main__":
363
 
    
 
298
 
364
299
    parser = OptionParser()
365
 
    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")        
 
300
    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")
366
301
    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.")
367
302
    parser.add_option("--template", dest="template", type="string", metavar="FILE", help=u"Override the template for the plugin, default or config based template ignored.")
368
303
    parser.add_option("--verbose", dest="verbose", default=False, action="store_true", help=u"Outputs verbose info to the terminal")
369
304
    parser.add_option("--version", dest="version", default=False, action="store_true", help=u"Displays the version of the script.")
370
 
    parser.add_option("--logfile", dest="logfile", type="string", metavar="FILE", help=u"If a filepath is set, the script logs to the filepath.")                
371
 
    
 
305
    parser.add_option("--logfile", dest="logfile", type="string", metavar="FILE", help=u"If a filepath is set, the script logs to the filepath.")
 
306
 
372
307
    (options, args) = parser.parse_args()
373
 
        
 
308
 
374
309
    output = Output(options)
375
310
    html = output.getOutput()
376
311
    del output