예제 #1
0
 def insertFname(self,
                 fname: str,
                 mockDialog: Optional[QDialog] = None) -> None:
     '''fname: file name to insert.
     mockDialog: not used by the application, used only for testing.'''
     sxtmp = SxFile()
     sxtmp.fromFile(fname)
     dialog = mockDialog or InsertDialog(
         self)  # trick to allow mocking the dialog
     dialog.setWindowFlags(dialog.windowFlags()
                           & ~Qt.WindowContextHelpButtonHint)
     pos = 0  # type: int
     if dialog.exec_() == QDialog.Accepted:
         if dialog.radioStart.isChecked():
             pos = DataTable.ISTART
         elif dialog.radioBeforeCurrent.isChecked():
             pos = DataTable.IBEFORESEL
         elif dialog.radioAfterCurrent.isChecked():
             pos = DataTable.IAFTERSEL
         else:
             pos = DataTable.IEND
         self.dataTable.insertItems(pos, sxtmp.sxItems)
         self.statusBar().showMessage("File %s successfully inserted !" %
                                      str(fname))
     else:
         self.statusBar().showMessage("Insertion aborted")
예제 #2
0
    def testDeleteFullSxFile(self):
        mys = '''S00A11223344556677889900
S10B00000001020304050607FF
S10B000808090A0B0C0D0E0FFF
S90300000F
'''
        sxf = SxFile()
        fstream = io.StringIO(mys)  # type: TextIO
        sxf.fromFileStream(fstream, 'stream')
        del sxf
예제 #3
0
    def testSxFileRepr(self):
        sxfile = SxFile()
        sxfile.sxItems = [
            SxItem('S1', '03', '0123', '010203', 'FF'),
            SxItem('S1', '03', '0126', '111213', 'FF'),
        ]
        self.assertEqual(str(sxfile), '''
S1030123010203FF
S1030126111213FF

''')
예제 #4
0
 def __init__(self,
              parent: Optional[QWidget] = None,
              name: Optional[str] = None) -> None:
     QTableWidget.__init__(self, 0, 0, parent)
     self.setObjectName(name)
     self.sxfile = SxFile()  # type: SxFile
     self.setFont(QFont("Courier new", 10))
     self.setSelectionMode(QAbstractItemView.ExtendedSelection)
     self.setSelectionBehavior(QAbstractItemView.SelectRows)
     self.initTable()
     self.file = ''  # type: str
     self.copy_list = []  # type: List[SxItem]
     self.setSizeAdjustPolicy(1)
예제 #5
0
    def testSxFileSplitItem(self):
        sxfile = SxFile()
        sxfile.sxItems = [
            SxItem('S1', '03', '0123', '010203', 'FF'),
            SxItem('S1', '03', '0123', '111213', 'FF'),
            SxItem('S1', '03', '012C', '212223', 'FF'),
        ]

        sxfile.splitItem(1, 2)

        self.assertEqual(sxfile.sxItems[0].data, '010203')
        self.assertEqual(sxfile.sxItems[1].data, '1112')
        self.assertEqual(sxfile.sxItems[2].data, '13')
        self.assertEqual(sxfile.sxItems[3].data, '212223')
예제 #6
0
    def testSxFileMergeItem(self):
        sxfile = SxFile()
        sxfile.sxItems = [
            SxItem('S1', '03', '0123', '010203', 'FF'),
            SxItem('S1', '03', '0126', '111213', 'FF'),
            SxItem('S1', '03', '0129', '212223', 'FF'),
            SxItem('S1', '03', '012C', '313233', 'FF'),
            SxItem('S1', '03', '012F', '414243', 'FF'),
        ]

        sxfile.mergeItem(1, 3)

        self.assertEqual(sxfile.sxItems[0].data, '010203')
        self.assertEqual(sxfile.sxItems[1].data, '111213212223313233')
        self.assertEqual(sxfile.sxItems[2].data, '414243')

        sxfile.sxItems = [
            SxItem('S1', '03', '0123', '010203', 'FF'),
            SxItem('S1', '03', '0126', '111213', 'FF'),
            SxItem('S1', '03', '0123', '212223', 'FF'),
            SxItem('S1', '03', '0126', '313233', 'FF'),
            SxItem('S1', '03', '0129', '414243', 'FF'),
        ]

        sxfile.mergeItem(1, 3)

        self.assertEqual(sxfile.sxItems[0].data, '010203')
        self.assertEqual(sxfile.sxItems[1].data, '111213')
        self.assertEqual(sxfile.sxItems[2].data, '212223313233')
        self.assertEqual(sxfile.sxItems[3].data, '414243')
