# Python Script for Adaptive Path # Convert Mental Model Excel WorkSheet or Word Doc to Visio Chart # # 4-6-2006 Gary Wang firepotter@gmail.com # 07-Apr-09 Alexey Pavlenko alex.gripav@gmail.com - added variables the user # can change for box height, etc. Did not test if it works for # Word doc conversion, still. #---------------------------------------------------------------------------------------- from win32com.client import Dispatch import string, os, sys, re filePath = '' # leave these blank if you want to use the default excelFilename = '' """ These are the printable characters: 0123456789abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ """ boxInTopDownOrder = 1 generateDebugFiles = 1 #set to 1 to generate a debug output file # # Color Generator Function def makeColor( red=1, green=1, blue=1 ): rc = int( red*255 ) % 256 gc = int( green*255 ) % 256 bc = int( blue*255 ) % 256 return ( bc*0x010000 + gc*0x000100 + rc*0x000001 ) colorBlack = makeColor(red=0,green=0,blue=0) colorRed = makeColor(red=1,green=0,blue=0) colorGreen = makeColor(red=0,green=1,blue=0) colorYellow = makeColor(red=1,green=1,blue=0) colorBlue = makeColor(red=0,green=0,blue=1) colorMagenta = makeColor(red=1,green=0,blue=1) colorCyan = makeColor(red=0,green=1,blue=1) colorWhite = makeColor(red=1,green=1,blue=1) colorLightCyan = makeColor(red=0.5,green=1,blue=1) # Chart Property Constants filePath = '' # leave these blank if you want to use the default wordFilename = '' #unit = inch # Height of a page (8.5 - landscape, 11 - portrait) PageLength = 11 #PageWidth=88 # The gap of this size is placed between the content (boxes, lines) and the end # of the page. PageMargin = 0.25 ChartStartX=1+5.0/8 HorizontalLinePosition = PageLength/2.0-0.125 # An empty space between two boxes in a row (either vertical or horizontal) Box2BoxSpacing = 0.0625 # A gap between a tower border and a box side Box2TowerSpacing = 0.125 # A gap between two towers in a row Tower2TowerSpacing = 0.125 # An empty space between a section border and the first or the last tower Tower2SectionSpacing = 0.125 # Height of the tower header. This value is used in case VariableTowerHeader = False TowerHeaderExtra = 0.250+0.125 # Height of a section, currently is calculated from the PageLength value and margins. # Please use PageLength instead in order to modify the height of a page. SectionHeight = PageLength/2-PageMargin - 0.25 # Sets the maximum number of boxes in a column of a tower. MaxBoxNumberPerColumn = 7 # Regular box height, used when VariableHeight = False BOXHEIGHT=0.375 # Maximum box height, limits the height of the box when VariableHeight = True MAXBOXHEIGHT= BOXHEIGHT * 4 # Minimum box height, sets the minumum height of the box when VariableHeight = True MINBOXHEIGHT=0.375 #BOXHEIGHT / 2 # Adjust task box height to fit the content, True or False VariableHeight = True # Absolute tower maximum height, used in case VariableHeight = True MaxTowerHeight = MAXBOXHEIGHT * 2.7 # Adjust tower header to fit the content. Used only in case VariableHeight = True VariableTowerHeader = True # A width of a single box BOXWIDTH=0.625 # A width of a medium size tower of one column. # Please do not change this value. TOWERWIDTH = BOXWIDTH + 2 * Box2TowerSpacing #0.875 DefaultLineWeight = 0.06 SectionFontPoint = 12 SectionLineWeight = 0.01 SectionFill = 0 SectionFillColor = colorWhite SectionLine = 0 SectionLineColor = colorWhite TowerFontPoint = 6 TowerLineWeight = 0.01 TowerFill = 1 TowerFillColor = colorLightCyan TowerLine = 0 TowerLineColor = colorLightCyan BoxFontPoint = 6 BoxLineWeight = 0.01 BoxFill = 1 BoxFillColor = colorWhite BoxLine = 1 BoxLineColor = colorBlack # # Global Variables sectionIndex = 0 towerIndex = 0 boxIndex = 0 sectionX=2 sectionY=0 sectionNextX = ChartStartX towerX=0 towerY=0 runningBoxX=0 runningBoxY=0 columnsInTower=1 styleList = {} # Current tower header text currentTowerHeader = 0 #---------------------------------------------------------------------------------------- import string, os, sys, time allowedFileTypeList = ['.xml'] operatingSystem = os.environ['OS'] print 'OS=',operatingSystem if operatingSystem in ['Windows_NT']: allowedFileTypeList.append('.xls') allowedFileTypeList.append('.doc') def prepareText(text): ''' Adapt the text to the measurement process ''' text = text.split('::::')[0] # Prepare the replacement text = text.replace('&', '&') text = text.replace('<', '<') text = text.replace('>', '>') text = text.replace('"', '"') text = re.sub(r'[\-/]', ' ', text) return text def getTextHeight(text, width = BOXWIDTH): ''' Return the height of the task box ''' global VariableHeight if VariableHeight: text = prepareText(text) return getTextHeight_win(text, width) return BOXHEIGHT def getTextHeight_win(text, width): ''' Based on a BOXWIDTH value determine the height of the Box that is going to be displayed by Visio. The return value is in inches. Visio default font for task boxes is 'Arial 6pt normal', thus we should use in the computation. Windows implementation of the routine. ''' import win32api, win32gui, win32print, win32con # Base font settings, override if necessary fontFace = 'Arial' fontHeight = 6 # First, create a DCs we're going to work with hWinDC = win32gui.GetDC(0) hDC = win32gui.CreateCompatibleDC(hWinDC) # Set antialiasing #oldSmoothing = win32gui.SystemParametersInfo(win32con.SPI_GETFONTSMOOTHING) #oldSmoothType = win32gui.SystemParametersInfo(win32con.SPI_GETFONTSMOOTHINGTYPE) #win32gui.SystemParametersInfo(win32con.SPI_SETFONTSMOOTHING, True, 0) #win32gui.SystemParametersInfo(win32con.SPI_SETFONTSMOOTHINGTYPE, win32con.FE_FONTSMOOTHINGCLEARTYPE) # Get font height in logical units height = -int((fontHeight * win32print.GetDeviceCaps(hDC, win32con.LOGPIXELSY)) / 72.0 + 0.5) # Now create the font that is going to be used lf = win32gui.LOGFONT() lf.lfWeight = win32con.FW_NORMAL lf.lfHeight = height lf.lfFaceName = 'Arial' lf.lfQuality = win32con.CLEARTYPE_NATURAL_QUALITY hFont = win32gui.CreateFontIndirect(lf) if not hFont: raise StandardError('Unable to create font') # Set our font to get measurement hFont = win32gui.SelectObject(hDC, hFont) # Visio rect is using margins, so take it into account rectWidth = int(width * win32print.GetDeviceCaps(hDC, win32con.LOGPIXELSX)) rectWidth = rectWidth - 10 # Margins from both sides # Now finally measure text height rect = (0, 300, rectWidth, 10000) (height, rect) = win32gui.DrawText(hDC, text, len(text), rect, win32con.DT_CENTER | win32con.DT_WORDBREAK ) #win32gui.SystemParametersInfo(win32con.SPI_SETFONTSMOOTHING, oldSmoothing, 0) #win32gui.SystemParametersInfo(win32con.SPI_SETFONTSMOOTHINGTYPE, oldSmoothType) height = float(height) / win32print.GetDeviceCaps(hDC, win32con.LOGPIXELSY) # Return the original font to the device context win32gui.SelectObject(hDC, hFont) # Delete font win32gui.DeleteObject(hFont) # Delete the DCs win32gui.DeleteDC(hDC) win32gui.ReleaseDC(0, hWinDC) return height def adjustedTextHeight(text): global MINBOXHEIGHT, MAXBOXHEIGHT height = getTextHeight(text) height = min(height, MAXBOXHEIGHT) height = max(height, MINBOXHEIGHT) return height # from xml.sax import saxutils from xml.sax import ContentHandler from xml.sax import make_parser from xml.sax.handler import feature_namespaces def hex2Integer( s='' ): s = string.lower(s) sum = 0 for c in s: i = string.find( string.hexdigits, c ) if i>=0: sum = sum*16 + i return sum def normalize_whitespace(text): "Remove redundant whitespace from a string" return ' '.join(text.split()) class ProcessData(ContentHandler): def __init__(self): self.inDataContent = 0 self.inTargetSheet = 0 self.sID = "Default" self.styleList = {} self.dataList = {} def startElement(self, name, attrs): try: if name=='Style': self.sID = attrs.get('ss:ID', None) self.styleList[self.sID]={} if name=='Font': self.styleList[self.sID]['Family']=attrs.get('x:Family', None) self.styleList[self.sID]['Size'] = attrs.get('ss:Size', None) self.styleList[self.sID]['Color'] = attrs.get('ss:Color', '#FFFFFF') self.styleList[self.sID]['Italic'] = attrs.get('ss:Italic', None) self.styleList[self.sID]['Bold'] = attrs.get('ss:Bold', None) self.styleList[self.sID]['Underline'] = attrs.get('ss:Underline', None) if name=='Interior': self.styleList[self.sID]['iColor'] = attrs.get('ss:Color', '#FFFFFF') self.styleList[self.sID]['iPattern'] = attrs.get('ss:Pattern', None) # if name=='Worksheet': self.currentSheet = attrs.get('ss:Name', None) self.currentRow = 0 if name=='Row': self.currentRow = self.currentRow + 1 self.currentCell = 0 if name=='Cell': self.sID = attrs.get('ss:StyleID', None) sIx = attrs.get('ss:Index', None) if sIx==None: self.currentCell = self.currentCell + 1 else: #print sIx self.currentCell = string.atoi(sIx) if name=='Data': self.inDataContent = 1 self.dataContent = "" except: pass def characters(self, ch): if self.inDataContent: #XML special characters (< > & ") must be encoded (< > & ") #to be used in python code. This, unfortunately, is unavoidable. if ch=='&': ch = '&' if ch=='<': ch = '<' if ch=='>': ch = '>' if ch=='"': ch = '"' self.dataContent = self.dataContent + ch def endElement(self, name): if name=='Data': self.inDataContent = 0 self.dataContent = normalize_whitespace(self.dataContent) data = (self.currentRow, self.currentCell, self.sID, self.dataContent) if not self.dataList.has_key(self.currentSheet): self.dataList[self.currentSheet] = [] self.dataList[self.currentSheet].append( data ) def readXMLDoc( filename, debug=1 ): # Create a parser parser = make_parser() # Tell the parser we are not interested in XML namespaces parser.setFeature(feature_namespaces, 0) # Create the handler dh = ProcessData() # Tell the parser to use our handler parser.setContentHandler(dh) # Parse the input parser.parse(filename) tmpFilename = filename[:-4]+'tmp2139' fd = open( tmpFilename+".txt", "w" ) operatingSystem = os.environ['OS'] fd.write('\nOS=%s\n' % (operatingSystem) ) fd.write('Script=%s\n' % (sys.argv[0]) ) fd.write('Date=%s\n' % (time.asctime() ) ) fd.flush() for key in dh.styleList.keys(): ss = '%s' % (key) for skey in dh.styleList[key].keys(): ss = ss + ', %s:%s ' % (skey, dh.styleList[key][skey]) if debug: print ss fd.write(ss+'\n') fd.write('\n\n') workSheetKeys = dh.dataList.keys() #print workSheetKeys stack = {} for worksheet in workSheetKeys: (chart, section, tower) = ([],[],[]) (sectionIndex, towerIndex, boxIndex) = (0,0,0) #print worksheet for (row, col, sID, text) in dh.dataList[worksheet]: if row==1 and text=='Mental Space': sectionIndex = col if row==1 and text=='Task Tower': towerIndex = col if row==1 and text=='Task': boxIndex = col if row>1 and (sectionIndex, towerIndex, boxIndex) == (0,0,0): break text = '%s::::%s' % (text , sID) if row>1 and col==sectionIndex: if len(section)>0: if len(tower)>0: section.append(tower) chart.append(section) section=[text] tower = [] if row>1 and col==towerIndex: if len(tower)>0: section.append(tower) tower=[text] if row>1 and col==boxIndex: tower.append( text ) if len(section)>0: if len(tower)>0: section.append(tower) chart.append(section) if len(chart)>0: stack[worksheet]=chart fd.close() if not debug: os.remove( tmpFilename+".txt" ) return (dh.styleList, stack) def process( s ): s1 = s[10:] s2 = '' flag = 0 for i in range(0,len(s1)): bStart = (s1[i]=='<') bEnd = (s1[i]=='>') if bStart: flag = 1 if not flag: if s1[i] in string.printable: s2 = s2 + s1[i] if bEnd: flag = 0 s3 = string.split(s2,'>') s4 = string.split(s3[0],' ') return (s4[0],s3[-1]) def readWordDocFast( fname='', debug=0 ): #Faster Method. #Save to a *.htm file and parse it. print fname chart=[] if fname=='': return chart filename = fname[:-4] style = {} style['APHeading2'] = 'section' style['APHeading3'] = 'tower' style['APHeading4'] = 'box' tmpFilename = filename+'tmp2138' filteredHTMLFormat = 8 app = Dispatch("Word.Application") """ #using word's File Open GUI ????? #app.Visible = 1 #app.Dialogs(80).Show() app.Dialogs(80).Display() print 'Name is', app.Dialogs(80).Name """ app.Visible = 0 doc = app.Documents.Open( filename + ".doc" ) doc.SaveAs(tmpFilename+".htm",filteredHTMLFormat) doc.Close(0) del app fd = open( tmpFilename+".htm", 'r') lines=fd.readlines() fd.close() os.remove( tmpFilename+"_files\\filelist.xml" ) os.remove( tmpFilename+"_files\\header.htm" ) os.rmdir( tmpFilename+"_files") if not debug: os.remove( tmpFilename+".htm" ) fd = open( tmpFilename+".txt", "w" ) output = [] flag = 0 tFlag = 0 s = '' for line in lines: pStart = string.find(line,'

