Пример #1
0
 def __init__(self, parent = None):
     """
     Constructor
     """
     QMainWindow.__init__(self, parent)
     
     self.setupUi(self)
     self.ignoreValueEvents=False
     
     self.workerThread=ToolpathThread()
     self.workerThread.workFinished.connect(self.toolpathCalculationFinished)
     
     self.postprocessorComboBox.addItems(Postprocessor.listPostprocessors())
     
     # Signal-Slot-connections
     self.workflowTabs.currentChanged.connect(self.workflowTabChanged)
     self.toolpathList.itemSelectionChanged.connect(self.toolpathSelectionChanged)
     self.toolpathAddButton.clicked.connect(self.addToolpath)
     self.toolpathRemoveButton.clicked.connect(self.removeSelectedToolpath)
     # unselect toolpath (and auto-close the settings) when pressing "OK" in the settings dialog
     self.toolpathSettingsButtonGroup.clicked.connect(self.unselectToolpaths)
     self.selectNCFileButton.clicked.connect(self.selectNCFile)
     self.saveNCButton.clicked.connect(self.saveNCFile)
     self.simSlider.valueChanged.connect(self.simSliderChanged)
     self.simSpeedSlider.valueChanged.connect(self.simSpeedSliderChanged)
     self.runSimButton.clicked.connect(self.runPauseSimulation)
     
     self.simTimer=QTimer()
     self.simTimer.setInterval(100)
     self.simTimer.timeout.connect(self.simulationTick)
     
     self.updatePreviewLazyTimer=QTimer()
     self.updatePreviewLazyTimer.setInterval(1000)
     self.updatePreviewLazyTimer.setSingleShot(True)
     self.updatePreviewLazyTimer.timeout.connect(self.updatePreviewImmediately)
     
     # init
     # parameter name, object, default value, type
     # (type currently unused)
     self.globalSettingsParameters=[{"name":"materialDiameter", "object":self.materialDiameterSpinBox, "default":0, "type": float}, 
                           {"name":"materialLength", "object":self.materialLengthSpinBox, "default": 0, "type": float},
                           {"name":"flightDistance", "object":self.flightDistanceSpinBox, "default": 20, "type": float}, 
                           {"name":"approachDistance", "object":self.safeApproachSpinBox, "default": 2, "type": float}, 
                           {"name":"curveTolerance", "object":self.curveToleranceSpinBox, "default": 0.10, "type": float}, 
                           {"name":"postprocessorId", "object":self.postprocessorComboBox, "default": 0, "type": "enum"}, 
                           ]
     self.toolpathSettingsParameters=[{"name":"name", "object":self.toolpathNameLineEdit, "default":"Unnamed Toolpath", "type": str},
                                      {"name":"tool", "object":self.toolSpinBox, "default":0, "type": int},
                                      {"name":"feedValue", "object":self.feedSpinBox, "default":10, "type": float},
                                      {"name":"feedMode", "object":self.feedModeComboBox, "default":0, "type": "enum"},
                                      {"name":"speedValue", "object":self.speedSpinBox, "default":2000, "type": float},
                                      {"name":"speedMode", "object":self.speedModeComboBox, "default":0, "type": "enum"}, 
                                       {"name":"cutDepth", "object":self.cutDepthSpinBox, "default": 0.5, "type": float}, 
                                       {"name":"finalPassDepth", "object":self.finalPassSpinBox, "default": 0.2, "type": float}
                                      # {"name":"", "object":, "default":, "type": },
                                      ]
                                      
     # connect all changes of GUI settings elements to updateValuesFromGUI():
     for s in self.globalSettingsParameters + self.toolpathSettingsParameters:
         if hasattr(s["object"], 'valueChanged'):
             s["object"].valueChanged.connect(self.updateValuesFromGUI)
         elif hasattr(s["object"], 'currentIndexChanged'):
             s["object"].currentIndexChanged.connect(self.updateValuesFromGUI)
         elif hasattr(s["object"], 'textEdited'):
             s["object"].textEdited.connect(self.updateValuesFromGUI)
         else:
             raise Exception("could not connect unknown GUI-Object "+str(s["object"])+" to self.updateValuesFromGUI")
     
     
     self.loadEmptyFile()
     self.workflowTabChanged(0)
