import Pmw, AppShell, math, time, string, marshal from toolbarbutton import ToolBarButton import tkFileDialog from tkSimpleDialog import Dialog import mesh from Tkinter import FALSE,TRUE, Frame,X, LEFT,YES,BOTH,ALL,Widget,CURRENT, Label,W, Entry, E, StringVar, END #from cursornames import TLC,TRC, BLC, BRC, TS, RS, LS, BS from tkMessageBox import showerror, _show, QUESTION,YESNOCANCEL import types import visualmesh import os # CONSTANTS VERT_SELECT_ADDING_SEG_COLOR = 'orange' SELECT_COLOR = 'red' SEG_COLOUR = 'blue' TRIANGLE_COLOUR = 'green' APPLICATION_NAME = 'Pmesh' class Draw(AppShell.AppShell): usecommandarea = 1 appname = APPLICATION_NAME frameWidth = 840 frameHeight = 600 def createButtons(self): """ Add buttons to the bottom of the GUI """ self.buttonAdd('Postscript', helpMessage='Save current drawing (as PostScript)', statusMessage='', command=self.ipostscript) self.buttonAdd('Clear', helpMessage='Delete the mesh', statusMessage='', command=self.clearMesh) self.buttonAdd('Close', helpMessage='Close Screen', statusMessage='', command=self.close) def createBase(self): """ Create the GUI framework. Set up the GUI """ self.toolbar = self.createcomponent('toolbar', (), None, Frame, (self.interior(),), background="gray90") self.toolbar.pack(fill=X) self.scrolledcanvas = self.createcomponent('ScrolledCanvas', (), None, Pmw.ScrolledCanvas, (self.interior(),) ,borderframe = 1 ,labelpos = 'n' ) self.scrolledcanvas.configure(hscrollmode = 'dynamic') self.scrolledcanvas.configure(vscrollmode = 'dynamic') self.scrolledcanvas.pack(side=LEFT, expand=YES, fill=BOTH) self.canvas = self.scrolledcanvas.component('canvas') self.canvas.configure( background="white" ) self.canvas.pack(side=LEFT, expand=YES, fill=BOTH) Widget.bind(self.canvas, "", self.mouseDown) Widget.bind(self.canvas, "", self.rightMouseUp) Widget.bind(self.canvas, "",self.DeleteSelectedMeshObject) # "" didn't work.. #Widget.bind(self.canvas, "", self.DeleteSelectedMeshObject) #self.root.bind("", self.setRegular) #self.root.bind("", self.setRegular) self.scrolledcanvas.resizescrollregion() # def setRegular(self, event): # if event.type == '2' and event.keysym == 'Shift_L': # self.regular = TRUE # else: # self.regular = FALSE def createMenus(self): """ Add menus to the top of the GUI """ self.menuBar.deletemenuitems('File',0) self.menuBar.addmenuitem('File', 'command', 'New mesh', label='New', command=self.clearMesh) self.menuBar.addmenuitem('File', 'command', 'Open mesh', label='Open...', command=self.importFile) self.menuBar.addmenuitem('File', 'command', 'Save mesh', label='Save', command=self.saveDrawing) self.menuBar.addmenuitem('File', 'command', 'Save mesh', label='SaveAs...', command=self.saveAsDrawing) self.menuBar.addmenuitem('File', 'separator') self.menuBar.addmenuitem('File', 'command', 'Add ungenerated file from arcGIS', label='Add ungenerated file...', command=self.ImportUngenerate) self.menuBar.addmenuitem('File', 'command', 'Export ASCII obj', label='Export ASCII obj', command=self.exportObj) self.menuBar.addmenuitem('File', 'command', 'Export ASCII segment outline', label='Export ASCII segment outline...', command=self.exportASCIIsegmentoutlinefile) self.menuBar.addmenuitem('File', 'command', 'Export ASCII xya file', label='Export ASCII xya file...', command=self.exportxyafile) self.menuBar.addmenuitem('File', 'command', 'add Segments to connect all vertices' , label='join vertices', command=self.joinVertices) self.menuBar.addmenuitem('File', 'command', 'add Segments to form alpha shape' , label='Auto segment', command=self.autoSegment) self.menuBar.addmenuitem('File', 'command', 'Normalise mesh', label='Normalise mesh', command=self.normaliseMesh) self.menuBar.addmenuitem('File', 'command', 'Normalise mesh for glutobj', label='Normalise mesh for glutobj', command=self.normalise4ObjMesh) self.menuBar.addmenuitem('File', 'separator') self.menuBar.addmenuitem('File', 'command', 'Exit program', label='Exit', command=self.quit) def createTools(self): """ Add buttons to the top of the GUI """ self.mouseDownFunc = {} self.modeClass = {} ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10, state='disabled') for key, balloon, mouseDownFunc, Mode in [ ('pointer','Edit drawing eventually. Right now this does nothing', self.drag, None) ,('vertex', 'Vertex mode', self.drawVertex, mesh.Vertex) ,('segment', 'Segment mode',self.selectSegmentPoint, mesh.Segment) ,('hole', 'hole mode',self.drawHole, mesh.Hole) ,('region', 'region mode',self.drawRegion, mesh.Region) ]: t = ToolBarButton(self, self.toolbar, key, '%s.gif' % key, command=self.selectFunc, balloonhelp=balloon, statushelp='') t.cycle("DrawMode") if key == 'pointer': #FIXME- this is specified in line 1062 as well # self.selectFunc('pointer') self.curFunc = self.drawVertex t.setInitialSunkenButton("DrawMode") self.modeClass[key] = Mode # for actions that occur when the mouse goes down self.mouseDownFunc[key] = mouseDownFunc def createZooms(self): """ Add zoom buttons to the top of the GUI """ ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10, state='disabled') zoom = '0.5' ToolBarButton(self, self.toolbar, zoom, 'zoom%s.gif' % zoom, command=self.selectZoom, balloonhelp='*%s zoom' % zoom, statushelp='') ToolBarButton(self, self.toolbar,'1.0', 'zoomToMesh.gif', command=self.ResizeToFitWrapper, balloonhelp='Zooms to mesh size', statushelp='') zoom = '2' ToolBarButton(self, self.toolbar, zoom, 'zoom%s.gif' % zoom, command=self.selectZoom, balloonhelp='*%s zoom' % zoom, statushelp='') def createEdits(self): """ Add Edit buttons to the top of the GUI """ ToolBarButton(self, self.toolbar, 'sep', 'sep.gif', width=10, state='disabled') for key, func, balloon in [ ('addVertex', self.windowAddVertex, 'add Vertex'), ('edit', self.windowEdit, 'edit selected object'), ('default', self.windowDefault, 'set default value for selected mode'), ('joinVer', self.joinVerticesButton, 'add Segments to connect all vertices'), ('autoSeg', self.autoSegmentButton, 'add Segments to form alpha shape'), ('meshGen', self.windowMeshGen, 'Generate Mesh')]: ToolBarButton(self, self.toolbar, key, '%s.gif' % key, command=func, balloonhelp=balloon, statushelp='' ) def createMesh(self): """ Build the data structures for storing the mesh objects """ self.mesh = mesh.Mesh() self.Vertices = visualmesh.vPoints(mesh.Vertex) self.Segments = visualmesh.vSegments(mesh.Segment) self.Holes = visualmesh.vPoints(mesh.Hole) self.Regions = visualmesh.vRegions(mesh.Region) self.UserMesh = visualmesh.vMesh([self.Vertices,self.Segments,self.Holes,self.Regions]) self.Triangles = visualmesh.vTriangles(mesh.Triangle) def deleteMesh(self): """ Delete the data structures for storing the mesh objects """ self.mesh = None self.Vertices = None self.Segments = None self.Triangles = None def addVertsAndSegs(self): """ Automatically add some verts and segs to the mesh.Used in Debugging """ v1 = self.drawVertex(0,0,None) v2 = self.drawVertex(0,200,None) v3 = self.drawVertex(200,0,None) v3 = self.drawVertex(300,100,None) self.drawSegment(v1,v2) self.drawSegment(v1,v3) self.drawSegment(v2,v3) #Since the new vertex may be off screen self.scrolledcanvas.resizescrollregion() def selectFunc(self, tag): """ Change the current mode class When changing from one mode to another """ self.mouseDownCurFunc = self.mouseDownFunc[tag] self.curModeClass = self.modeClass[tag] self.clearSelections() self.canvas.config(cursor='arrow') # I can make it arrow, but it will change back to pointer, after # adding an object # if self.curFunc == self.func['pointer']: # self.canvas.config(cursor='arrow') # else: # self.canvas.config(cursor='crosshair') def clearSelections(self): """ deselect objects that have been selected """ if self.selMeshObject: self.deselectMeshObject(self.selMeshObject, self.selMeshTag) if self.selVertex: self.deselectVertex(self.selVertex, self.selVertexTag) def selectZoom(self, tag): """ Zoom in or out of the current mesh view """ fraction = string.atof(tag) self.SCALE *= fraction self.scrolledcanvas.scale(ALL, 0, 0, fraction, fraction) # Redraw all of the vertices, holes and regions, #so the squares representing vertices # don't get bigger vertices = self.mesh.getUserVertices() holes = self.mesh.getHoles() regions = self.mesh.getRegions() MeshObjects = vertices + holes + regions for obj in MeshObjects: self.canvas.delete(obj.guiID) if self.selVertex == obj: obj.draw(self.canvas,obj.guiID, scale =self.SCALE ,colour= VERT_SELECT_ADDING_SEG_COLOR) elif self.selMeshObject == obj: obj.draw(self.canvas,obj.guiID, scale =self.SCALE ,colour= SELECT_COLOR) else: obj.draw(self.canvas,obj.guiID, scale =self.SCALE ) top, bottom = self.scrolledcanvas.xview() xcenter = (top + bottom)/2 xdiff = xcenter - top xcnew = xcenter - xdiff/fraction top, bottom = self.scrolledcanvas.yview() ycenter = (top + bottom)/2 ydiff = ycenter - top ycnew = ycenter - ydiff/fraction self.scrolledcanvas.resizescrollregion() # update so the moveto calls will work... self.scrolledcanvas.update() # but calling update now does make things jerky self.canvas.xview_moveto(xcnew) self.canvas.yview_moveto(ycnew) def windowAddVertex (self, parent): """ add a vertex using a window and entering x y values. the parent attribute isn't used by this function. need to userstand toolbarbutton.py to know how to get rid of it. """ dialog = AddVertexDialog(self.canvas) if dialog.xyValuesOk: print dialog.x print dialog.y self.drawVertex(dialog.x*self.SCALE,dialog.y*self.SCALE,None) #Since the new vertex may be off screen self.ResizeToFit() def windowDefault (self, parent): """ the parent attribute isn't used by this function. need to userstand toolbarbutton.py to know how to get rid of it. """ # self.UserMesh is a vMesh instance self.UserMesh.defaultWindow(self.canvas, self.curModeClass) def windowEdit (self, parent): """ the parent attribute isn't used by this function. need to userstand toolbarbutton.py to know how to get rid of it. """ if self.selMeshObject: self.UserMeshChanged = self.UserMesh.editWindow(self.canvas, self.selMeshObject, self.UserMeshChanged) def autoSegmentButton (self, parent): self.autoSegment() def autoSegment (self): """ add Segments to bound all vertices the parent attribute isn't used by this function. need to userstand toolbarbutton.py to know how to get rid of it. """ if len(self.mesh.getUserVertices()) >= 3: newsegs = self.mesh.autoSegment() for segment in newsegs: self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Segments.visualise(segment, self.uniqueID, self.canvas, self.SCALE) else: showerror('pMesh', 'Three or more vetices are needed to be able to autosegment.') def joinVerticesButton (self, parent): self.joinVertices() def joinVertices (self): """ add Segments to connect all vertices the parent attribute isn't used by this function. need to userstand toolbarbutton.py to know how to get rid of it. """ if len(self.mesh.getUserVertices()) >= 3: newsegs = self.mesh.joinVertices() for segment in newsegs: self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Segments.visualise(segment, self.uniqueID, self.canvas, self.SCALE) else: showerror('pMesh', 'Three or more vetices are needed to be able to join vertices.') def windowMeshGen (self, parent): """ The parent attribute isn't used by this function. need to understand toolbarbutton.py to know how to get rid of it. """ # Put exceptions round things. #catch failure in self.mesh.generateMesh dialog = MeshGenDialog(self.canvas, self.MeshMinAngle, self.MeshMaxArea, self.MeshnumTriangles, self.MeshMaxAreaLast) if dialog.ValuesOk: print dialog.minAngle print dialog.maxArea self.clearSelections() self.canvas.delete(ALL) if dialog.goodMaxArea == True: self.MeshMinAngle = dialog.minAngle self.MeshMaxArea = dialog.maxArea self.MeshMaxAreaLast = True self.mesh = self.MeshGenAreaAngle (dialog.minAngle, dialog.maxArea, self.mesh) elif dialog.goodNumTriangles == True: self.MeshMinAngle = dialog.minAngle self.MeshnumTriangles = dialog.numTriangles self.MeshMaxAreaLast = False self.mesh = self.MeshGenAreaNumTriangles (dialog.minAngle, dialog.numTriangles, self.mesh) else: pass print "userMeshChanged = False" self.UserMeshChanged = False self.visualiseMesh(self.mesh) print "Mesh Generation finished" def MeshGenAreaAngle (self, minAngle, maxArea, mesh): """ Generate a mesh, given a minAngle and max area """ tempMesh = mesh try: tempMesh.generateMesh(mode = "pzq"+str(minAngle) +"a"+str(maxArea) +"a") #So areas for regions will be used except AttributeError : # can't catch PyEval_RestoreThread # This doesn't catch tempMesh.generateMesh failing tempMesh = mesh return tempMesh def MeshGenAreaNumTriangles (self, minAngle, numTriangles, mesh): """ Generate a mesh, given a minAngle and rough # of triangles """ #get big triangles #calc area #calc max triangle area # tempMesh = mesh try: tempMesh.generateMesh("pzq1") except AttributeError : # can't catch PyEval_RestoreThread # This doesn't catch tempMesh.generateMesh failing pass meshArea = 0 for triangle in tempMesh.getTriangulation(): meshArea += triangle.calcArea() print "meshArea: ", meshArea maxArea = meshArea/numTriangles return self.MeshGenAreaAngle (minAngle, maxArea, self.mesh) def mouseDown(self, event): """ On a mouse down event, depending on the current state, either add a vertex or a seg etc """ self.curObject = None self.lastx = self.startx = self.canvas.canvasx(event.x) #The screen canvas has y 'flipped'. -1* unflips it self.lasty = self.starty = -1*self.canvas.canvasy(event.y) print "----------------------" self.mouseDownCurFunc( self.lastx, self.lasty,event) #!!! remove the event? # do last def rightMouseUp(self, event): """ On a right mouse button up event select the nearest object. """ found=False if event.widget.find_withtag(CURRENT): # if there's a widget with a tag [tag,string] = self.canvas.gettags(CURRENT) # get a list of them print "tag",tag #tags ('M*1008', 'current') if tag[:2] == 'M*': #!!! this can be removed when there are # only mesh objects on screen #key, value = string.split(tag, '*') objectID = tag print "Found!! objectID:", objectID meshObjects = self.getAllUserMeshObjects() # It may be a triangle, which is ignored if meshObjects.hasKey(objectID): selMeshObject = meshObjects.getMeshObject(objectID) found = True print "Found! selMeshObject", selMeshObject #Only select one object at a time if self.selMeshObject: self.deselectMeshObject(self.selMeshObject, self.selMeshTag) self.selectMeshObject(selMeshObject,objectID) def getAllUserMeshObjects(self): return self.UserMesh def DeleteSelectedMeshObject(self, event): """ if an object is selected, delete it. """ if self.selMeshObject: #an object is selected #first deselect the vertex, for selecting a segment if self.selVertex: self.deselectVertex(self.selVertex, self.selVertexTag) ObjectsToVisuallyDelete = self.mesh.deleteMeshObject (self.selMeshObject) for drawOb in ObjectsToVisuallyDelete: self.UserMesh.unvisualise(drawOb, self.canvas) self.selMeshObject = None self.selMeshTag = None def selectMeshObject(self, meshObject, objectID): """ selected a mesh object. """ self.canvas.delete(objectID) self.selMeshObject = meshObject self.selMeshTag = objectID meshObject.draw(self.canvas,objectID, scale =self.SCALE ,colour = SELECT_COLOR) def deselectMeshObject(self, meshObject, objectID): """ deselected a mesh object. """ self.canvas.delete(objectID) self.selMeshObject = None self.selMeshTag = None if isinstance(meshObject, mesh.Segment): meshObject.draw(self.canvas,objectID, scale =self.SCALE ,colour = SEG_COLOUR) else: meshObject.draw(self.canvas,objectID, scale =self.SCALE ) def drag(self,x,y,event): """ Hack function. called when in select and left mouse goes down """ pass def drawVertex(self,x,y,event): """ draw a vertex object, plus add it to the mesh data structure event isn't used """ self.serial +=1 self.uniqueID = 'M*%d' % self.serial vert = self.Vertices.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event) self.UserMeshChanged = True return vert def drawHole(self,x,y,event): """ draw a hole object, plus add it to the mesh data structure event isn't used """ self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.userMeshChanged = True hole = self.Holes.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event) return hole def drawRegion(self,x,y,event): """ draw a region object, plus add it to the mesh data structure event isn't used """ self.serial +=1 self.uniqueID = 'M*%d' % self.serial region = self.Regions.draw(x,y,self.mesh,self.uniqueID,self.SCALE,self.canvas,event) return region def selectSegmentPoint(self,x,y, event): """ logic when selecting a vertex object to add a segment """ found=False if event.widget.find_withtag(CURRENT): # if there's a widget with a tag [tag,string] = self.canvas.gettags(CURRENT) # get a list of them print "tag",tag #tags ('M*1008', 'current') objectID = tag #print "Found!! objectID:", objectID if self.Vertices.hasKey(objectID): #isinstance(self.meshObjects[objectID],mesh.Vertex): vertex = self.Vertices.getMeshObject(objectID) found = True print "Found! vertex", vertex if found and self.selVertex == vertex: print "The selected vertex has already been selected" #The selected vertex has already been selected # therefore deselect it self.deselectVertex(self.selVertex, self.selVertexTag) found = False if found: #A vertex has been selected! if self.selVertex: if self.mesh.isUserSegmentNew(self.selVertex,vertex): #vertex is the 2nd vertex self.drawSegment(vertex,self.selVertex) self.deselectVertex(self.selVertex, self.selVertexTag) self.selectVertex(vertex,objectID) else: print "vertex is the 1st vertex" #vertex is the 1st vertex self.selectVertex(vertex,objectID) else: print " There are no widgets. This happen's too much" def selectVertex(self, vertex,objectID): """ select a vertex object when adding a segment """ self.canvas.delete(objectID) self.selVertex = vertex self.selVertexTag = objectID vertex.draw(self.canvas,objectID, scale =self.SCALE ,colour = VERT_SELECT_ADDING_SEG_COLOR) def deselectVertex(self, vertex,objectID): """ deselect a vertex object when adding a segment """ self.canvas.delete(objectID) self.selVertex = None self.selVertexTag = None vertex.draw(self.canvas,objectID, scale =self.SCALE ) def drawSegment(self,v1,v2): """ Create a seg object, draw it and add it to the mesh data structure """ self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.userMeshChanged = True seg = self.Segments.draw(v1,v2,self.mesh,self.uniqueID,self.SCALE,self.canvas,None) return seg def visualiseMesh(self,mesh): """ visualise vertices, segments, triangulation, holes """ for triangle in mesh.getTriangulation(): self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Triangles.visualise(triangle, self.uniqueID, self.canvas, self.SCALE) for segment in mesh.getUserSegments(): self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Segments.visualise(segment, self.uniqueID, self.canvas, self.SCALE) for vertex in mesh.getUserVertices(): self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Vertices.visualise(vertex, self.uniqueID, self.canvas, self.SCALE) for hole in mesh.getHoles(): self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Holes.visualise(hole, self.uniqueID, self.canvas, self.SCALE) for region in mesh.getRegions(): self.serial +=1 self.uniqueID = 'M*%d' % self.serial self.Regions.visualise(region, self.uniqueID, self.canvas, self.SCALE) def normalise4ObjMesh(self): if self.mesh: self.clearSelections() self.canvas.delete(ALL) self.mesh.normaliseMesh(400,-200,20) self.visualiseMesh(self.mesh) self.ResizeToFit() self.ResizeToFit() def normaliseMesh(self): if self.mesh: self.clearSelections() self.canvas.delete(ALL) self.mesh.normaliseMesh(1,0,1) self.visualiseMesh(self.mesh) self.ResizeToFit() self.ResizeToFit() def clearMesh(self): """Clear the current mesh object, and the canvas """ self.clearSelections() self.canvas.delete(ALL) self.deleteMesh() self.initData() self.createMesh() def exportObj(self): fileType = "obj" fileTypeDesc = "obj mesh" ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType), ("All Files", "*")]) if ofile: addOn = "." + fileType jumpback = - len(addOn) if ofile[jumpback:] != addOn: ofile = ofile + addOn try: self.mesh.exportASCIIobj(ofile) except IOError: showerror('Export ASCII file', 'Can not write to file.') except RuntimeError: showerror('Export ASCII file', 'No triangulation to export.') def ImportUngenerate(self): ofile = tkFileDialog.askopenfilename(initialdir=self.currentPath, filetypes=[ ("ungenerated polygon information", "txt"), ("All Files", "*")]) if ofile == "": # The user cancelled the loading action return try: self.clearSelections() self.canvas.delete(ALL) dict = mesh.importUngenerateFile(ofile) self.mesh.addVertsSegs(dict) except SyntaxError: #this is assuming that the SyntaxError is thrown in #loadxyafile showerror('File error', ofile + ' is not in the correct format.') except IOError: #!!! this error type can not be thrown? showerror('File error', 'file ' + ofile + ' could not be found.') except RuntimeError: showerror('File error', 'file ' + ofile + ' has an unknown file type.') self.visualiseMesh(self.mesh) self.ResizeToFit() def exportASCIIsegmentoutlinefile(self): fileType = "tsh" fileTypeDesc = "text mesh" ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType), ("All Files", "*")]) if ofile: addOn = "." + fileType jumpback = - len(addOn) if ofile[jumpback:] != addOn: ofile = ofile + addOn try: self.mesh.exportASCIIsegmentoutlinefile(ofile) except IOError: showerror('Export ASCII file', 'Can not write to file.') def exportxyafile(self): fileType = "xya" fileTypeDesc = "text vertices" ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType), ("All Files", "*")]) if ofile: addOn = "." + fileType jumpback = - len(addOn) if ofile[jumpback:] != addOn: ofile = ofile + addOn try: self.mesh.exportxyafile(ofile) except IOError: showerror('Export ASCII file', 'Can not write to file.') def exportTriangulation(self): """ This function is not used? """ fileType = "tsh" fileTypeDesc = "text mesh" ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType), ("All Files", "*")]) if ofile: addOn = "." + fileType jumpback = - len(addOn) if ofile[jumpback:] != addOn: ofile = ofile + addOn try: self.mesh.exportASCIItrianglulationfile(ofile) except IOError: showerror('Export ASCII file', 'Can not write to file.') except RuntimeError: showerror('Export ASCII file', 'No triangulation to export.') def exportMesh(self): fileType = "tsh" fileTypeDesc = "text Mesh" ofile = tkFileDialog.asksaveasfilename(filetypes=[(fileTypeDesc, fileType), ("All Files", "*")]) if ofile: addOn = "." + fileType jumpback = - len(addOn) if ofile[jumpback:] != addOn: ofile = ofile + addOn try: self.mesh.exportASCIImeshfile(ofile) except IOError: showerror('Export ASCII file', 'Can not write to file.') except RuntimeError: showerror('Export ASCII file', 'No mesh to export.') def importFile(self): """ import mesh data from a variety of formats (currently 2!) """ print "self.currentPath",self.currentPath ofile = tkFileDialog.askopenfilename(initialdir=self.currentPath, filetypes=[ ("text Mesh", "tsh"), ("points", "xya"), ("All Files", "*")]) if ofile == "": # The user cancelled the loading action return try: newmesh = mesh.importMeshFromFile(ofile) self.currentPath, dummy = os.path.split(ofile) #print "be good self.currentPath",self.currentPath self.currentFilePathName = ofile self.clearMesh() self.mesh = newmesh self.visualiseMesh(self.mesh) self.ResizeToFit() except SyntaxError: #this is assuming that the SyntaxError is thrown in #loadxyafile showerror('File error', ofile + ' is not in the correct format.') except IOError: #!!! this error type can not be thrown? showerror('File error', 'file ' + ofile + ' could not be found.') except RuntimeError: showerror('File error', 'file ' + ofile + ' has an unknown file type.') # Could not get the file name to showup in the title #appname = ofile + " - " + APPLICATION_NAME #print "appname",appname def ResizeToFitWrapper(self, Parent): """ The parent attribute isn't used by this function. need to understand toolbarbutton.py to know how to get rid of it. """ self.ResizeToFit() def ResizeToFit(self): """Visualise the mesh so it fits in the window""" if self.mesh.getUserVertices() == []: return #There are no vertices! # Resize the window self.scrolledcanvas.resizescrollregion() # I need this so the xview values are correct self.scrolledcanvas.update() xtop, xbottom = self.scrolledcanvas.xview() ytop, ybottom = self.scrolledcanvas.yview() xdiff = xbottom-xtop ydiff = ybottom-ytop if xdiff == 1 and xdiff == 1: #The mesh might be too small. #Make it too large, then resize #!!! Recursive hack. Should be a better way fraction = 50 self.SCALE *= fraction self.scrolledcanvas.scale(ALL, 0, 0, fraction, fraction) self.ResizeToFit() else: # without 0.99 some of the mesh may be off screen fraction = 0.99*min(xdiff,ydiff) self.selectZoom(fraction) def saveDrawing(self): """ Save the current drawing """ #print "dsg!!! self.currentFilePathName ",self.currentFilePathName if (self.currentFilePathName[-4:] != ".tsh"): # force user to choose a name self.saveAsDrawing() else: self.exportASCIItrianglulationfile(self.currentFilePathName) def saveAsDrawing(self): """ Save the current drawing, prompting for a file name """ ofile = tkFileDialog.asksaveasfilename(initialdir=self.currentPath, filetypes=[("text mesh", "tsh"), ("All Files", "*")]) print "aa" if ofile: if ofile[-4:] == ".tsh": self.currentFilePathName = ofile else: self.currentFilePathName = ofile + ".tsh" self.exportASCIItrianglulationfile(self.currentFilePathName) def exportASCIItrianglulationfile(self,currentFilePathName): """ Have a warning prompt when saving a mesh where the generated mesh is different from the user mesh - eg boundary tags that aren't carried thru. Warning ~"Mesh not generated after changes. Generate mesh? " - cancel, don't gen, don't save. Yes - generate mesh, go to save screen. No - goto save screen. To implement this need to know when the user has done a change, and the mesh hasn't been generated. If there is no generated mesh do not prompt. """ #print "self.UserMeshChanged",self.UserMeshChanged #print "self.mesh.isTriangulation()",self.mesh.isTriangulation() if (self.UserMeshChanged) and self.mesh.isTriangulation(): m = _show("Warning", "A triangulation has not been generated, after mesh changes. Generate triangulation before saving?", icon=QUESTION, type=YESNOCANCEL) if m == "no": self.mesh.exportASCIItrianglulationfile(currentFilePathName) self.UserMeshChanged = False elif m == "cancel": pass elif m == "yes": self.windowMeshGen(None) self.mesh.exportASCIItrianglulationfile(currentFilePathName) else: self.mesh.exportASCIItrianglulationfile(currentFilePathName) self.UserMeshChanged = False def initData(self): """ Initialise various lists and flags """ self.serial = 1000 self.currentFilePathName = 'untitled' # these are attributes I've added self.SCALE = 1 self.selVertex = None #The last vertex selected, in draw seg mode self.selVertexTag = None # The selected vertex drawn object tag self.mesh = None self.MeshMinAngle = 20 self.MeshMaxArea = 200 self.MeshnumTriangles = 20 self.MeshMaxAreaLast = False self.selMeshObject = None # The mesh object selected in the current mode self.selMeshTag = None mesh.Segment.set_default_tag("") self.UserMeshChanged = False def ipostscript(self): """ Print the canvas as a postscript file """ ofile = tkFileDialog.asksaveasfilename(filetypes=[("postscript", "ps"), ("All Files", "*")]) if ofile: if ofile[-3:] != ".ps": ofile = ofile + ".ps" postscript = self.canvas.postscript() fd = open(ofile, 'w') fd.write(postscript) fd.close() def close(self): self.quit() def createInterface(self): """ Call all functions that create the GUI interface """ self.initData() self.createMesh() AppShell.AppShell.createInterface(self) self.createButtons() self.createMenus() self.createBase() self.createTools() self.createZooms() self.createEdits() #self.addVertsAndSegs() # !!!DSG start pmesh with a triangle self.selectFunc('pointer') self.currentPath = os.getcwd() def loadtestmesh(self,ofile): """ debugging script to test loading a file """ fd = open(ofile) a = mesh.Vertex (-10.0, 0.0) d = mesh.Vertex (0.0, 4.0) f = mesh.Vertex (4.0,0.0) g = mesh.Vertex (-5.0,5.0) s1 = mesh.Segment(a,d) s2 = mesh.Segment(d,f) s3 = mesh.Segment(a,f) r1 = mesh.Region(0.3, 0.3) m = mesh.Mesh(userVertices=[a,d,f,g], userSegments=[s1,s2,s3], regions=[r1] ) fd.close() print 'returning m' return oadtestmesh(ofile) class AddVertexDialog(Dialog): """ Dialog box for adding a vertex by entering co-ordindates """ def body(self, master): """ GUI description """ self.title("Add New Vertex") Label(master, text='X position:').grid(row=0, sticky=W) Label(master, text='Y position:').grid(row=1, sticky=W) self.xstr = Entry(master, width = 16, name ="entry") self.ystr = Entry(master, width = 16) self.xstr.grid(row=0, column=1, sticky=W) self.ystr.grid(row=1, column=1, sticky=W) self.x = 0 self.y = 0 self.xyValuesOk = False def apply(self): """ check entered values """ try: self.x = float(self.xstr.get()) self.y = float(self.ystr.get()) self.xyValuesOk = True except ValueError: showerror('Bad Vertex values', 'X Y values are not numbers.') class MeshGenDialog(Dialog): """ Dialog box for generating a mesh """ # initial values, hard coded. # should be values associated with the current mesh lastMinAngle = 20 lastMaxArea = 100 def __init__(self, parent, minAngle, maxArea, numTriangles, MeshMaxAreaLast): self.minAngle = minAngle self.maxArea = maxArea self.numTriangles = numTriangles self.MeshMaxAreaLast = MeshMaxAreaLast Dialog.__init__(self, parent) def body(self, master): """ GUI description """ self.title("Generate Mesh") Label(master, text='Minimum Angle(0 - 40):').grid(row=0, sticky=W) Label(master, text='Angles>33 may not converge').grid(row=1, sticky=W) Label(master, text='Maximum Area:').grid(row=2, sticky=W) Label(master, text='OR # of triangles:').grid(row=3, sticky=W) minAngleVar = StringVar() minAngleVar.set(self.minAngle) self.minAnglestr = Entry(master, width = 16, textvariable = minAngleVar, takefocus=1) if (self.MeshMaxAreaLast): maxAreaVar = StringVar() maxAreaVar.set(self.maxArea) self.maxAreastr = Entry(master, textvariable = maxAreaVar, width = 16) self.numTrianglesstr = Entry(master, width = 16) else: self.maxAreastr = Entry(master, width = 16) numTrianglesVar = StringVar() numTrianglesVar.set(self.numTriangles) self.numTrianglesstr = Entry(master, textvariable = numTrianglesVar, width = 16) self.minAnglestr.grid(row=0, column=1, sticky=W) self.maxAreastr.grid(row=2, column=1, sticky=W) self.numTrianglesstr.grid(row=3, column=1, sticky=W) self.numTriangles = 0 self.minAngle = 0 self.maxArea = 0 self.ValuesOk = False def apply(self): """ check entered values """ self.goodMaxArea = self.goodNumTriangles = True self.ValuesOk = True try: self.minAngle = float(self.minAnglestr.get()) MeshGenDialog.lastMinAngle =self.minAngle except ValueError: self.ValuesOk = False showerror('Bad mesh generation values', ' Values are not numbers.') try: self.maxArea = float(self.maxAreastr.get()) MeshGenDialog.lastMaxArea =self.maxArea except ValueError: self.goodMaxArea = False try: self.numTriangles = int(self.numTrianglesstr.get()) MeshGenDialog.lastNumTriangles =self.numTriangles except ValueError: self.goodNumTriangles= False if self.goodMaxArea == False and self.goodNumTriangles == False: self.ValuesOk = False showerror('Bad mesh generation values', 'Values are not numbers.') if self.goodMaxArea == True and self.goodNumTriangles == True: self.ValuesOk = False showerror('Bad mesh generation values', 'Give a maximum area OR number of triangles, not both.') try: # value checking if self.minAngle <0.0 or self.minAngle >40.0: raise IOError if self.goodMaxArea == True and self.maxArea <0.0: raise IOError if self.goodNumTriangles == True and self.numTriangles <=0: raise IOError except IOError: self.ValuesOk = False showerror('Bad mesh generation values', 'Values are out of range.') if __name__ == '__main__': draw = Draw() draw.run()