')>=0 tStart = string.find(line,'=0 tEnd = string.find(line,'')>=0 if tStart: tFlag = 1 if tEnd: tFlag = 0 pStart = 0 s = '' flag = 0 if pStart: flag = 1 if (pStart or pEnd or flag) and (not tFlag): s = s + ' ' + string.strip(line) if pEnd: flag = 0 if len(s)>0: (key,t) = process(s) if key in style.keys(): t = string.strip(t) #t = string.replace(t,'&','&') #t = string.replace(t,'x',"'") if style[key]=='section': section = t chart.append([section]) sectionIndex = len(chart)-1 if style[key]=='tower': tower = t chart[sectionIndex].append([tower]) towerIndex = len(chart[sectionIndex])-1 if style[key]=='box': chart[sectionIndex][towerIndex].append(t) ss = "%s %7s %s" % (key, style[key], t) print ss fd.write( ss+'\n' ) fd.write( s+'\n' ) s = '' fd.close() if not debug: os.remove( tmpFilename+".txt" ) return chart def readWordDocSlow( filename='' ): from win32com.client import Dispatch #Get the info from Word Objects #This is slower than readWordDoc2 chart=[] if filename=='': return chart style = {} style['AP Heading 2'] = 'section' style['AP Heading 3'] = 'tower' style['AP Heading 4'] = 'box' app = Dispatch("Word.Application") app.Visible = 0 doc = app.Documents.Open(filename) for para in doc.paragraphs: s = para.format.style.nameLocal if s in style.keys(): t = string.strip(para.Range.Text) t = string.replace(t,'&','&') if style[key]=='section': section = t chart.append([section]) sectionIndex = len(chart)-1 if style[key]=='tower': tower = t chart[sectionIndex].append([tower]) towerIndex = len(chart[sectionIndex])-1 if style[key]=='box': chart[sectionIndex][towerIndex].append(t) print "%s %7s %s" % (key, style[key], t) doc.Close(0) del app return chart def drawVisio( filename, shapes, width=88, length=PageLength ): fd = open( filename, 'w' ) fd.write("\n") fd.write("\n") fd.write("\n") fd.write("\n") fd.write("\n") fd.write("\n") fd.write("%.2f\n" % (width) ) fd.write("%.2f\n" % (length) ) fd.write("1\n") fd.write("3\n") fd.write("0\n") fd.write("\n") fd.write("\n") fd.write("8\n") fd.write("8\n") fd.write("4\n") fd.write("4\n") fd.write("\n") fd.write("\n") fd.write("\n") shapeID = 1 for (X, Y, Width, Height, Text, vAlign, TextColor, TextFont, TextPoint, TextStyle, Line, \ LineColor, LineWeight, Fill, FillColor) in shapes: shapeWidth = Width shapeHeight = Height shapeX = X shapeY = Y fillColor = FillColor lineWeight = LineWeight lineColor = LineColor textMarginHorizontal = 0.02 textMarginVertical = 0.02 verticalAlignment = vAlign fill = Fill line = Line text = Text pSize = TextPoint*(0.11111111111/8) if string.find(text,'::::')>=0: tlist = string.split(text,'::::') text = tlist[0] try: fillColor = string.atoi(styleList[tlist[-1]]['iColor'][1:],16) except: pass x1 = 0 x2 = shapeWidth y1 = 0 y2 = shapeHeight shapeCenterX = shapeX + shapeWidth/2.0 shapeCenterY = shapeY + shapeHeight/2.0 #LineStyle='6' FillStyle='6' TextStyle='6' fd.write("\n" % (shapeID, shapeID) ) fd.write("\n") fd.write("%.4f\n" % (shapeCenterX) ) fd.write("%.4f\n" % (shapeCenterY) ) fd.write("%.4f\n" % (shapeWidth) ) fd.write("%.4f\n" % (shapeHeight) ) fd.write("%.4f\n" % (shapeWidth/2.0) ) fd.write("%.4f\n" % (shapeHeight/2.0) ) fd.write("\n") fd.write("%.3f\n" % (lineWeight) ) if lineColor!=0: fd.write("#%x\n" % (lineColor) ) fd.write("\n") fd.write("#%x\n" % (fillColor) ) fd.write("\n") fd.write("0\n") fd.write("0\n") fd.write("\n" % (TextStyle) ) fd.write("0\n") fd.write("0\n") fd.write("%.10f\n" % (pSize) ) fd.write("\n") fd.write("\n") fd.write("%.3f\n" % (textMarginHorizontal) ) fd.write("%.3f\n" % (textMarginHorizontal) ) fd.write("%.3f\n" % (textMarginVertical) ) fd.write("%.3f\n" % (textMarginVertical) ) fd.write("%d\n" % (verticalAlignment) ) fd.write("\n") fd.write("\n") fd.write("%d\n" % (1-fill) ) fd.write("%d\n" % (1-line) ) fd.write("0\n") fd.write("0\n") fd.write("\n%.4f\n%.4f\n\n" % (x1,y1) ) fd.write("\n%.4f\n%.4f\n\n" % (x2,y1) ) fd.write("\n%.4f\n%.4f\n\n" % (x2,y2) ) fd.write("\n%.4f\n%.4f\n\n" % (x1,y2) ) fd.write("\n%.4f\n%.4f\n\n" % (x1,y1) ) fd.write("\n") try: fd.write("%s\n" % (text) ) except: fd.write("%s\n" % ('ERROR???') ) fd.write("\n") shapeID = shapeID + 1 fd.write("\n\n\n\n") fd.close() return 1 def shape( X, Y, Width=1, Height=1, Text='', vAlign=1, TextColor=0xffff80, TextFont='arial', \ TextPoint=6, TextStyle=0, Line=1, LineColor=0x000000, LineWeight=0.02, \ Fill=0, FillColor=0xffffff): return((X, Y, Width, Height, Text, vAlign, TextColor, TextFont, TextPoint, TextStyle,\ Line, LineColor, LineWeight, Fill, FillColor)) def line( x1, y1, x2, y2, Text='' ): Width=x2-x1 Height=y2-y1 return shape( x1, y1, Width, Height, Text, LineWeight=DefaultLineWeight ) def getTowerTextHeight(text, columns): ''' Return the height of the tower boxs' caption ''' global VariableTowerHeader, VariableHeight, TowerHeaderExtra if VariableHeight and VariableTowerHeader: width = 2 * Box2TowerSpacing - Box2BoxSpacing + \ (BOXWIDTH+Box2BoxSpacing) * columns return getTextHeight(text, width) + Box2BoxSpacing return TowerHeaderExtra def getTowerSplit(tower): ''' Split boxes to different towers depending on the settings. Returns a list of 'boxes per tower' ''' global TowerHeaderExtra, MaxTowerHeight, Box2BoxSpacing, MAXBOXHEIGHT global VariableHeight, MaxBoxNumberPerColumn, currentTowerHeader global Box2TowerSpacing, BOXWIDTH, MINBOXHEIGHT split = [] boxCnt = 0 towerHeight = 0 maxHeight = 0 # TODO: support this for box in tower[1:]: boxCnt += 1 if VariableHeight: boxHeight = adjustedTextHeight(box) if towerHeight + boxHeight + Box2BoxSpacing > \ MaxTowerHeight - getTowerTextHeight(tower[0], len(split) + 1): split.append(boxCnt - 1) towerHeight = boxHeight + Box2BoxSpacing boxCnt = 1 else: towerHeight += boxHeight + Box2BoxSpacing maxHeight = max(maxHeight, towerHeight) if boxCnt == MaxBoxNumberPerColumn: split.append(boxCnt) boxCnt = 0 towerHeight = 0 # Add tail tower if boxCnt: split.append(boxCnt) # Erase empty columns split = filter(None, split) return (maxHeight, split) def getTowerCount(tower): ''' Based on the task box height compute the actual number of the towers ''' return len(getTowerSplit(tower)[1]) def getTowerMaxHeight(tower): ''' Return the maximum height for tower ''' return getTowerSplit(tower)[0] def sectionBox( section ): global sectionIndex, towerIndex global sectionX, sectionY, sectionNextX global towerX, towerY global VariableHeight, sectionHeight Text = section['name'] numberOfTowers = len(section['towers']) sectionIndex = sectionIndex + 1 Width = Tower2SectionSpacing for tower in section['towers']: m = len(tower['boxes']) #Width = Width + (0.1+(TOWERWIDTH-0.1)*m) + Tower2TowerSpacing Width = Width + Box2TowerSpacing * 2 + BOXWIDTH * m + Box2BoxSpacing * (m-1) + Tower2TowerSpacing if numberOfTowers==0: Width = Width+TOWERWIDTH+Tower2SectionSpacing else: # Remove the last Tower2TowerSpacing and add final Tower2SectionSpacing Width -= Tower2TowerSpacing Width += Tower2SectionSpacing Height= SectionHeight sectionX = sectionNextX sectionNextX = sectionNextX + Width sectionY = PageLength/2.0 towerIndex = 0 return shape( sectionX, sectionY, Width, Height, Text, vAlign=0, TextStyle=1, \ LineWeight=SectionLineWeight, Fill=SectionFill, Line=SectionLine,\ FillColor=SectionFillColor, LineColor=SectionLineColor, TextPoint=SectionFontPoint ) def getBoxCount(boxes): count = 0 for x in boxes: count += len(x) return count def towerBox( tower ): global MaxBoxNumberPerColumn global towerIndex, boxIndex global sectionX, sectionY global towerX, towerY, VariableTowerHeader global VariableHeight, runningBoxY, runningBoxX, currentTowerHeader global Tower2SectionSpacing, TowerHeaderExtra global BOXWIDTH, Box2BoxSpacing, Tower2TowerSpacing global columnsInTower Text=tower['name'] currentTowerHeader = Text boxNumber = getBoxCount(tower['boxes']) widthMultiplier = len(tower['boxes']) towerIndex = towerIndex + 1 boxIndex = 0 towerX=sectionX+Tower2SectionSpacing+(towerIndex-1)*(TOWERWIDTH+Tower2TowerSpacing) towerY=sectionY runningBoxX = towerX runningBoxY = towerY columnsInTower = 1 bn = boxNumber if bn > MaxBoxNumberPerColumn: bn = MaxBoxNumberPerColumn Width = 2*Box2TowerSpacing-Box2BoxSpacing+(BOXWIDTH+Box2BoxSpacing)*widthMultiplier if VariableHeight: towerHeaderHeight = getTowerTextHeight(Text, widthMultiplier) Height = tower['maxHeight'] + towerHeaderHeight else: Height = bn*(BOXHEIGHT+Box2BoxSpacing)+TowerHeaderExtra return shape( towerX, towerY, Width, Height, Text, vAlign=0, \ LineWeight=TowerLineWeight, Fill=TowerFill, Line=TowerLine,\ FillColor=TowerFillColor, LineColor=TowerLineColor, TextPoint=TowerFontPoint ) def boxBox( Text='Box', extendTower=False ): global boxIndex, Box2BoxSpacing, currentTowerHeader global sectionX, sectionY global towerX, towerY, MaxBoxNumberPerColumn global VariableHeight, MAXBOXHEIGHT, MaxTowerHeight, MINBOXHEIGHT global runningBoxX, runningBoxY global BOXWIDTH, BOXHEIGHT, columnsInTower boxIndex = boxIndex + 1 # Get the height of the box if not VariableHeight: Height=BOXHEIGHT else: Height=adjustedTextHeight(Text) # If box index is greater then the maximum number or # Box height is greater then the tower height start a new tower if extendTower: columnsInTower += 1 towerX = towerX + BOXWIDTH + Box2BoxSpacing towerY = sectionY runningBoxX = towerX runningBoxY = towerY sectionX = sectionX + BOXWIDTH + Box2BoxSpacing boxIndex = 1 boxX = runningBoxX + Box2TowerSpacing boxY = runningBoxY + Box2BoxSpacing runningBoxY = boxY + Height Width=BOXWIDTH return shape( boxX, boxY, Width, Height, Text, vAlign=1, \ LineWeight=BoxLineWeight, Fill=BoxFill, Line=BoxLine,\ FillColor=BoxFillColor, LineColor=BoxLineColor, TextPoint=BoxFontPoint ) def drawSection( section ): s = [] s.append( sectionBox( section ) ) for tower in section['towers']: s = s + drawTower( tower, topDown=boxInTopDownOrder ) return s def drawTower( tower, topDown=0 ): s = [] s.append( towerBox( tower ) ) split = tower['boxes'] startNewPart = False for towerPart in tower['boxes']: for box in towerPart: s.append( boxBox(box, startNewPart)) startNewPart = False # Extend the tower startNewPart = True return s def rearrangeTaskBoxes(boxes): ''' For each box in the tower find the height of the box and move it to the top of the list ''' return sorted(boxes, key=adjustedTextHeight) def calculateTower(tower): # Set the name to the beginning of the list my = {} my['name'] = tower[0] my['boxes'] = [] # Get the way how towers are going to be splitted maxHeight, split = getTowerSplit(tower) my['maxHeight'] = maxHeight # Now make the split tower = tower[1:] for t in split: arranged = rearrangeTaskBoxes(tower[:t]) my['boxes'].append( arranged ) tower = tower[t:] return my def calculateSheet(section): my = {} my['name'] = section[0] my['towers'] = [] for tower in section[1:]: my['towers'].append( calculateTower(tower) ) return my if __name__ == '__main__': #get filename from user if len(filePath)==0: filePath = string.replace( os.path.abspath('') ,'\\','\\\\') +'\\\\' if len(excelFilename)==0: files = [] for file in os.listdir( filePath ): if file[-4:] in allowedFileTypeList and file[0]!='~': files.append(file) files.append('QUIT') i = 0 for file in files: print "%2d: %s" % (i, file) i = i + 1 while 1: try: j = string.atoi(raw_input('Select File: ')) excelFilename = files[j] break except: pass if excelFilename=='QUIT': sys.exit(0) print 'Processing', excelFilename, '...' filename = os.path.abspath('')+'\\'+excelFilename[:-4]+'.xml' fileType = excelFilename[-4:] if fileType=='.xls': # convert file to xml format, window system only, will not work on mac or linux from win32com.client import Dispatch app = Dispatch("Excel.Application") app.Visible = 0 doc = app.Workbooks.Open( filePath+excelFilename ) print 'Saving File As', filename type = 46 doc.SaveAs(filename, type) doc.Close(0) del app if fileType in ['.xml','.xls']: (styleList, stack) = readXMLDoc( filename, debug=generateDebugFiles ) #print styleList.keys() if fileType=='.doc': filename = os.path.abspath('')+'\\'+excelFilename chart = readWordDocFast( filename, debug=generateDebugFiles ) stack = { '%FORWORDDOC%': chart } print stack.keys() vFileList = [] for key in stack.keys(): ChartStartX=1+5.0/8 sectionIndex = 0 towerIndex = 0 boxIndex = 0 sectionX=2 sectionY=0 sectionNextX = ChartStartX towerX=0 towerY=0 chart = stack[key] #print key #print chart s = [] for section in chart: calculated = calculateSheet(section) s = s + drawSection(calculated) # drawSection( section ) s.append( line(sectionNextX, PageMargin, sectionNextX, PageLength-PageMargin) ) PageWidth= sectionNextX + PageMargin s.append( line(ChartStartX, HorizontalLinePosition, PageWidth-PageMargin, HorizontalLinePosition) ) tag = '-'+key if key=='%FORWORDDOC%': tag = '' visioFilename = filename[:-4]+tag+ '.vdx' print visioFilename vFileList.append(visioFilename) drawVisio( visioFilename, s, width=PageWidth, length=PageLength ) # Launch Visio and Open the file visioFilename = vFileList[0] print '\nLaunching Visio and Opening ' + visioFilename appVisio = Dispatch("Visio.Application") appVisio.Visible = 1 doc = appVisio.Documents.Open( visioFilename)