Inherits FWCore::GuiBrowsers::FileExportPlugin::FileExportPlugin.
Public Member Functions | |
def | __init__ |
def | dotIndenter |
def | export |
def | get_png_size |
def | processMap |
def | selectNode |
def | write_output |
Public Attributes | |
include_nodes | |
shapes | |
Static Public Attributes | |
tuple | file_types = ('bmp','dot','eps','gif','jpg','pdf','png','ps','svg','tif','png+map','stdout') |
dictionary | option_types |
string | plugin_name = 'DOT Export' |
Export a CMSSW config file to DOT (https://www.graphviz.org) markup, either as raw markup or by invoking the dot program, as an image.
Definition at line 261 of file DOTExport.py.
def DOTExport::DotExport::__init__ | ( | self | ) |
Definition at line 295 of file DOTExport.py.
00296 : 00297 FileExportPlugin.__init__(self) 00298 00299 #could make these customizeable in the general options dict 00300 self.shapes={} 00301 self.shapes['EDProducer']='box' 00302 self.shapes['EDFilter']='invhouse' 00303 self.shapes['EDAnalyzer']='house' 00304 self.shapes['OutputModule']='invtrapezium' 00305 self.shapes['ESSource']='Mdiamond' 00306 self.shapes['ESProducer']='Msquare' 00307 self.shapes['Source']='ellipse' 00308 self.shapes['Service']='diamond'
def DOTExport::DotExport::dotIndenter | ( | self, | |
dot | |||
) |
Simple indenter for dot output, mainly to prettify it for human reading.
Definition at line 309 of file DOTExport.py.
00310 : 00311 """ 00312 Simple indenter for dot output, mainly to prettify it for human reading. 00313 """ 00314 spaces = lambda d: ''.join([space]*d) 00315 newdot = "" 00316 depth = 0 00317 space = ' ' 00318 for line in dot.splitlines(): 00319 if '{' in line: 00320 newdot += spaces(depth)+line+'\n' 00321 depth += 1 00322 elif '}' in line: 00323 depth -= 1 00324 newdot += spaces(depth)+line+'\n' 00325 else: 00326 newdot += spaces(depth)+line+'\n' 00327 return newdot
def DOTExport::DotExport::export | ( | self, | |
data, | |||
filename, | |||
filetype | |||
) |
Definition at line 463 of file DOTExport.py.
00464 : 00465 #if not data.process(): 00466 # raise "DOTExport requires a cms.Process object" 00467 00468 #dot = self.produceDOT(data) 00469 dot_producer = DotProducer(data,self.options,self.shapes) 00470 dot = dot_producer() 00471 00472 if len(dot_producer.nodes)>0: 00473 00474 if self.options['node_graphs']: 00475 nodes = [n for n in dot_producer.nodes if data.type(dot_producer.nodes[n]['obj']) in ('EDAnalyzer','EDFilter','EDProducer','OutputModule')] 00476 for n in nodes: 00477 if self.options['node_graphs_restrict'] in n: 00478 try: 00479 node_dot = self.selectNode(dot,n,self.options['node_depth']) 00480 self.write_output(node_dot,filename.replace('.','_%s.'%n),filetype) 00481 except: 00482 pass 00483 else: 00484 dot = self.dotIndenter(dot) 00485 self.write_output(dot,filename,filetype) 00486 else: 00487 print "WARNING: Empty image. Not saved." 00488
def DOTExport::DotExport::get_png_size | ( | self, | |
filename | |||
) |
Definition at line 489 of file DOTExport.py.
00490 : 00491 png_header = '\x89PNG\x0d\x0a\x1a\x0a' 00492 ihdr = 'IHDR' 00493 filedata = open(filename,'r').read(24) 00494 png_data = struct.unpack('>8s4s4sII',filedata) 00495 if not (png_data[0]==png_header and png_data[2]==ihdr): 00496 raise 'PNG header or IHDR not found' 00497 return png_data[3],png_data[4]
def DOTExport::DotExport::processMap | ( | self, | |
mapdata | |||
) |
Re-process the client-side image-map produces when png+map is selected. DOT will only ever put a single URL in the imagemap corresponding to a node, with the 'url' parameter (after html encoding) as the url, and the 'title' parameter as the title. This isn't useful behaviour for our purposes. We want probably several link areas, or a javascript link to make a menu appear, or other more complex behaviour. If the option 'urlprocess' is turned on, this function is called, and it expects to find a dictionary it can eval in the url parameter. I can't think of a less messy way of passing data to this function without having inner access to DOT at the moment. This function iterates through all the areas in the mapfile, replacing each one with one or more areas according to the rules in the dictionary stored in the URL parameter. The dictionary should have structure: { split_x:#, split_y:#, scale_x:#, scale_y:#, cells:[ { top:#, left:#, width:#, height:#, html_attribute1:"...", html_attribute2:"..." ] } The imagemap is first scaled in size by scale_x and scale_y. It is then split into split_x*split_y rectangular cells. New areas are created for each defined cell with the defined top,left location and width,height. This will not check you aren't making new areas that overlap if you define them as such. The areas then get attributes defined by html_attribute fields - eg, 'html_href':'mypage.htm' becomes 'href'='mypage.htm' in the area. Probably you want as a minimum to define html_href and html_alt. It would also be useful to set html_class to allow highlighting of different link types, or html_onclick/onmouseover for more exotic behaviour. This will probably be quite sensitive to the presence of special characters, complex splitting schemes, etc. Use with caution. This may be somewhat replaceable with the <html_label> and cut-down table format that graphviz provides, but I haven't had much of an experiment with that.
Definition at line 393 of file DOTExport.py.
00394 : 00395 """ 00396 Re-process the client-side image-map produces when png+map is selected. 00397 DOT will only ever put a single URL in the imagemap corresponding to a node, with the 'url' parameter (after html encoding) as the url, and the 'title' parameter as the title. This isn't useful behaviour for our purposes. We want probably several link areas, or a javascript link to make a menu appear, or other more complex behaviour. 00398 00399 If the option 'urlprocess' is turned on, this function is called, and it expects to find a dictionary it can eval in the url parameter. I can't think of a less messy way of passing data to this function without having inner access to DOT at the moment. 00400 00401 This function iterates through all the areas in the mapfile, replacing each one with one or more areas according to the rules in the dictionary stored in the URL parameter. 00402 00403 The dictionary should have structure: 00404 { 00405 split_x:#, 00406 split_y:#, 00407 scale_x:#, 00408 scale_y:#, 00409 cells:[ 00410 { 00411 top:#, 00412 left:#, 00413 width:#, 00414 height:#, 00415 html_attribute1:"...", 00416 html_attribute2:"..." 00417 ] 00418 } 00419 The imagemap is first scaled in size by scale_x and scale_y. 00420 It is then split into split_x*split_y rectangular cells. 00421 New areas are created for each defined cell with the defined top,left location and width,height. This will not check you aren't making new areas that overlap if you define them as such. 00422 The areas then get attributes defined by html_attribute fields - eg, 'html_href':'mypage.htm' becomes 'href'='mypage.htm' in the area. Probably you want as a minimum to define html_href and html_alt. It would also be useful to set html_class to allow highlighting of different link types, or html_onclick/onmouseover for more exotic behaviour. 00423 00424 This will probably be quite sensitive to the presence of special characters, complex splitting schemes, etc. Use with caution. 00425 00426 This may be somewhat replaceable with the <html_label> and cut-down table format that graphviz provides, but I haven't had much of an experiment with that. 00427 """ 00428 new_areas=[] 00429 area_default = {'split_x':1,'scale_x':1.,'split_y':1,'scale_y':1.,'cells':[]} 00430 cell_default = {'top':0,'left':0,'width':1,'height':1,'html_href':'#'} 00431 re_area = re.compile('<area.*?/>',re.DOTALL) 00432 #sometimes DOT comes up with negative coordinates, so we need to deal with them here (so all the other links will work at least) 00433 re_content = re.compile('href="(.*?)" title=".*?" alt="" coords="(-?[0-9]{1,6}),(-?[0-9]{1,6}),(-?[0-9]{1,6}),(-?[0-9]{1,6})"',re.DOTALL) 00434 re_htmlunquote = re.compile('&#([0-9]{1,3});') 00435 mapdata = re_htmlunquote.sub(lambda x: chr(int(x.group(1))),mapdata) 00436 areas = re_area.findall(mapdata) 00437 for area in areas: 00438 #print area 00439 data = re_content.search(area) 00440 baseurl = data.group(1) 00441 x1,y1,x2,y2 = map(int,(data.group(2),data.group(3),data.group(4),data.group(5))) 00442 rad_x,rad_y = int((x2-x1)*0.5),int((y2-y1)*0.5) 00443 centre_x,centre_y = x1+rad_x,y1+rad_y 00444 basedict = eval(baseurl) 00445 for ad in area_default: 00446 if not ad in basedict: 00447 basedict[ad]=area_default[ad] 00448 rad_x = int(rad_x*basedict['scale_x']) 00449 rad_y = int(rad_y*basedict['scale_y']) 00450 top_x,top_y = centre_x-rad_x,centre_y-rad_y 00451 split_x,split_y = int((2*rad_x)/basedict['split_x']),int((2*rad_y)/basedict['split_y']) 00452 00453 for cell in basedict['cells']: 00454 for cd in cell_default: 00455 if not cd in cell: 00456 cell[cd]=cell_default[cd] 00457 x1,y1 = top_x+split_x*cell['left'],top_y+split_y*cell['top'] 00458 x2,y2 = x1+split_x*cell['width'],y1+split_y*cell['height'] 00459 area_html = '<area shape="rect" coords="%s,%s,%s,%s" %s />' % (x1,y1,x2,y2,' '.join(['%s="%s"'%(key.split('_',1)[1],value) for key, value in cell.items() if key.startswith('html_')])) 00460 new_areas.append(area_html) 00461 return '<map id="configbrowse" name="configbrowse">\n%s\n</map>'%('\n'.join(new_areas)) 00462
def DOTExport::DotExport::selectNode | ( | self, | |
dotdata, | |||
node, | |||
depth_s | |||
) |
Definition at line 328 of file DOTExport.py.
00329 : 00330 depth = int(depth_s) 00331 backtrace=False 00332 if depth<0: 00333 depth = abs(depth) 00334 backtrace=True 00335 re_link = re.compile(r'^\s*?(\w*?)->(\w*?)(?:\[.*?\])?$',re.MULTILINE) 00336 re_nodedef = re.compile(r'^\s*?(\w*?)(?:\[.*?\])?$',re.MULTILINE) 00337 re_title = re.compile(r'^label=\"(.*?)\"$',re.MULTILINE) 00338 re_nodeprops = re.compile(r'^\s*?('+node+r')\[(.*?)\]$',re.MULTILINE) 00339 00340 nodes = re_nodedef.findall(dotdata) 00341 if not node in nodes: 00342 raise Exception, "Selected node (%s) not found" % (node) 00343 links_l = re_link.findall(dotdata) 00344 links = {} 00345 for link in links_l: 00346 if not backtrace: 00347 if link[0] in links: 00348 links[link[0]] += [link[1]] 00349 else: 00350 links[link[0]] = [link[1]] 00351 if link[1] in links: 00352 links[link[1]] += [link[0]] 00353 else: 00354 links[link[1]] = [link[0]] 00355 00356 def node_recursor(links,depthleft,start): 00357 if start in links: 00358 if depthleft==0: 00359 return links[start]+[start] 00360 else: 00361 result = [start] 00362 for l in links[start]: 00363 result.extend(node_recursor(links,depthleft-1,l)) 00364 return result 00365 else: 00366 return [start] 00367 00368 00369 include_nodes = set(node_recursor(links,depth-1,node)) 00370 include_nodes.add(node) 00371 00372 class link_replacer: 00373 def __init__(self,include_nodes): 00374 self.include_nodes=include_nodes 00375 def __call__(self,match): 00376 if match.group(1) in self.include_nodes and match.group(2) in self.include_nodes: 00377 return match.group(0) 00378 return '' 00379 class node_replacer: 00380 def __init__(self,include_nodes): 00381 self.include_nodes=include_nodes 00382 def __call__(self,match): 00383 if match.group(1) in self.include_nodes: 00384 return match.group(0) 00385 return '' 00386 00387 dotdata = re_link.sub(link_replacer(include_nodes),dotdata) 00388 dotdata = re_nodedef.sub(node_replacer(include_nodes),dotdata) 00389 dotdata = re_title.sub(r'label="\g<1>\\nDepth '+str(depth_s)+r' from node ' +node+r'"',dotdata,1) 00390 dotdata = re_nodeprops.sub('\\g<1>[\\g<2>,color="red"]',dotdata,1) 00391 00392 return dotdata
def DOTExport::DotExport::write_output | ( | self, | |
dot, | |||
filename, | |||
filetype | |||
) |
Definition at line 498 of file DOTExport.py.
00499 : 00500 #don't use try-except-finally here, we want any errors passed on so the enclosing program can decide how to handle them 00501 if filetype=='dot': 00502 dotfile = open(filename,'w') 00503 dotfile.write(dot) 00504 dotfile.close() 00505 elif filetype=='stdout': 00506 print result 00507 elif filetype=='pdf': 00508 dot_p = subprocess.Popen(['dot','-Tps2'],stdin=subprocess.PIPE,stdout=subprocess.PIPE) 00509 ps2 = dot_p.communicate(dot)[0] 00510 if not dot_p.returncode==0: 00511 raise "dot returned non-zero exit code: %s"%dot_p.returncode 00512 pdf_p = subprocess.Popen(['ps2pdf','-',filename],stdin=subprocess.PIPE) 00513 pdf_p.communicate(ps2) 00514 if not pdf_p.returncode==0: 00515 raise "ps2pdf returned non-zero exit code: %s"%pdf_p.returncode 00516 elif filetype=='png+map': 00517 if '.' in filename: 00518 filename = filename.split('.')[0] 00519 dot_p = subprocess.Popen(['dot','-Tpng','-o','%s.png'%filename,'-Tcmapx_np'],stdin=subprocess.PIPE,stdout=subprocess.PIPE) 00520 mapdata = dot_p.communicate(dot)[0] 00521 if not dot_p.returncode==0: 00522 raise "dot returned non-zero exit code: %s"%dot_p.returncode 00523 if self.options['urlprocess']: 00524 mapdata = self.processMap(mapdata) 00525 mapfile = open('%s.map'%filename,'w') 00526 mapfile.write(mapdata) 00527 mapfile.close() 00528 filesize = self.get_png_size('%s.png'%filename) 00529 if max(filesize) > self.options['png_max_size']: 00530 print "png image is too large (%s pixels/%s max pixels), deleting" % (filesize,self.options['png_max_size']) 00531 os.remove('%s.png'%filename) 00532 os.remove('%s.map'%filename) 00533 elif filetype=='png': 00534 dot_p = subprocess.Popen(['dot','-T%s'%(filetype),'-o',filename],stdin=subprocess.PIPE) 00535 dot_p.communicate(dot) 00536 if not dot_p.returncode==0: 00537 raise "dot returned non-zero exit code: %s"%dot_p.returncode 00538 filesize = self.get_png_size(filename) 00539 if max(filesize) > self.options['png_max_size']: 00540 print "png image is too large (%s pixels/%s max pixels), deleting" % (filesize,self.options['png_max_size']) 00541 os.remove(filename) 00542 else: 00543 dot_p = subprocess.Popen(['dot','-T%s'%(filetype),'-o',filename],stdin=subprocess.PIPE) 00544 dot_p.communicate(dot) 00545 if not dot_p.returncode==0: 00546 raise "dot returned non-zero exit code: %s"%dot_p.returncode 00547 00548 00549
tuple DOTExport::DotExport::file_types = ('bmp','dot','eps','gif','jpg','pdf','png','ps','svg','tif','png+map','stdout') [static] |
Definition at line 294 of file DOTExport.py.
Definition at line 328 of file DOTExport.py.
dictionary DOTExport::DotExport::option_types [static] |
{ 'legend':('Show Legend','boolean',True), 'source':('Show Source','boolean',True), 'es':('Show Event Setup','boolean',False), 'tagconnect':('Connect InputTags','boolean',True), 'seqconnect':('Connect Module Sequence','boolean',False), 'services':('Show Services','boolean',False), 'endpath':('Show EndPaths','boolean',True), 'seq':('Group Sequences','boolean',True), 'class':('Show Class','boolean',True), 'file':('Show File','boolean',True), 'schedule':('Show Schedule','boolean',False), 'taglabel':('Show Tag Labels','boolean',True), 'color_path':('Path Color','color','#ff00ff'), 'color_endpath':('EndPath Color','color','#ff0000'), 'color_sequence':('Sequence Color','color','#00ff00'), 'color_inputtag':('InputTag Color','color','#0000ff'), 'color_schedule':('Schedule Color','color','#00ffff'), 'url':('Include URLs','boolean',False), #this is only purposeful for png+map mode 'urlprocess':('Postprocess URL (for client-side imagemaps)','boolean',False), #see processMap documentation; determines whether to treat 'urlbase' as a dictionary for building a more complex imagemap or a simple URL 'urlbase':('URL to generate','string',"{'split_x':1,'split_y':2,'scale_x':1.,'scale_y':1.,'cells':[{'top':0,'left':0,'width':1,'height':1,'html_href':'https://cmslxr.fnal.gov/lxr/ident/?i=$classname','html_alt':'LXR','html_class':'LXR'},{'top':1,'left':0,'width':1,'height':1,'html_href':'https://cmssw.cvs.cern.ch/cgi-bin/cmssw.cgi/CMSSW/$pypath?view=markup#$pyline','html_alt':'CVS','html_class':'CVS'}]}"), #CVS markup view doesn't allow line number links, only annotate view (which doesn't then highlight the code...) 'node_graphs':('Produce individual graphs focussing on each node','boolean',False), 'node_graphs_restrict':('Select which nodes to make node graphs for','string',''), 'node_depth':('Search depth for individual node graphs','int',1), 'font_name':('Font name','string','Times-Roman'), 'font_size':('Font size','int',8), 'png_max_size':('Maximum edge for png image','int',16768) }
Definition at line 265 of file DOTExport.py.
string DOTExport::DotExport::plugin_name = 'DOT Export' [static] |
Definition at line 293 of file DOTExport.py.
Definition at line 295 of file DOTExport.py.