Пример #2
0
class VisiLatheGUI(QMainWindow, Ui_MainWindow):
    """
    Class documentation goes here.
    """
    
    # constants:
    INDEX_DRAWINGS=1
    INDEX_TOOLPATHS=2
    INDEX_SIMULATION=3
    FILE_STRUCTURE_VERSION="2013-12-27" # increment this as soon as the file format is incompatible with earlier versions
        
    
    def __init__(self, parent = None):
        """
        Constructor
        """
        QMainWindow.__init__(self, parent)
        
        self.setupUi(self)
        self.ignoreValueEvents=False
        
        self.workerThread=ToolpathThread()
        self.workerThread.workFinished.connect(self.toolpathCalculationFinished)
        
        self.postprocessorComboBox.addItems(Postprocessor.listPostprocessors())
        
        # Signal-Slot-connections
        self.workflowTabs.currentChanged.connect(self.workflowTabChanged)
        self.toolpathList.itemSelectionChanged.connect(self.toolpathSelectionChanged)
        self.toolpathAddButton.clicked.connect(self.addToolpath)
        self.toolpathRemoveButton.clicked.connect(self.removeSelectedToolpath)
        # unselect toolpath (and auto-close the settings) when pressing "OK" in the settings dialog
        self.toolpathSettingsButtonGroup.clicked.connect(self.unselectToolpaths)
        self.selectNCFileButton.clicked.connect(self.selectNCFile)
        self.saveNCButton.clicked.connect(self.saveNCFile)
        self.simSlider.valueChanged.connect(self.simSliderChanged)
        self.simSpeedSlider.valueChanged.connect(self.simSpeedSliderChanged)
        self.runSimButton.clicked.connect(self.runPauseSimulation)
        
        self.simTimer=QTimer()
        self.simTimer.setInterval(100)
        self.simTimer.timeout.connect(self.simulationTick)
        
        self.updatePreviewLazyTimer=QTimer()
        self.updatePreviewLazyTimer.setInterval(1000)
        self.updatePreviewLazyTimer.setSingleShot(True)
        self.updatePreviewLazyTimer.timeout.connect(self.updatePreviewImmediately)
        
        # init
        # parameter name, object, default value, type
        # (type currently unused)
        self.globalSettingsParameters=[{"name":"materialDiameter", "object":self.materialDiameterSpinBox, "default":0, "type": float}, 
                              {"name":"materialLength", "object":self.materialLengthSpinBox, "default": 0, "type": float},
                              {"name":"flightDistance", "object":self.flightDistanceSpinBox, "default": 20, "type": float}, 
                              {"name":"approachDistance", "object":self.safeApproachSpinBox, "default": 2, "type": float}, 
                              {"name":"curveTolerance", "object":self.curveToleranceSpinBox, "default": 0.10, "type": float}, 
                              {"name":"postprocessorId", "object":self.postprocessorComboBox, "default": 0, "type": "enum"}, 
                              ]
        self.toolpathSettingsParameters=[{"name":"name", "object":self.toolpathNameLineEdit, "default":"Unnamed Toolpath", "type": str},
                                         {"name":"tool", "object":self.toolSpinBox, "default":0, "type": int},
                                         {"name":"feedValue", "object":self.feedSpinBox, "default":10, "type": float},
                                         {"name":"feedMode", "object":self.feedModeComboBox, "default":0, "type": "enum"},
                                         {"name":"speedValue", "object":self.speedSpinBox, "default":2000, "type": float},
                                         {"name":"speedMode", "object":self.speedModeComboBox, "default":0, "type": "enum"}, 
                                          {"name":"cutDepth", "object":self.cutDepthSpinBox, "default": 0.5, "type": float}, 
                                          {"name":"finalPassDepth", "object":self.finalPassSpinBox, "default": 0.2, "type": float}
                                         # {"name":"", "object":, "default":, "type": },
                                         ]
                                         
        # connect all changes of GUI settings elements to updateValuesFromGUI():
        for s in self.globalSettingsParameters + self.toolpathSettingsParameters:
            if hasattr(s["object"], 'valueChanged'):
                s["object"].valueChanged.connect(self.updateValuesFromGUI)
            elif hasattr(s["object"], 'currentIndexChanged'):
                s["object"].currentIndexChanged.connect(self.updateValuesFromGUI)
            elif hasattr(s["object"], 'textEdited'):
                s["object"].textEdited.connect(self.updateValuesFromGUI)
            else:
                raise Exception("could not connect unknown GUI-Object "+str(s["object"])+" to self.updateValuesFromGUI")
        
        
        self.loadEmptyFile()
        self.workflowTabChanged(0)
        
    ####################################################################################
    # Main GUI
    ####################################################################################

    def about(self):
        QMessageBox.information(self, "VisiLathe", "VisiLathe (C) 2013 VisiLathe Contributors.\nPatrick Kanzler, Max Gaukler\nhttp://github.com/mgmax/VisiLathe\n\nLicensed under GNU GPL 2, see the file COPYING in the program directory")
    
    def workflowTabChanged(self, index):
        # The "workflow" tabs at the left were switched to another step
        # Change the rest of the GUI accordingly
        
        # tab indices - need to be readjusted when other tabs are added

        
        # show/hide the additional settings GroupBox
        self.drawingSettings.setVisible(index==self.INDEX_DRAWINGS)
        self.resetSimulation()
        self.toolpathSelectionChanged()
        
    def workflowNext(self):
        # switch to next workflow tab
        self.workflowTabs.setCurrentIndex(self.workflowTabs.currentIndex()+1)
    
    def updatePreview(self):
        self.updatePreviewLazyTimer.start()
        self.previewWidget.setInvalid()
        
    def updatePreviewImmediately(self): # called after 100ms of inactivity
        print "uS"
        self.previewWidget.setMaterialSize(self.globalSettings["materialLength"], self.globalSettings["materialDiameter"])
        self.workerThread.restartWithData({"toolpaths":self.toolpaths, "globalSettings":self.globalSettings})
        print "uE"
    
    def toolpathCalculationFinished(self):
        print "fS"
        c=self.workerThread.getOutput()
        if c==None:
            c=[] # Preview-rendering was aborted/restarted
        self.previewWidget.showMachineCode(c)
        self.simSlider.setMaximum(len(c))
        self.simSlider.setValue(len(c))
        self.simSliderChanged()
        print "fE"
    
    ####################################################################################
    # settings handling
    ####################################################################################
    
    # GUI -> globalSettings, toolpathSettings
    def updateValuesFromGUI(self):
        if self.ignoreValueEvents:
            return
        self.globalSettings=ParameterHelper.getValuesFromGUI(self.globalSettingsParameters)
        if self.currentToolpath is not None:
            self.currentToolpath.settings=ParameterHelper.getValuesFromGUI(self.toolpathSettingsParameters)
        #self.loadToolpathsInGUI()        
        self.updatePreview()
    
    
    # globalSettings, toolpathSettings -> GUI
    def loadValuesInGUI(self):
        if self.ignoreValueEvents:
            return
        self.ignoreValueEvents=True
        ParameterHelper.setValuesInGUI(self.globalSettingsParameters, self.globalSettings)    
        if self.currentToolpath is not None:
            ParameterHelper.setValuesInGUI(self.toolpathSettingsParameters, self.currentToolpath.settings)
        self.loadToolpathsInGUI()
        self.updatePreview()
        self.ignoreValueEvents=False
    
    ####################################################################################
    # DRAWING
    ####################################################################################
    
    
    # TODO TODO TODO 
    
    
    
    ####################################################################################
    # TOOLPATH
    ####################################################################################
    
    # toolpath array -> GUI
    def loadToolpathsInGUI(self):
        oldIgnoreValueEvents=self.ignoreValueEvents
        self.ignoreValueEvents=True
        self.toolpathList.clear()
        for t in self.toolpaths:
            self.toolpathList.addItem(t.settings["name"])
        # restore selection
        if self.currentToolpath in self.toolpaths:
            self.toolpathList.setItemSelected(self.toolpathList.item(self.toolpaths.index(self.currentToolpath)), True)
        else:
            self.currentToolpath = None # current toolpath was deleted
        self.ignoreValueEvents=oldIgnoreValueEvents
        self.toolpathSelectionChanged()
    
    def unselectToolpaths(self):
        for i in self.toolpathList.selectedItems():
            self.toolpathList.setItemSelected(i, False)
    
    # show/hide the toolpath settings GroupBox
    def toolpathSelectionChanged(self):
        if self.ignoreValueEvents:
            return
