3

Is it possible to write the contents of a QTableWidget to a csv? I found a question about writing to an .xls using xlwt, but can't seem to get it to work using my code.

def saveFile(self):
    filename = unicode(QtGui.QFileDialog.getSaveFileName(self, 'Save File', '', ".xls(*.xls)"))    
    wbk = xlwt.Workbook()
    self.sheet = wbk.add_sheet("sheet")
    self.write()
    wbk.save(filename)    


def write(self):
    for col in range (self.coordinates.columnCount()):
        for row in range(self.coordinates.rowCount()):
            text=str(self.coordinates.item(row,col).text())
            self.sheet.write(row,col,text)

I get the following error:

  File "C:\Users\Tory\Desktop\DIDSON.py", line 186, in saveFile
    self.write()
  File "C:\Users\Tory\Desktop\DIDSON.py", line 192, in write
    text=str(self.coordinates.item(row,col).text())
AttributeError: 'NoneType' object has no attribute 'text'

Why is this? self.coordinates is a QTableWidget. I was able to get the items themselves to save successfully to a worksheet, although I'd still like to save as a .csv...

Victoria Price
  • 637
  • 3
  • 13
  • 26
  • Why are you setting and incrementing `row` and `col`? You can just use `i` and `x`, or am I missing something? It would be usefull for debugging if you don't do try/except, but just let the trace print and post it in the question. – BrtH Sep 26 '12 at 20:21
  • And further, why are you taking the data from the QTableWidget? If you have data to put in the table, why don't you write this raw data to a csv/xls? – BrtH Sep 26 '12 at 20:28
  • First comment-- not really sure, actually. Will revise. Second question, the table 'records' mouse click coordinates from an image, so I would like the user to be able to see the coordinates and then save a finished version. – Victoria Price Sep 26 '12 at 20:33

1 Answers1

12

To answer the question about the AttributeError: it probably happens because there are some empty rows or columns in the table.

An empty cell has no QTableWidgetItem assigned to it, so table.item() will return None (which obviously has no 'text' attribute).

For saving the table as csv, try the example below (which can also open csv files, and hopefully deals with any potential unicode issues):

import sys, csv
from PyQt4 import QtGui, QtCore

class Window(QtGui.QWidget):
    def __init__(self, rows, columns):
        QtGui.QWidget.__init__(self)
        self.table = QtGui.QTableWidget(rows, columns, self)
        for column in range(columns - 1):
            for row in range(rows - 1):
                item = QtGui.QTableWidgetItem('Text%d' % row)
                self.table.setItem(row, column, item)
        self.buttonOpen = QtGui.QPushButton('Open', self)
        self.buttonSave = QtGui.QPushButton('Save', self)
        self.buttonOpen.clicked.connect(self.handleOpen)
        self.buttonSave.clicked.connect(self.handleSave)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)
        layout.addWidget(self.buttonOpen)
        layout.addWidget(self.buttonSave)

    def handleSave(self):
        path = QtGui.QFileDialog.getSaveFileName(
                self, 'Save File', '', 'CSV(*.csv)')
        if not path.isEmpty():
            with open(unicode(path), 'wb') as stream:
                writer = csv.writer(stream)
                for row in range(self.table.rowCount()):
                    rowdata = []
                    for column in range(self.table.columnCount()):
                        item = self.table.item(row, column)
                        if item is not None:
                            rowdata.append(
                                unicode(item.text()).encode('utf8'))
                        else:
                            rowdata.append('')
                    writer.writerow(rowdata)

    def handleOpen(self):
        path = QtGui.QFileDialog.getOpenFileName(
                self, 'Open File', '', 'CSV(*.csv)')
        if not path.isEmpty():
            with open(unicode(path), 'rb') as stream:
                self.table.setRowCount(0)
                self.table.setColumnCount(0)
                for rowdata in csv.reader(stream):
                    row = self.table.rowCount()
                    self.table.insertRow(row)
                    self.table.setColumnCount(len(rowdata))
                    for column, data in enumerate(rowdata):
                        item = QtGui.QTableWidgetItem(data.decode('utf8'))
                        self.table.setItem(row, column, item)

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window(10, 5)
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • `open` can take unicode file names and do the right thing. You can drop the `.encode(sys.getfilesystemencoding())` parts. – Avaris Sep 27 '12 at 05:11
  • @Avaris. It *may* do the right thing, depending on the platform and the particular version/build of python in use - but it's not guaranteed. However, I agree that it can always be dropped for Windows (which I think the OP is using). – ekhumoro Sep 27 '12 at 15:39
  • It's not guaranteed? [Docs](http://docs.python.org/howto/unicode.html#unicode-filenames) say using a unicode filename is _at least_ the same as explicitly encoding it with `sys.getfilesystemencoding()`. – Avaris Sep 28 '12 at 01:05
  • @Avaris. Sorry, I missed that you were specifically referring to `open`. I meant that, in general, automatic conversion of unicode paths cannot be relied upon. But in any case, the only reason it was in my example is due to copy and paste from some existing code - so I'll just take it out to avoid any further confusion... – ekhumoro Sep 28 '12 at 03:39