Example #1
0
    def buildWidget(self):
        self._widget = QWidget()
        self._ui = Ui_ImportCSV()
        self._ui.setupUi(self._widget)

        # Connect button signals
        self._ui.csvFileNameButton.clicked.connect(self.csvFileSelector)
        self._ui.loadDataButton.clicked.connect(self.loadData)
        self._ui.importDataButton.clicked.connect(self.importData)
        self._ui.data.cellChanged.connect(self.handleDataChange)
Example #2
0
class ImportCSV(Module):
    """Module to import data as CSV (or TSV, etc)."""

    def __init__(self):
        Module.__init__(self)

    def buildWidget(self):
        self._widget = QWidget()
        self._ui = Ui_ImportCSV()
        self._ui.setupUi(self._widget)

        # Connect button signals
        self._ui.csvFileNameButton.clicked.connect(self.csvFileSelector)
        self._ui.loadDataButton.clicked.connect(self.loadData)
        self._ui.importDataButton.clicked.connect(self.importData)
        self._ui.data.cellChanged.connect(self.handleDataChange)

    def csvFileSelector(self):
        """Button-lineedit link"""
        directory = os.path.dirname(Util.getWidgetValue(self._ui.csvFileName))
        if not os.path.isdir(directory):
            directory = self._app.preferences.getInternal('projectDirectory')

        csvFile = str(QFileDialog.getOpenFileName(self._app.ui.workspace, "Select Data File", directory, "Comma Separated Values (*.csv);;All Files(*)"))

        if csvFile != "":
            return Util.setWidgetValue(self._ui.csvFileName, csvFile)
        return False

    def loadData(self):
        """Load data into the widget for viewing before importing into the application."""
        
        # Block the cellChanged signal, because all the cells are going to be changed and we
        # are dealing with that in this method.
        self._ui.data.blockSignals(True)

        dataTable = self._ui.data

        self.clearTable()

        # Get data from file
        csvFile = Util.getWidgetValue(self._ui.csvFileName)
        if os.path.isfile(csvFile):
            
            rows = []
            delimiterText = Util.getWidgetValue(self._ui.delimiterButtonGroup)
            if delimiterText == "Comma":
                rows = csv.reader(open(csvFile), dialect="excel", delimiter=",")
            elif delimiterText == "Tab":
                rows = csv.reader(open(csvFile), dialect="excel-tab")
            elif delimiterText == "Other":
                rows = csv.reader(open(csvFile), dialect="excel", delimiter=Util.getWidgetValue(self._ui.otherDelimiter))
            else:
                rows = csv.reader(open(csvFile), dialect="excel", delimiter=",")

            for rownum, row in enumerate(rows):
                # Make sure the cells exist to enter the data into
                dataTable.insertRow(rownum)
                if len(row) > dataTable.columnCount():
                    for i in range(dataTable.columnCount(), len(row)):
                        dataTable.insertColumn(dataTable.columnCount())
                # Add this row to the table
                for colnum, item in enumerate(row):
                    dataTable.setItem(rownum, colnum, QTableWidgetItem(item))
        
        # Hide the QT default column header, since we cannot edit it easily
        dataTable.horizontalHeader().hide()

        # Add a row for setting the data type of each wave
        dataTable.insertRow(0)
        defaultType = Util.getWidgetValue(self._ui.defaultDataType)
        for col in range(dataTable.columnCount()):
            typeBox = QComboBox()
            typeBox.addItem("Integer")
            typeBox.addItem("Decimal")
            typeBox.addItem("String")
            Util.setWidgetValue(typeBox, defaultType)
            dataTable.setCellWidget(0, col, typeBox)

        # Potential wave names
        waveNamePrefix = Util.getWidgetValue(self._ui.waveNamePrefix)
        tempWaveNames = []
        if Util.getWidgetValue(self._ui.useWaveNamePrefix):
            tempWaveNames = self._app.waves().findGoodWaveNames(dataTable.columnCount(), waveNamePrefix)
        else:
            tempWaveNames = self._app.waves().findGoodWaveNames(dataTable.columnCount())

        # Wave names can either be generated or taken from the first row in the file
        if not Util.getWidgetValue(self._ui.firstRowWaveNames):
            # Generate headers
            dataTable.insertRow(1)
            
            # PyQt does not have QList support yet, so this is a hack to get around that
            for col,name in enumerate(tempWaveNames):
                dataTable.setItem(1, col, QTableWidgetItem(name))
        else:
            # Use the first row of data, but check to see if there is text for each column
            # and if there is no text for a column, add in a tempWaveName entry
            for col in range(dataTable.columnCount()):
                if not dataTable.item(1, col) or str(dataTable.item(1, col).text()).strip() == "":
                    dataTable.setItem(1, col, QTableWidgetItem(tempWaveNames.pop(0)))
                else:
                    # For the names that came from the file, add the prefix specified by the user
                    if Util.getWidgetValue(self._ui.useWaveNamePrefix):
                        dataTable.item(1, col).setText(str(waveNamePrefix) + dataTable.item(1, col).text())


        # Edit the name so that it could be valid (no spaces, etc). But it might
        # still be a duplicate.
        self.validateWaveNames()

        # Adjust the row headers so that they number correctly with the wave names in the first row
        rowLabels = QStringList("Type")
        rowLabels.append("Name")
        for row in range(1, dataTable.rowCount()):
            rowLabels.append(str(row))
        dataTable.setVerticalHeaderLabels(rowLabels)

        # Verify that all wave names are acceptable for the app's waves object
        self.verifyGoodWaveNames()

        self._ui.data.blockSignals(False)

        # Resize rows and columns
        dataTable.resizeRowsToContents()
        dataTable.resizeColumnsToContents()

    def clearTable(self):
        dataTable = self._ui.data

        # Reset table to be empty
        for i in range(dataTable.rowCount()):
            dataTable.removeRow(0)
        for i in range(dataTable.columnCount()):
            dataTable.removeColumn(0)

    def validateWaveNames(self):
        """
        Run Wave.validateWaveName on all potential wave names.
        """

        dataTable = self._ui.data

        for col in range(dataTable.columnCount()):
            if dataTable.item(1, col):
                dataTable.setItem(1, col, QTableWidgetItem(Wave.validateWaveName(str(dataTable.item(1, col).text()))))

    def verifyGoodWaveNames(self):
        """
        Verify that all the wave names provided will be acceptable in the application's waves object.
        If there are any conflicts, then disable importing and mark the offending names.
        """
        
        dataTable = self._ui.data
        
        allNamesAreGood = True
        importWaveNames = []

        for col in range(dataTable.columnCount()):
            if dataTable.item(1, col):
                name = str(dataTable.item(1, col).text())
                if name in importWaveNames or not self._app.waves().goodWaveName(name):
                    # Bad name, highlight
                    dataTable.item(1, col).setBackground(QBrush(QColor('red')))
                    allNamesAreGood = False
                else:
                    # Good name
                    dataTable.item(1, col).setBackground(QBrush(QColor('lightgreen')))
                importWaveNames.append(name)

        # Disable the import button
        if allNamesAreGood:
            self._ui.importDataButton.setEnabled(True)
        else:
            self._ui.importDataButton.setEnabled(False)

        return allNamesAreGood

    def handleDataChange(self, row, column):
        """
        If data in a cell is changed, then this method will be called.

        Currently, if a name cell is edited, then we re-verify the wave names.
        """

        if row == 1:
            self.verifyGoodWaveNames()

    def importData(self):
        """Import data into the application as waves."""
        
        dataTable = self._ui.data
        
        # Loop through all waves
        for col in range(dataTable.columnCount()):
            dataType = Util.getWidgetValue(dataTable.cellWidget(0, col))
            wave = Wave(str(dataTable.item(1, col).text()), dataType)
            for row in range(2, dataTable.rowCount()):
                if dataTable.item(row, col):
                    wave.push(str(dataTable.item(row, col).text()))
                else:
                    wave.push('')
            self._app.waves().addWave(wave)

        # Close window
        self.clearTable()
        self.window.hide()


    def load(self):
        self.window = SubWindow(self._app.ui.workspace)

        self.menuEntry = QAction(self._app)
        self.menuEntry.setObjectName("actionImportCSV")
        self.menuEntry.setShortcut("Ctrl+I")
        self.menuEntry.setText("Import CSV Data")
        self.menuEntry.triggered.connect(self.window.show)
        
        # Check if menu already exists
        if "menuImport" not in vars(self._app.ui).keys():
            self._app.ui.menuImport = QMenu(self._app.ui.menuFile)
            self._app.ui.menuImport.setObjectName("menuImport")
            self._app.ui.menuImport.setTitle(QApplication.translate("MainWindow", "Import", None, QApplication.UnicodeUTF8))
            self._app.ui.menuFile.addAction(self._app.ui.menuImport.menuAction())
        
        self.menu = vars(self._app.ui)["menuImport"]
        self.menu.addAction(self.menuEntry)

        self.buildWidget()
        self.window.setWidget(self._widget)
        self._widget.setParent(self.window)

        self.window.hide()

    def unload(self):

        self._widget.deleteLater()
        self.window.deleteLater()
        self.menu.removeAction(self.menuEntry)

    def setDefaults(self):
        for child in self._widget.children():
            print child
            del child
        self._ui.setupUi(self._widget)