#        print self.toolpathList.currentRow()
#        print self.toolpathList.selectedItems()
        self.toolpathSettings.setVisible(self.workflowTabs.currentIndex()==self.INDEX_TOOLPATHS and len(self.toolpathList.selectedItems())>0)
        if len(self.toolpathList.selectedItems())>0:
            self.currentToolpath=self.toolpaths[self.toolpathList.currentRow()]
        else:
            self.currentToolpath=None
        self.loadValuesInGUI() # necessary?
        self.toolpathRemoveButton.setDisabled(len(self.toolpathList.selectedItems())==0)
        
    def addToolpath(self):
        defaultSettings={}
        for s in self.toolpathSettingsParameters:
            defaultSettings[s["name"]]=s["default"]
        t=ZParallelToolpath(shape=CylinderShape(30, 50, 15), settings=defaultSettings)
        self.toolpaths.append(t)
        self.currentToolpath=t
        # TODO selection of different toolpath types
        # TODO default settings for toolpath: specific for toolpathtype....
        self.loadToolpathsInGUI()
        self.toolpathSelectionChanged()
        self.loadValuesInGUI()
        self.toolpathNameLineEdit.selectAll()
        self.toolpathNameLineEdit.setFocus()
    
    def removeSelectedToolpath(self):
        if len(self.toolpathList.selectedItems())==0:
            return
        self.toolpaths.pop(self.toolpathList.currentRow())
        self.loadToolpathsInGUI()
        self.loadValuesInGUI()
        
    
    ####################################################################################
    # Simulation
    ####################################################################################
    
    def resetSimulation(self):
        self.simTimer.stop()
        self.simSlider.setValue(self.simSlider.maximum())
        self.runSimButton.setChecked(False)
        self.simSliderChanged()
        
    def runPauseSimulation(self):
        if self.simTimer.isActive():
            self.simTimer.stop()
            self.runSimButton.setChecked(False)
        else:
            # not running: start
            if self.simSlider.value()==self.simSlider.maximum():
                # if at end, rewind to start
                self.simSlider.setValue(0)
            self.runSimButton.setChecked(True)
            self.simSpeedSliderChanged() # initialize timer period
            self.simTimer.start()
            
    
    def simulationTick(self):
        # increment simulation slider, this causes an event that updates the preview
        self.simSlider.setValue(self.simSlider.value()+1)
        if self.simSlider.value()==self.simSlider.maximum():
            self.resetSimulation()
            
    def simSliderChanged(self):
        self.previewWidget.setPreviewStep(self.simSlider.value())
        self.simProgressLabel.setText("{0}/{1}".format(self.simSlider.value(), self.simSlider.maximum()))
    
    def simSpeedSliderChanged(self):
        self.simTimer.setInterval(1000/self.simSpeedSlider.value())
        if self.simTimer.isActive():
            self.simTimer.start() # restart with new interval
    
    ####################################################################################
    # Save/Open Project
    ####################################################################################
    def loadEmptyFile(self):
        self.globalSettings={}
        for s in self.globalSettingsParameters:
            self.globalSettings[s["name"]]=s["default"]
        self.NCFilename=""
        self.projectFilename=""
        self.currentToolpath=None
        self.toolpaths=[]
        self.loadValuesInGUI()
        self.loadToolpathsInGUI()
        self.workflowTabs.setCurrentIndex(0)
        
    def newProject(self):
        # TODO check for unsaved changes?
        self.loadEmptyFile()
        self.statusbar.showMessage("Successfully loaded empty project.", 5000)
        
    def saveProjectAs(self):
        f = QFileDialog.getSaveFileName(self, "Save Project As...",
                                self.projectFilename,
                                "VisiLathe Project (*.visilathe)")
        self.projectFilename=f
        self.saveProject()
    
    def saveProject(self):
        try:
            f=file(self.projectFilename, "w")
            # ATTENTION: increment FILE_STRUCTURE_VERSION on every incompatible data format change
            data={"fileStructureVersion":self.FILE_STRUCTURE_VERSION, "globalSettings":self.globalSettings, "toolpaths":self.toolpaths}
            pickle.dump(data, f)
            f.close()
            QMessageBox.warning(self, "Warning","There is currently no guarantee that projects can be opened in future versions of VisiLathe! The file format is likely to change and there won't be any backwards compatibility.")
            self.statusbar.showMessage("Successfully saved project.", 5000)
        except Exception, e:
            # TODO better messages
            QMessageBox.critical(self, "Error saving file", e.__repr__() + "\n\n" + str(e))