예제 #7
0
 def testXorData(self):
     sxfile = SxFile()
     sxfile.sxItems = [
         SxItem('S1', '03', '0123', '010203', 'FF'),
         SxItem('S1', '03', '0123', '111213', 'FF'),
         SxItem('S1', '03', '0126', '212223', 'FF'),
         SxItem('S1', '03', '0129', '313233', 'FF'),
         SxItem('S1', '03', '012C', '414243', 'FF'),
     ]
     for item in sxfile.sxItems:
         item.xorData('15AF')
     self.assertEqual(sxfile.sxItems[0].data, '14AD03')
     self.assertEqual(sxfile.sxItems[1].data, '04BD13')
     self.assertEqual(sxfile.sxItems[2].data, '348D23')
     self.assertEqual(sxfile.sxItems[3].data, '249D33')
     self.assertEqual(sxfile.sxItems[4].data, '54ED43')
예제 #8
0
 def testFlipBits(self):
     sxfile = SxFile()
     sxfile.sxItems = [
         SxItem('S1', '03', '0123', '010203', 'FF'),
         SxItem('S1', '03', '0123', '111213', 'FF'),
         SxItem('S1', '03', '0126', '212223', 'FF'),
         SxItem('S1', '03', '0129', '313233', 'FF'),
         SxItem('S1', '03', '012C', '414243', 'FF'),
     ]
     for item in sxfile.sxItems:
         item.flipBits()
     self.assertEqual(sxfile.sxItems[0].data, 'FEFDFC')
     self.assertEqual(sxfile.sxItems[1].data, 'EEEDEC')
     self.assertEqual(sxfile.sxItems[2].data, 'DEDDDC')
     self.assertEqual(sxfile.sxItems[3].data, 'CECDCC')
     self.assertEqual(sxfile.sxItems[4].data, 'BEBDBC')
예제 #9
0
 def testApplyOffset(self):
     sxfile = SxFile()
     sxfile.sxItems = [
         SxItem('S1', '03', '0123', '010203', 'FF'),
         SxItem('S1', '03', '0123', '111213', 'FF'),
         SxItem('S1', '03', '0126', '212223', 'FF'),
         SxItem('S1', '03', '0129', '313233', 'FF'),
         SxItem('S1', '03', '012C', '414243', 'FF'),
     ]
     i = 1
     for item in sxfile.sxItems:
         item.applyOffset(i)
         i += 5
     self.assertEqual(sxfile.sxItems[0].address, '0124')
     self.assertEqual(sxfile.sxItems[1].address, '0129')
     self.assertEqual(sxfile.sxItems[2].address, '0131')
     self.assertEqual(sxfile.sxItems[3].address, '0139')
     self.assertEqual(sxfile.sxItems[4].address, '0141')
