def __init__(self, parent = None): QtGui.QMainWindow.__init__(self, parent) self.ui = Ui_MainWindow() if os.path.exists('/Users/emonson/Data/Fodava/EMoGWDataSets'): self.openFilesDefaultPath = '/Users/emonson/Data/Fodava/EMoGWDataSets' else: self.openFilesDefaultPath = QtCore.QDir.homePath() data_file = QtGui.QFileDialog.getOpenFileName(self, "Load Saved Matlab File", self.openFilesDefaultPath, "All Files (*);;Matlab Files (*.mat)") # DataSource loads .mat file and can generate data from it for other views print "Loading data from ", str(data_file) self.ds = DataSource(str(data_file)) self.last_data_dir = os.path.dirname(str(data_file)) # All view classes have access to an instance of that data source for internal queries # Note that the only view which will pull and display data right away is the icicle view # the other views need to be able to initialize without any data and only pull and show # upon the first AnnotationChanged event... # View #0 -- Icicle View self.ice_class = IcicleNoView(self.ds) self.ice_class.GetRenderWindow().SetPosition(50,500) self.ice_class.GetRenderWindow().SetSize(630,470) self.ice_al_out = self.ice_class.GetOutputAnnotationLink() # Note: With the way I've implemented the output annotation link in PCoords chart, # it will always have a selection node, but the selection list may have no tuples if # it's an empty selection (and event gets fired on every empty selection # View #1 -- PCoords View self.pc_class = PCoordsChart(self.ds) self.pc_class.SetInputAnnotationLink(self.ice_al_out) self.pc_al_out = self.pc_class.GetOutputAnnotationLink() self.pc_al = self.pc_class.GetAnnotationLink() # View #2 -- Image Flow View self.if_class = ImageFlow(self.ds, self.pc_al_out) self.if_al_out = self.if_class.GetOutputAnnotationLink() self.pc_class.SetHighlightAnnotationLink(self.if_al_out) # View #3 -- Detail View self.nf_class = DetailImageFlow(self.ds, self.if_al_out) self.nf_class.SetFlowDirection(Direction.Vertical) self.nf_al_out = self.nf_class.GetOutputAnnotationLink() # View #4 -- XY Chart View self.xy_class = XYChart(self.ds) self.xy_class.SetInputAnnotationLink(self.ice_al_out) self.xy_class.SetAnnotationLink(self.pc_al) self.xy_class.SetHighlightAnnotationLink(self.if_al_out) self.ice_class.SetGroupAnnotationLink(self.pc_al_out) self.ice_class.SetHighlightAnnotationLink(self.if_al_out) self.ice_class.SetScaleAnnotationLink(self.nf_al_out) # View #5 -- Axis Images (xy control) View # Set up all the render windows in the GUI self.ui.setupUi(self) self.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Multi-scale SVD :: Wavelets", None, QtGui.QApplication.UnicodeUTF8)) # Now need to get all the interactors working properly # Icicle self.ice_class.GetRenderWindow().SetInteractor(self.ui.qvtkWidget_0.GetInteractor()) self.ui.qvtkWidget_0.SetRenderWindow(self.ice_class.GetRenderWindow()) self.ice_class.SetInteractorStyleToImage() # self.ui.qvtkWidget_0.show() # PCoords self.pc_class.GetView().SetInteractor(self.ui.qvtkWidget_1.GetInteractor()) self.ui.qvtkWidget_1.SetRenderWindow(self.pc_class.GetView().GetRenderWindow()) # self.ui.qvtkWidget_1.show() # Image Flow self.if_class.GetRenderWindow().SetInteractor(self.ui.qvtkWidget_2.GetInteractor()) self.ui.qvtkWidget_2.SetRenderWindow(self.if_class.GetRenderWindow()) self.if_class.SetInteractorStyleToImage() # self.ui.qvtkWidget_2.show() # Detail Flow self.nf_class.GetRenderWindow().SetInteractor(self.ui.qvtkWidget_3.GetInteractor()) self.ui.qvtkWidget_3.SetRenderWindow(self.nf_class.GetRenderWindow()) self.nf_class.SetInteractorStyleToImage() # self.ui.qvtkWidget_3.show() # XYChart self.xy_class.GetChartView().SetInteractor(self.ui.qvtkWidget_4.GetInteractor()) self.ui.qvtkWidget_4.SetRenderWindow(self.xy_class.GetChartView().GetRenderWindow()) # self.ui.qvtkWidget_4.show() # AxisImages self.xy_class.GetAxisView().SetInteractor(self.ui.qvtkWidget_5.GetInteractor()) self.ui.qvtkWidget_5.SetRenderWindow(self.xy_class.GetAxisView().GetRenderWindow()) # self.ui.qvtkWidget_5.show() # Set sizes for veritcal splitters (left,right or top,bottom) self.ui.splitter_0.setSizes([280,220]) self.ui.splitter_1.setSizes([260,240]) self.ui.splitter_2.setSizes([130,370]) self.ui.splitter_3.setSizes([490,280]) self.ui.splitter_4.setSizes([330,770]) # Deal with color_by_array menu items self.color_array_actions_list = [] # Only want one array color to be set at a time self.colorActionGroup = QtGui.QActionGroup(self) # TODO: Switch this for different default self.ui.actionColorNone.setChecked(True) self.colorActionGroup.addAction(self.ui.actionColorNone) if self.ds.hasLabels: for ii,label in enumerate(self.ds.label_names): actionTmp = QtGui.QAction(self) actionTmp.setCheckable(True) actionTmp.setObjectName(_fromUtf8(label)) actionTmp.setText(QtGui.QApplication.translate("MainWindow", label, None, QtGui.QApplication.UnicodeUTF8)) self.color_array_actions_list.append(actionTmp) self.ui.menuPlot_Colors.addAction(self.color_array_actions_list[ii]) self.colorActionGroup.addAction(self.color_array_actions_list[ii]) QtCore.QObject.connect(self.color_array_actions_list[ii], QtCore.SIGNAL("triggered()"), self.setColorByArray) # Explicitly set default here self.ds.SetCoeffSource('wavelets') # self.ds.SetCoeffSource('scaling') # TODO: Need to call menu actions to set color and coeff defaults rather than # hard-coding them here... # Only want one type of coefficient to be set at a time basisActionGroup = QtGui.QActionGroup(self) self.ui.actionWavelet.setChecked(True) basisActionGroup.addAction(self.ui.actionWavelet) basisActionGroup.addAction(self.ui.actionScaling) # Only want one type of parallel coordinates scale range to be set at a time pcScaleRangeActionGroup = QtGui.QActionGroup(self) self.ui.actionPC_Coarsest_to_Current.setChecked(True) pcScaleRangeActionGroup.addAction(self.ui.actionPC_All_Scales) pcScaleRangeActionGroup.addAction(self.ui.actionPC_Current_Scale) pcScaleRangeActionGroup.addAction(self.ui.actionPC_Coarsest_to_Current) pcScaleRangeActionGroup.addAction(self.ui.actionPC_Current_to_Finest) # Connect signals and slots QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL("triggered()"), self.fileExit) QtCore.QObject.connect(self.ui.actionOpen, QtCore.SIGNAL("triggered()"), self.fileOpen) QtCore.QObject.connect(self.ui.actionWavelet, QtCore.SIGNAL("triggered()"), self.switchToWavelets) QtCore.QObject.connect(self.ui.actionScaling, QtCore.SIGNAL("triggered()"), self.switchToScaling) QtCore.QObject.connect(self.ui.actionPC_All_Scales, QtCore.SIGNAL("triggered()"), self.switchToPCAllScales) QtCore.QObject.connect(self.ui.actionPC_Current_Scale, QtCore.SIGNAL("triggered()"), self.switchToPCCurrentScale) QtCore.QObject.connect(self.ui.actionPC_Coarsest_to_Current, QtCore.SIGNAL("triggered()"), self.switchToPCCoarsestToCurrent) QtCore.QObject.connect(self.ui.actionPC_Current_to_Finest, QtCore.SIGNAL("triggered()"), self.switchToPCCurrentToFinest) QtCore.QObject.connect(self.ui.actionColorNone, QtCore.SIGNAL("triggered()"), self.setColorByArray) # NOTE: These are broken for now with QVTKWidget... # Trying to see whether I can pass selection bounds from xy chart to pcoords # self.xy_class.GetChartView().GetInteractor().AddObserver("LeftButtonReleaseEvent", self.XYSelectionReleaseCallback) # Trying to see whether I can pass selection bounds from xy chart to pcoords # self.xy_class.GetChartView().GetInteractor().AddObserver("LeftButtonPressEvent", self.XYSelectionPressCallback) # Testing out events for axis image modification so can have callback here self.xy_class.GetAxisImageItem().AddObserver("PropertyModifiedEvent", self.AIxyChangedCallback)
class MultiScaleSVDViews(QtGui.QMainWindow): def __init__(self, parent = None): QtGui.QMainWindow.__init__(self, parent) self.ui = Ui_MainWindow() if os.path.exists('/Users/emonson/Data/Fodava/EMoGWDataSets'): self.openFilesDefaultPath = '/Users/emonson/Data/Fodava/EMoGWDataSets' else: self.openFilesDefaultPath = QtCore.QDir.homePath() data_file = QtGui.QFileDialog.getOpenFileName(self, "Load Saved Matlab File", self.openFilesDefaultPath, "All Files (*);;Matlab Files (*.mat)") # DataSource loads .mat file and can generate data from it for other views print "Loading data from ", str(data_file) self.ds = DataSource(str(data_file)) self.last_data_dir = os.path.dirname(str(data_file)) # All view classes have access to an instance of that data source for internal queries # Note that the only view which will pull and display data right away is the icicle view # the other views need to be able to initialize without any data and only pull and show # upon the first AnnotationChanged event... # View #0 -- Icicle View self.ice_class = IcicleNoView(self.ds) self.ice_class.GetRenderWindow().SetPosition(50,500) self.ice_class.GetRenderWindow().SetSize(630,470) self.ice_al_out = self.ice_class.GetOutputAnnotationLink() # Note: With the way I've implemented the output annotation link in PCoords chart, # it will always have a selection node, but the selection list may have no tuples if # it's an empty selection (and event gets fired on every empty selection # View #1 -- PCoords View self.pc_class = PCoordsChart(self.ds) self.pc_class.SetInputAnnotationLink(self.ice_al_out) self.pc_al_out = self.pc_class.GetOutputAnnotationLink() self.pc_al = self.pc_class.GetAnnotationLink() # View #2 -- Image Flow View self.if_class = ImageFlow(self.ds, self.pc_al_out) self.if_al_out = self.if_class.GetOutputAnnotationLink() self.pc_class.SetHighlightAnnotationLink(self.if_al_out) # View #3 -- Detail View self.nf_class = DetailImageFlow(self.ds, self.if_al_out) self.nf_class.SetFlowDirection(Direction.Vertical) self.nf_al_out = self.nf_class.GetOutputAnnotationLink() # View #4 -- XY Chart View self.xy_class = XYChart(self.ds) self.xy_class.SetInputAnnotationLink(self.ice_al_out) self.xy_class.SetAnnotationLink(self.pc_al) self.xy_class.SetHighlightAnnotationLink(self.if_al_out) self.ice_class.SetGroupAnnotationLink(self.pc_al_out) self.ice_class.SetHighlightAnnotationLink(self.if_al_out) self.ice_class.SetScaleAnnotationLink(self.nf_al_out) # View #5 -- Axis Images (xy control) View # Set up all the render windows in the GUI self.ui.setupUi(self) self.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Multi-scale SVD :: Wavelets", None, QtGui.QApplication.UnicodeUTF8)) # Now need to get all the interactors working properly # Icicle self.ice_class.GetRenderWindow().SetInteractor(self.ui.qvtkWidget_0.GetInteractor()) self.ui.qvtkWidget_0.SetRenderWindow(self.ice_class.GetRenderWindow()) self.ice_class.SetInteractorStyleToImage() # self.ui.qvtkWidget_0.show() # PCoords self.pc_class.GetView().SetInteractor(self.ui.qvtkWidget_1.GetInteractor()) self.ui.qvtkWidget_1.SetRenderWindow(self.pc_class.GetView().GetRenderWindow()) # self.ui.qvtkWidget_1.show() # Image Flow self.if_class.GetRenderWindow().SetInteractor(self.ui.qvtkWidget_2.GetInteractor()) self.ui.qvtkWidget_2.SetRenderWindow(self.if_class.GetRenderWindow()) self.if_class.SetInteractorStyleToImage() # self.ui.qvtkWidget_2.show() # Detail Flow self.nf_class.GetRenderWindow().SetInteractor(self.ui.qvtkWidget_3.GetInteractor()) self.ui.qvtkWidget_3.SetRenderWindow(self.nf_class.GetRenderWindow()) self.nf_class.SetInteractorStyleToImage() # self.ui.qvtkWidget_3.show() # XYChart self.xy_class.GetChartView().SetInteractor(self.ui.qvtkWidget_4.GetInteractor()) self.ui.qvtkWidget_4.SetRenderWindow(self.xy_class.GetChartView().GetRenderWindow()) # self.ui.qvtkWidget_4.show() # AxisImages self.xy_class.GetAxisView().SetInteractor(self.ui.qvtkWidget_5.GetInteractor()) self.ui.qvtkWidget_5.SetRenderWindow(self.xy_class.GetAxisView().GetRenderWindow()) # self.ui.qvtkWidget_5.show() # Set sizes for veritcal splitters (left,right or top,bottom) self.ui.splitter_0.setSizes([280,220]) self.ui.splitter_1.setSizes([260,240]) self.ui.splitter_2.setSizes([130,370]) self.ui.splitter_3.setSizes([490,280]) self.ui.splitter_4.setSizes([330,770]) # Deal with color_by_array menu items self.color_array_actions_list = [] # Only want one array color to be set at a time self.colorActionGroup = QtGui.QActionGroup(self) # TODO: Switch this for different default self.ui.actionColorNone.setChecked(True) self.colorActionGroup.addAction(self.ui.actionColorNone) if self.ds.hasLabels: for ii,label in enumerate(self.ds.label_names): actionTmp = QtGui.QAction(self) actionTmp.setCheckable(True) actionTmp.setObjectName(_fromUtf8(label)) actionTmp.setText(QtGui.QApplication.translate("MainWindow", label, None, QtGui.QApplication.UnicodeUTF8)) self.color_array_actions_list.append(actionTmp) self.ui.menuPlot_Colors.addAction(self.color_array_actions_list[ii]) self.colorActionGroup.addAction(self.color_array_actions_list[ii]) QtCore.QObject.connect(self.color_array_actions_list[ii], QtCore.SIGNAL("triggered()"), self.setColorByArray) # Explicitly set default here self.ds.SetCoeffSource('wavelets') # self.ds.SetCoeffSource('scaling') # TODO: Need to call menu actions to set color and coeff defaults rather than # hard-coding them here... # Only want one type of coefficient to be set at a time basisActionGroup = QtGui.QActionGroup(self) self.ui.actionWavelet.setChecked(True) basisActionGroup.addAction(self.ui.actionWavelet) basisActionGroup.addAction(self.ui.actionScaling) # Only want one type of parallel coordinates scale range to be set at a time pcScaleRangeActionGroup = QtGui.QActionGroup(self) self.ui.actionPC_Coarsest_to_Current.setChecked(True) pcScaleRangeActionGroup.addAction(self.ui.actionPC_All_Scales) pcScaleRangeActionGroup.addAction(self.ui.actionPC_Current_Scale) pcScaleRangeActionGroup.addAction(self.ui.actionPC_Coarsest_to_Current) pcScaleRangeActionGroup.addAction(self.ui.actionPC_Current_to_Finest) # Connect signals and slots QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL("triggered()"), self.fileExit) QtCore.QObject.connect(self.ui.actionOpen, QtCore.SIGNAL("triggered()"), self.fileOpen) QtCore.QObject.connect(self.ui.actionWavelet, QtCore.SIGNAL("triggered()"), self.switchToWavelets) QtCore.QObject.connect(self.ui.actionScaling, QtCore.SIGNAL("triggered()"), self.switchToScaling) QtCore.QObject.connect(self.ui.actionPC_All_Scales, QtCore.SIGNAL("triggered()"), self.switchToPCAllScales) QtCore.QObject.connect(self.ui.actionPC_Current_Scale, QtCore.SIGNAL("triggered()"), self.switchToPCCurrentScale) QtCore.QObject.connect(self.ui.actionPC_Coarsest_to_Current, QtCore.SIGNAL("triggered()"), self.switchToPCCoarsestToCurrent) QtCore.QObject.connect(self.ui.actionPC_Current_to_Finest, QtCore.SIGNAL("triggered()"), self.switchToPCCurrentToFinest) QtCore.QObject.connect(self.ui.actionColorNone, QtCore.SIGNAL("triggered()"), self.setColorByArray) # NOTE: These are broken for now with QVTKWidget... # Trying to see whether I can pass selection bounds from xy chart to pcoords # self.xy_class.GetChartView().GetInteractor().AddObserver("LeftButtonReleaseEvent", self.XYSelectionReleaseCallback) # Trying to see whether I can pass selection bounds from xy chart to pcoords # self.xy_class.GetChartView().GetInteractor().AddObserver("LeftButtonPressEvent", self.XYSelectionPressCallback) # Testing out events for axis image modification so can have callback here self.xy_class.GetAxisImageItem().AddObserver("PropertyModifiedEvent", self.AIxyChangedCallback) # Only need to Start() interactor for one view # self.pc_class.GetView().GetInteractor().Start() # Shouldn't have to do this render... # for rw in self.renWinList: # rw.Render() def XYSelectionReleaseCallback(self, caller, event): x0,y0 = caller.GetEventPosition() print "Release (", x0, y0, ")" # Using this callback to get rid of parallel coordinates selection bars # if a selection has been made in the XY chart... self.pc_class.GetChart().ClearAxesSelections() self.pc_class.GetView().Render() def XYSelectionPressCallback(self, caller, event): x0,y0 = caller.GetEventPosition() print "Press (", x0, y0, ")" def IcicleSelectionCallback(self, caller, event): annSel = caller.GetCurrentSelection() if annSel.GetNumberOfNodes() > 0: idxArr = annSel.GetNode(0).GetSelectionList() if idxArr.GetNumberOfTuples() > 0: print "if_out ", VN.vtk_to_numpy(idxArr) else: print "if back to main with selection node but no tuples" else: print "if back to main with no selection node" def AIxyChangedCallback(self, caller, event): xI = self.xy_class.GetAxisImageItem().GetXAxisIndex() yI = self.xy_class.GetAxisImageItem().GetYAxisIndex() # print "AI CALLBACK: (%d, %d)" % (xI,yI) self.xy_class.GetChartXY().SetPlotColumnIndices(xI,yI) self.xy_class.GetChartView().Render() self.pc_class.SetCurrentXY(xI,yI) self.pc_class.GetView().Render() # - - - - - - - - - - - - - - - - - - - - - - def switchToWavelets(self): if self.ds.GetCoeffSource().lower().startswith('sca'): self.ds.SetCoeffSource('wavelet') # Force other classes to reload their image and table data from new source # while keeping all selections... self.ice_class.ReloadTextureImages() # self.nf_class.ReloadBasisImages() # Instead of reloading basis images in detail view, try just firing event from image flow... self.if_al_out.InvokeEvent("AnnotationChangedEvent") # Axis images seems to reset properly on switchover, so follow that... xI = self.xy_class.GetAxisImageItem().GetXAxisIndex() yI = self.xy_class.GetAxisImageItem().GetYAxisIndex() # self.xy_class.GetChartXY().SetPlotColumnIndices(xI,yI) # self.xy_class.GetChartView().Render() self.pc_class.SetCurrentXY(xI,yI) self.pc_class.GetView().Render() self.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Multi-scale SVD :: Wavelets", None, QtGui.QApplication.UnicodeUTF8)) def switchToScaling(self): if self.ds.GetCoeffSource().lower().startswith('wav'): self.ds.SetCoeffSource('scaling') # Force other classes to reload their image and table data from new source # while keeping all selections... self.ice_class.ReloadTextureImages() # self.nf_class.ReloadBasisImages() # Instead of reloading basis images in detail view, try just firing event from image flow... self.if_al_out.InvokeEvent("AnnotationChangedEvent") # Axis images seems to reset properly on switchover, so follow that... xI = self.xy_class.GetAxisImageItem().GetXAxisIndex() yI = self.xy_class.GetAxisImageItem().GetYAxisIndex() # self.xy_class.GetChartXY().SetPlotColumnIndices(xI,yI) # self.xy_class.GetChartView().Render() self.pc_class.SetCurrentXY(xI,yI) self.pc_class.GetView().Render() self.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Multi-scale SVD :: Scaling Functions", None, QtGui.QApplication.UnicodeUTF8)) def switchToPCAllScales(self): self.pc_class.SetScaleRange('all') self.pc_class.UpdateChartWithCurrentData() def switchToPCCurrentScale(self): self.pc_class.SetScaleRange('current') self.pc_class.UpdateChartWithCurrentData() def switchToPCCoarsestToCurrent(self): self.pc_class.SetScaleRange('coarse') self.pc_class.UpdateChartWithCurrentData() def switchToPCCurrentToFinest(self): self.pc_class.SetScaleRange('fine') self.pc_class.UpdateChartWithCurrentData() def setColorByArray(self): sender = self.sender() if sender in self.color_array_actions_list: self.pc_class.SetColorByArray(str(sender.text())) self.pc_class.GetView().Render() self.xy_class.SetColorByArray(str(sender.text())) self.xy_class.GetChartView().Render() self.ice_class.SetColorByArray(str(sender.text())) self.ice_class.ReloadTextureImages() else: self.pc_class.SetColorByArrayOff() self.pc_class.GetView().Render() self.xy_class.SetColorByArrayOff() self.xy_class.GetChartView().Render() self.ice_class.SetColorByArrayOff() self.ice_class.ReloadTextureImages() def generate_color_array_actions(self): self.color_array_actions_list = [] # Only want one array color to be set at a time self.colorActionGroup = QtGui.QActionGroup(self) # TODO: Switch this for different default self.ui.actionColorNone.setChecked(True) self.colorActionGroup.addAction(self.ui.actionColorNone) if self.ds.hasLabels: for ii,label in enumerate(self.ds.label_names): actionTmp = QtGui.QAction(self) actionTmp.setCheckable(True) actionTmp.setObjectName(_fromUtf8(label)) actionTmp.setText(QtGui.QApplication.translate("MainWindow", label, None, QtGui.QApplication.UnicodeUTF8)) self.color_array_actions_list.append(actionTmp) self.ui.menuPlot_Colors.addAction(self.color_array_actions_list[ii]) self.colorActionGroup.addAction(self.color_array_actions_list[ii]) QtCore.QObject.connect(self.color_array_actions_list[ii], QtCore.SIGNAL("triggered()"), self.setColorByArray) # - - - - - - - - - - - - - - - - - - - - - - def fileOpen(self): openFilesDefaultPath = '' # This dialog allows multiple files to be selected at once # If only want a single file, change to fileName = QtGui.QFileDialog.getOpenFileName(...) # Just change the string in the next to last line of the method for different file types file = QtGui.QFileDialog.getOpenFileName(self, "Load Saved Matlab File", self.last_data_dir, "All Files (*);;Matlab Files (*.mat)") if file: # Clear out selections empty_arr = N.array([],dtype='int64') empty_vtk = VN.numpy_to_vtkIdTypeArray(empty_arr, deep=True) self.pc_class.GetAnnotationLink().GetCurrentSelection().GetNode(0).SetSelectionList(empty_vtk) self.pc_class.GetAnnotationLink().InvokeEvent("AnnotationChangedEvent") self.if_class.GetOutputAnnotationLink().GetCurrentSelection().GetNode(0).SetSelectionList(empty_vtk) self.if_class.GetOutputAnnotationLink().InvokeEvent("AnnotationChangedEvent") self.nf_class.GetOutputAnnotationLink().GetCurrentSelection().GetNode(0).SetSelectionList(empty_vtk) self.nf_class.GetOutputAnnotationLink().InvokeEvent("AnnotationChangedEvent") self.ds.SetFileName(str(file)) self.ds.LoadData() self.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Multi-scale SVD :: Wavelets", None, QtGui.QApplication.UnicodeUTF8)) self.ui.actionWavelet.setChecked(True) # Remove old color_by_array menu items for action in self.color_array_actions_list: self.ui.menuPlot_Colors.removeAction(action) self.colorActionGroup.removeAction(action) QtCore.QObject.connect(action, QtCore.SIGNAL("triggered()"), self.setColorByArray) # Add new color_by_array menu items self.generate_color_array_actions() self.pc_class.SetColorByArrayOff() self.xy_class.SetColorByArrayOff() self.ice_class.SetColorByArrayOff() self.ice_class.LoadData() self.ui.qvtkWidget_0.update() self.ui.qvtkWidget_1.update() def fileExit(self): # Usually would use the qApp global variable qApp.quit(), but wasn't working... QtGui.QApplication.instance().quit()