예제 #10
0
    def testApplyNewRowSize(self):
        sxfile = SxFile()
        sxfile.sxItems = [
            SxItem('S1', '03', '0123', '010203', 'FF'),
            SxItem('S1', '03', '0123', '111213', 'FF'),
            SxItem('S1', '03', '0126', '212223', 'FF'),
            SxItem('S1', '03', '0129', '313233', 'FF'),
            SxItem('S1', '03', '012C', '414243', 'FF'),
        ]

        sxfile.applyNewRowSize(2, 1, 3)

        self.assertEqual(sxfile.sxItems[0].data, '010203')
        self.assertEqual(sxfile.sxItems[1].data, '1112')
        self.assertEqual(sxfile.sxItems[2].data, '1321')
        self.assertEqual(sxfile.sxItems[3].data, '2223')
        self.assertEqual(sxfile.sxItems[4].data, '3132')
        self.assertEqual(sxfile.sxItems[5].data, '33')
예제 #11
0
파일: sxtool.py 프로젝트: bluebird75/sxtool
def apply(files: List[str],
          operation: str,
          args: Optional[List[str]] = None,
          verbose: bool = False) -> None:
    '''Apply the given operation on all files.'''
    if not operation: return
    for file in files:  # type: str
        try:
            if verbose: print("Opening file " + file)
            sxfile = SxFile()  # type: SxFile
            sxfile.fromFile(file)
        except IOError as e:
            print("Error: couldn't open file: " + file)
            continue
        if verbose: print("Applying operation \"" + operation + "\" on file:")
        for item in sxfile.sxItems:
            method = getattr(item, operation,
                             None)  # type: Optional[Callable[..., None ]]
            if method:
                if verbose: print(str(item))
                if args:
                    method(args)
                else:
                    method()
                if verbose: print(" => " + str(item))
        # noinspection PyBroadException
        try:
            if verbose: print("Closing file " + file)
            sxfile.toFile(file)
        except Exception as e:
            print("Error: couldn't save file: " + file)
예제 #12
0
 def testGetFormat(self):
     sxFile = SxFile()
     self.assertEqual(sxFile.getFormat(), '')
     fnamesAndFmt = [(SX_EXAMPLE1, 's19'), (SX_EXAMPLE2, 's19'),
                     (SX_EXAMPLE3, 's28'), (SX_EXAMPLE4, 's37'),
                     (SX_EXAMPLE5, 's19')]
     for fname, fmt in fnamesAndFmt:
         with self.subTest(fname):
             sxFile.fromFile(fname)
             self.assertEqual(sxFile.getFormat(), fmt)
예제 #13
0
 def testSxFileLoadSave(self):
     sxFile = SxFile()
     fnames = ALL_EXAMPLES
     for fname in fnames:
         sxFile.fromFile(str(fname))
         with open(fname) as f:
             refOut = f.read().strip()
         strOut = io.StringIO()
         sxFile.toFileStream(strOut)
         self.assertEqual(strOut.getvalue().strip(), refOut)
         strOut.close()
예제 #14
0
    def testUpdateDataRange(self):
        sxfile = SxFile()
        sin = '\n'.join([
            'S004000088FF', 'S1'
            '06'
            '0123'
            '010203'
            'FF', 'S1'
            '06'
            '0126'
            '111213'
            'FF', 'S1'
            '06'
            '0129'
            '212223'
            'FF', 'S1'
            '06'
            '012C'
            '313233'
            'FF', 'S1'
            '06'
            '012F'
            '414243'
            'FF'
        ])
        sxfile.fromFileStream(io.StringIO(sin), 'stream')

        self.assertEqual(
            str(sxfile), '''S004000088FF
S1060123010203FF
S1060126111213FF
S1060129212223FF
S106012C313233FF
S106012F414243FF
''')

        sxfile.updateDataRange('AABB', (1, 4))
        self.assertEqual(
            str(sxfile), '''S004000088FF
S1060123010203FF
S1050126AABB6E
S1050129AABB6B
S105012CAABB68
S106012F414243FF
''')
예제 #15
0
    def testConvertRange(self):
        sxfile = SxFile()
        sin = '\n'.join([
            'S004000088FF', 'S1'
            '06'
            '0123'
            '010203'
            'FF', 'S1'
            '06'
            '0126'
            '111213'
            'FF', 'S1'
            '06'
            '0129'
            '212223'
            'FF', 'S1'
            '06'
            '012C'
            '313233'
            'FF', 'S1'
            '06'
            '012F'
            '414243'
            'FF'
        ])
        sxfile.fromFileStream(io.StringIO(sin), 'stream')
        self.assertEqual(
            str(sxfile), '''S004000088FF
S1060123010203FF
S1060126111213FF
S1060129212223FF
S106012C313233FF
S106012F414243FF
''')

        sxfile.convertRange('S28', (1, 4))
        self.assertEqual(
            str(sxfile), '''S004000088FF
S1060123010203FF
S2070001261112139B
S20700012921222368
S20700012C31323335
S106012F414243FF
''')
예제 #16
0
 def testDeleteEmptySxFile(self):
     sxf = SxFile()
     del sxf
예제 #17
0
class DataTable(QTableWidget
                ):  # type: ignore # PyQt and Mypy don't mix very well
    ISTART = 0  # type: int
    IBEFORESEL = 1  # type: int
    IAFTERSEL = 2  # type: int
    IEND = 3  # type: int
    PADD = 0  # type: int
    PADD_BEFORE = 1  # type: int
    PADD_AFTER = 2  # type: int
    PREPLACE = 3  # type: int
    PREPLACE_SELONLY = 4  # type: int
    PREPLACE_SELADD = 5  # type: int
    PREPLACE_SELREPLACE = 6  # type: int

    sigDataModifiedChanged = pyqtSignal(bool)

    def __init__(self,
                 parent: Optional[QWidget] = None,
                 name: Optional[str] = None) -> None:
        QTableWidget.__init__(self, 0, 0, parent)
        self.setObjectName(name)
        self.sxfile = SxFile()  # type: SxFile
        self.setFont(QFont("Courier new", 10))
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.initTable()
        self.file = ''  # type: str
        self.copy_list = []  # type: List[SxItem]
        self.setSizeAdjustPolicy(1)

    def initTable(self) -> None:
        self.setHorizontalHeaderLabels(
            ["Format", "Size", "Address", "Data", "Checksum"])
        # self.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        # self.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.verticalHeader().hide()
        self.setColumnWidth(3, 300)
        self.horizontalHeader().setStretchLastSection(1)
        # self.horizontalHeader().setSectionResizeMode(3)
        for i in range(5):
            self.horizontalHeader().resizeSection(
                i,
                int(
                    max(self.sizeHintForColumn(i),
                        self.horizontalHeader().sectionSizeHint(i)) * 1.1))

    def loadFile(self, fname: str) -> bool:
        self.file = fname
        self.copy_list = []
        try:
            self.sxfile.fromFile(self.file)
        except Exception as e:
            QMessageBox.critical(
                None, "Error",
                "Could not open file %s\n%s" % (self.file, repr(e)))
            return False
        self.redisplayTable()
        self.sigDataModifiedChanged.emit(False)
        return True

    def clear(self) -> None:
        for i in range(0, self.columnCount()):  # type: int
            self.removeColumn(i)

    def redisplayTable(self) -> None:
        self.clear()
        self.clearSelection()
        self.setRowCount(len(self.sxfile.sxItemsEx))
        self.setColumnCount(5)

        x = 0
        for l in self.sxfile.sxItemsEx:
            self.setText(x, 0, l.format)
            self.setText(x, 1, l.data_quantity)
            self.setText(x, 2, l.address)
            self.setText(x, 3, l.data)
            self.setText(x, 4, l.checksum)
            x += 1
        self.initTable()
        self.show()

    def setText(self, row: int, col: int, text: str) -> None:
        self.takeItem(row, col)
        item = DataItem(self, text, (col == 3))
        self.setItem(row, col, item)
        self.sigDataModifiedChanged.emit(True)

    def rowSelectedList(self) -> List[int]:
        """Return a list of all the row that are selected"""
        rows = []  # type: List[int]
        indexes = self.selectionModel().selectedRows()
        for index in indexes:
            rows.append(index.row())
        rows.sort()
        return rows

    def rowSelectedListWithoutFirstAndLast(self) -> List[int]:
        """Return a list of all the row that are selected"""
        rows = self.rowSelectedList()  # type: List[int]
        rows = [x for x in rows if x not in [0, self.rowCount() - 1]]
        return rows

    def isSelectionContinuus(self) -> bool:
        """Return True if the selection is continuus, False
        if there is no selection or if there are holes in the
        selection"""
        nbSwitch = 0  # type: int
        currentState = self.isRowSelected(0)  # type: bool
        for row in range(1, self.rowCount()):  # type: int
            if self.isRowSelected(row) != currentState:
                currentState = self.isRowSelected(row)
                nbSwitch += 1
        return (nbSwitch == 2)

    def numRowsSelected(self) -> int:
        """Return the number of selected rows"""
        return len(self.selectionModel().selectedRows())

    def updateRow(self, row: int) -> None:
        '''Refresh the display for the given row, reading the data again from object sxfile'''
        if row < 0 or row > self.rowCount():
            return
        self.setText(row, 0, self.sxfile.sxItemsEx[row].format)
        self.setText(row, 1, self.sxfile.sxItemsEx[row].data_quantity)
        self.setText(row, 2, self.sxfile.sxItemsEx[row].address)
        self.setText(row, 3, self.sxfile.sxItemsEx[row].data)
        self.setText(row, 4, self.sxfile.sxItemsEx[row].checksum)
        self.sigDataModifiedChanged.emit(True)

    def insertItems(self, pos: int, items: List[SxItem]) -> None:
        """ Insert a list of /items/ in the table"""
        insert_start = 0
        if pos == DataTable.ISTART:
            insert_start = 0
        elif pos == DataTable.IBEFORESEL:
            insert_start = min(self.rowSelectedList()) - 1
            if insert_start < 0:
                insert_start = 0
        elif pos == DataTable.IAFTERSEL:
            insert_start = max(self.rowSelectedList())
            if insert_start == self.rowCount() - 1:
                insert_start -= 1
        elif pos == DataTable.IEND:
            insert_start = self.rowCount() - 2
        else:
            QMessageBox.critical(None, "Error !",
                                 "Bad argument ! Operation aborted.")
            self.statusBar().showMessage("Insertion aborted")
            return
        items.reverse()
        for item in items:
            self.sxfile.sxItems.insert(insert_start, item)
        self.insertRows(insert_start + 1, len(items))
        for i in range(insert_start + 1, insert_start + len(items) + 1, 1):
            self.updateRow(i)
        self.sxfile.syncEx()

    def copyLines(self) -> int:
        """Copy the items corresponding to selection in a copy_list"""
        res = 0  # type: int
        self.copy_list = []
        for i in range(self.rowCount()):
            if self.isRowSelected(i):
                res += 1
                self.copy_list.append(self.sxfile.sxItemsEx[i])
        assert res == self.numRowsSelected()
        return res

    def convertTo(self, format: str) -> int:
        """Convert selected rows to a given format: S19, S28 or S37"""
        res = 0  # type: int
        for x in range(self.rowCount()):  # type: int
            if self.isRowSelected(x):
                res += 1
                self.sxfile.sxItemsEx[x].convert(format)
                self.updateRow(x)
        self.sxfile.syncFromEx()
        assert res == self.numRowsSelected()
        return res

    def editRow(self, row: int, data: str) -> None:
        """ Update the content of an item corresponding to 'row' with given data"""
        self.sxfile.sxItemsEx[row].updateData(data.upper())
        self.updateRow(row)
        self.sxfile.syncFromEx()

    def editSelection(self, data: str) -> None:
        """Edit every row selected with data"""
        for x in range(0, self.rowCount()):  # type:int
            if self.isRowSelected(x):
                self.sxfile.sxItemsEx[x].updateData(data.upper())
                self.updateRow(x)
        self.sxfile.syncFromEx()

    def deleteSelection(self) -> int:
        """Deleted the selected rows excluding first and last rows"""
        l = self.rowSelectedListWithoutFirstAndLast()  # Type l: List[int]
        if not l: return 0
        l.sort()
        self.clearSelection()
        self.removeRows(l)
        for x in l:  # type: int
            del self.sxfile.sxItemsEx[x]
        self.sxfile.syncFromEx()
        return len(l)

    def pasteLines(self, pasting_mode: int, precise_mode: int) -> int:
        """Paste lines according to some options"""
        pos = 0  # type: int
        if pasting_mode == DataTable.PADD:
            if precise_mode == DataTable.PADD_BEFORE:
                pos = DataTable.IBEFORESEL
            else:
                pos = DataTable.IAFTERSEL
            self.insertItems(pos, self.copy_list)
            self.sxfile.syncEx()
            return len(self.copy_list)
        else:
            x = 0  # type: int
            last = 1  # type: int
            for i in range(1, self.rowCount() - 1, 1):
                if x >= len(self.copy_list):
                    return x
                if self.isRowSelected(i):
                    self.sxfile.sxItems[i - 1] = self.copy_list[x]
                    x += 1
                    self.updateRow(i)
                    last = i
            self.sxfile.syncEx()
            if x < len(self.copy_list):
                if not self.isRowSelected(0):
                    last += 1
                if precise_mode == DataTable.PREPLACE_SELONLY:
                    return x + 1
                elif precise_mode == DataTable.PREPLACE_SELREPLACE:
                    for i in range(last, self.rowCount() - 1, 1):
                        if x >= len(self.copy_list):
                            return x
                        self.sxfile.sxItems[i - 1] = self.copy_list[x]
                        self.updateRow(i)
                        x += 1
                    self.sxfile.syncEx()
                    return (x + 1)
                else:
                    self.clearSelection()
                    self.selectRow(last)
                    self.insertItems(DataTable.IAFTERSEL, self.copy_list[x:])
                    self.sxfile.syncEx()
                    return len(self.copy_list)
            self.sxfile.syncEx()
            return x

    def insertRowsWithData(self, dialog: FormInsertRowValue) -> None:
        """Called after showing the "Insert Rows" dialog. Insert the actual
        data in the view and in the sxfile."""
        start = min(self.rowSelectedList())  # int
        if start == 0:
            start = 1
        self.insertRows(start, dialog.spinNbLines.value())
        format = str(dialog.comboBoxFormat.currentText())[:2]  # type: str
        dataLen = dialog.spinRowSize.value()
        data = ('00' * dataLen +
                dialog.lineEditData.text().upper())[-dataLen * 2:]  # type: str
        if dialog.radioPrevContinuity.isChecked():
            if start < 2:
                # no previous address, start at 0
                addr_start = 0
            else:
                addr_start = self.sxfile.sxItems[start - 2].addressEndValue()
        elif dialog.radioNextContinuity.isChecked():
            if start == self.rowCount() - dialog.spinNbLines.value() - 1:
                # no next line, just use 0
                addr_start = 0
            else:
                addr_start = self.sxfile.sxItems[start - 1].addressValue(
                ) - dialog.spinNbLines.value() * dataLen
        elif dialog.radioExplicitAddr.isChecked():
            addr_start = str2hexi(dialog.lineAddrStart.text())
        else:
            raise ValueError('No address strategy selected')

        for i in range(start, start + dialog.spinNbLines.value(), 1):
            sx = SxItem(format, "00", SxItem.formatAddress(addr_start, format),
                        "00", "00")  # type: SxItem
            sx.updateData(data)
            self.sxfile.sxItems.insert(i - 1, sx)
            self.updateRow(i)
            addr_start += dataLen
        self.sxfile.syncEx()

    def applyOffsetOnAddresses(self, offset: Union[str, int]) -> None:
        """Apply an offset on address of every selected row"""
        rows = self.rowSelectedListWithoutFirstAndLast()  # type: List[int]
        for i in rows:
            self.sxfile.sxItemsEx[i].applyOffset(offset)
            self.updateRow(i)
        self.sxfile.syncFromEx()

    def applyNewRowSize(self, newRowSize: int, selectedRowStart: int,
                        selectedRowEnd: int) -> None:
        """Adjust the size of the rows is the range (selectedRowStart,
        selectedRowEnd) with selectedRowEnd included"""
        self.sxfile.applyNewRowSize(newRowSize, selectedRowStart - 1,
                                    selectedRowEnd - 1)
        self.sxfile.syncEx()
        self.redisplayTable()
        self.sigDataModifiedChanged.emit(True)

    def splitRow(self, rowList: List[int], offset: int) -> None:
        """Split all the rows in rowList. The row must be given in ascending order"""
        rowOffset = 0  # type: int
        for row in rowList:
            # possible case:
            # - item is smaller or equal to offset asis
            #       => nothing to do
            # - item is splitted
            #       => a new item is inserted after the current one
            #       => skip this new item, it should not be splitted
            if self.sxfile.sxItems[row + rowOffset - 1].dataLen() > offset:
                self.sxfile.splitItem(row + rowOffset - 1, offset)
                rowOffset += 1
        self.sxfile.syncEx()
        self.redisplayTable()
        self.sigDataModifiedChanged.emit(True)

    def mergeRow(self, rowStart: int, rowEnd: int) -> None:
        """Merge the rows of the range (rowStart, rowEnd) (end included)
        together."""
        self.sxfile.mergeItem(rowStart - 1, rowEnd - 1)
        self.sxfile.syncEx()
        self.redisplayTable()
        self.sigDataModifiedChanged.emit(True)

    def isRowSelected(self, row: int) -> bool:
        indexes = self.selectionModel().selectedRows()
        for index in indexes:
            if row == index.row(): return True
        return False

    def text(self, row: int, col: int) -> str:
        item = self.item(row, col)
        s = str(item.text()) if item else ''
        return s

    def insertRows(self, pos: int, length: int) -> None:
        for i in range(pos, pos + length):
            self.insertRow(i)
        self.sigDataModifiedChanged.emit(True)

    def removeRows(self, rows: List[int]) -> None:
        i = 0
        for row in rows:
            self.removeRow(row - i)
            i += 1
        self.sxfile.syncEx()
        self.sigDataModifiedChanged.emit(True)

    def flipRows(self) -> None:
        rows = self.rowSelectedListWithoutFirstAndLast()  # type: List[int]
        for x in rows:
            self.sxfile.sxItems[x - 1].flipBits()
        self.sxfile.syncEx()
        self.redisplayTable()
        self.sigDataModifiedChanged.emit(True)

    def verifyChecksum(self,
                       rows: List[int]) -> List[Tuple[int, str, str, str]]:
        '''Verify the checksum of the rows number given in argument.

        Returns a list of tuple of: row number with a wrong checksum, address, correct checksum, invalid checksum.

        If all checksum are valid, returns an empty list.'''
        ret = []  # type: List[Tuple[int,str,str,str]]
        for r in rows:
            item = self.sxfile.sxItems[r - 1]
            if item.calcChecksum() != item.checksum:
                ret.append(
                    (r, item.address, item.calcChecksum(), item.checksum))
        return ret

    def updateSelectedChecksum(self) -> int:
        invalidChecksumRows = self.verifyChecksum(self.rowSelectedList())
        for rowNb, address, validChecksum, wrongChecksum in invalidChecksumRows:
            self.sxfile.sxItems[rowNb - 1].updateChecksum()
            self.updateRow(rowNb - 1)
        self.sxfile.syncEx()
        return len(invalidChecksumRows)