From 0231e6e56b83d710d6db98534d324007216aade3 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Sun, 19 Jun 2016 01:10:03 +0200 Subject: [PATCH 1/5] Add a Table widget (when label is None and value ends with '.csv') --- examples/advanced.py | 1 + examples/table.csv | 2 ++ formlayout.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 examples/table.csv diff --git a/examples/advanced.py b/examples/advanced.py index 491bbb7..58e4f19 100644 --- a/examples/advanced.py +++ b/examples/advanced.py @@ -19,6 +19,7 @@ def create_datalist_example(): test = [('str *', 'this is a string'), + (None, 'table.csv'), ('str_m *', """this is a MULTILINE string"""), diff --git a/examples/table.csv b/examples/table.csv new file mode 100644 index 0000000..0dc421d --- /dev/null +++ b/examples/table.csv @@ -0,0 +1,2 @@ +first,second +Pièrre,Flôrent diff --git a/formlayout.py b/formlayout.py index 30391ab..031a909 100644 --- a/formlayout.py +++ b/formlayout.py @@ -361,6 +361,27 @@ def apply(self, callback): self.dialog.formwidget.get_widgets()) +class CSVTableModel(QAbstractTableModel): + def __init__(self, csvdata, parent=None): + QAbstractTableModel.__init__(self, parent) + if PY2: + self.data = [[el.decode('utf-8') for el in row] for row in csvdata] + else: + self.data = [row for row in csvdata] + + def rowCount(self, parent): + return len(self.data) + + def columnCount(self, parent): + return len(self.data[0]) + + def data(self, index, role): + if index.isValid() and role == Qt.DisplayRole: + return self.data[index.row()][index.column()] + else: + return None + + def font_is_installed(font): """Check if font is installed""" return [fam for fam in QFontDatabase().families() @@ -518,6 +539,18 @@ def setup(self): lab = QLabel() lab.setPixmap(pixmap) self.formlayout.addRow(lab) + elif value.endswith('.csv'): + # CSV file + import csv + if PY2: + csvfile = open(value, 'rb') + else: + csvfile = open(value, 'r') + csvdata = csv.reader(csvfile) + tablemodel = CSVTableModel(csvdata) + table = QTableView() + table.setModel(tablemodel) + self.formlayout.addRow(table) else: # Comment self.formlayout.addRow(QLabel(value)) From 52be1240686209c9e77b9add9f81de499ed93bf0 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Mon, 20 Jun 2016 00:48:31 +0200 Subject: [PATCH 2/5] Add headers option to Table widget (when value starts with '#', '_', '|' and '/') --- examples/advanced.py | 1 - examples/table.csv | 6 ++++-- examples/table.py | 25 +++++++++++++++++++++++++ examples/tablehv.csv | 4 ++++ examples/tablev.csv | 3 +++ formlayout.py | 27 +++++++++++++++++++++++++-- 6 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 examples/table.py create mode 100644 examples/tablehv.csv create mode 100644 examples/tablev.csv diff --git a/examples/advanced.py b/examples/advanced.py index 58e4f19..491bbb7 100644 --- a/examples/advanced.py +++ b/examples/advanced.py @@ -19,7 +19,6 @@ def create_datalist_example(): test = [('str *', 'this is a string'), - (None, 'table.csv'), ('str_m *', """this is a MULTILINE string"""), diff --git a/examples/table.csv b/examples/table.csv index 0dc421d..f81cd8d 100644 --- a/examples/table.csv +++ b/examples/table.csv @@ -1,2 +1,4 @@ -first,second -Pièrre,Flôrent +Name,Age,City +Pièrre,35,Paris +Flôrent,34,Paris +Loïc,36,Paris diff --git a/examples/table.py b/examples/table.py new file mode 100644 index 0000000..ffd7237 --- /dev/null +++ b/examples/table.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2009 Pierre Raybaut +# Licensed under the terms of the MIT License +# (see formlayout.py for details) + +""" +Table examples +""" + +from formlayout import fedit + +datalist = [(None, 'No header:'), + (None, 'table.csv'), + (None, 'Numered headers:'), + (None, '#table.csv'), + (None, 'Horizontal header:'), + (None, '_table.csv'), + (None, 'Vertical header:'), + (None, '|tablev.csv'), + (None, 'Both headers:'), + (None, '/tablehv.csv'), + ] + +print("result:", fedit(datalist, title="Table examples")) diff --git a/examples/tablehv.csv b/examples/tablehv.csv new file mode 100644 index 0000000..d0a3a19 --- /dev/null +++ b/examples/tablehv.csv @@ -0,0 +1,4 @@ +Age,City +Pièrre,35,Paris +Flôrent,34,Paris +Loïc,36,Paris diff --git a/examples/tablev.csv b/examples/tablev.csv new file mode 100644 index 0000000..05141d3 --- /dev/null +++ b/examples/tablev.csv @@ -0,0 +1,3 @@ +Pièrre,35,Paris +Flôrent,34,Paris +Loïc,36,Paris diff --git a/formlayout.py b/formlayout.py index 031a909..ca06115 100644 --- a/formlayout.py +++ b/formlayout.py @@ -362,12 +362,20 @@ def apply(self, callback): class CSVTableModel(QAbstractTableModel): - def __init__(self, csvdata, parent=None): + def __init__(self, csvdata, header, parent=None): QAbstractTableModel.__init__(self, parent) if PY2: self.data = [[el.decode('utf-8') for el in row] for row in csvdata] else: self.data = [row for row in csvdata] + if header in ['_', '/']: + self.hdata = self.data.pop(0) + elif header == '#': + self.hdata = range(1, self.columnCount(parent)+1) + if header in ['|', '/']: + self.vdata = [row.pop(0) for row in self.data] + elif header == '#': + self.vdata = range(1, self.rowCount(parent)+1) def rowCount(self, parent): return len(self.data) @@ -375,6 +383,14 @@ def rowCount(self, parent): def columnCount(self, parent): return len(self.data[0]) + def headerData(self, section, orientation, role): + if orientation == Qt.Horizontal and role == Qt.DisplayRole: + return self.hdata[section] + elif orientation == Qt.Vertical and role == Qt.DisplayRole: + return self.vdata[section] + else: + return None + def data(self, index, role): if index.isValid() and role == Qt.DisplayRole: return self.data[index.row()][index.column()] @@ -542,13 +558,20 @@ def setup(self): elif value.endswith('.csv'): # CSV file import csv + header = None + if value.startswith(('_', '|', '/', '#')): + header, value = value[0], value[1:] if PY2: csvfile = open(value, 'rb') else: csvfile = open(value, 'r') csvdata = csv.reader(csvfile) - tablemodel = CSVTableModel(csvdata) + tablemodel = CSVTableModel(csvdata, header) table = QTableView() + if header in ['_', None]: + table.verticalHeader().hide() + if header in ['|', None]: + table.horizontalHeader().hide() table.setModel(tablemodel) self.formlayout.addRow(table) else: From 895926dcba16081f1570136209d16cea4a616248 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Fri, 24 Jun 2016 05:00:59 +0200 Subject: [PATCH 3/5] Make the Table widget editable --- formlayout.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/formlayout.py b/formlayout.py index ca06115..8863e36 100644 --- a/formlayout.py +++ b/formlayout.py @@ -397,6 +397,16 @@ def data(self, index, role): else: return None + def setData(self, index, value, role): + if index.isValid() and role == Qt.EditRole: + self.data[index.row()][index.column()] = value + return True + else: + return None + + def flags(self, index): + return QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable + def font_is_installed(font): """Check if font is installed""" From d5cbfb113d26fd0c28710e1c107d267c0e0a9942 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Sun, 26 Jun 2016 00:25:49 +0200 Subject: [PATCH 4/5] Fix data naming ambiguity --- formlayout.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/formlayout.py b/formlayout.py index 8863e36..c0e3b95 100644 --- a/formlayout.py +++ b/formlayout.py @@ -365,23 +365,24 @@ class CSVTableModel(QAbstractTableModel): def __init__(self, csvdata, header, parent=None): QAbstractTableModel.__init__(self, parent) if PY2: - self.data = [[el.decode('utf-8') for el in row] for row in csvdata] + self.tabledata = [[el.decode('utf-8') for el in row] + for row in csvdata] else: - self.data = [row for row in csvdata] + self.tabledata = [row for row in csvdata] if header in ['_', '/']: - self.hdata = self.data.pop(0) + self.hdata = self.tabledata.pop(0) elif header == '#': self.hdata = range(1, self.columnCount(parent)+1) if header in ['|', '/']: - self.vdata = [row.pop(0) for row in self.data] + self.vdata = [row.pop(0) for row in self.tabledata] elif header == '#': self.vdata = range(1, self.rowCount(parent)+1) def rowCount(self, parent): - return len(self.data) + return len(self.tabledata) def columnCount(self, parent): - return len(self.data[0]) + return len(self.tabledata[0]) def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: @@ -393,13 +394,13 @@ def headerData(self, section, orientation, role): def data(self, index, role): if index.isValid() and role == Qt.DisplayRole: - return self.data[index.row()][index.column()] + return self.tabledata[index.row()][index.column()] else: return None def setData(self, index, value, role): if index.isValid() and role == Qt.EditRole: - self.data[index.row()][index.column()] = value + self.tabledata[index.row()][index.column()] = value return True else: return None From ca97108c9fa0d4350611665db1233be12b7454b4 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Mon, 27 Jun 2016 04:14:00 +0200 Subject: [PATCH 5/5] Add regular form support to Table widget (when value is a list of list, tuple for header) --- examples/table.py | 20 ++++++++++++++------ formlayout.py | 36 +++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/examples/table.py b/examples/table.py index ffd7237..aacd25c 100644 --- a/examples/table.py +++ b/examples/table.py @@ -10,16 +10,24 @@ from formlayout import fedit -datalist = [(None, 'No header:'), +datalist = [('list of lists:', [[u'Pièrre', 35, 'Paris'], + [u'Flôrent', 34, 'Paris'], + [u'Loïc', 36, 'Paris']]), + ('tuple marks header:', [('Name', 'Age', 'City'), + [u'Pièrre', 35, 'Paris'], + [u'Flôrent', 34, 'Paris'], + [u'Loïc', 36, 'Paris']]), + (None, 'No header CSV:'), (None, 'table.csv'), - (None, 'Numered headers:'), + (None, 'Numered headers CSV:'), (None, '#table.csv'), - (None, 'Horizontal header:'), + (None, 'Horizontal header CSV:'), (None, '_table.csv'), - (None, 'Vertical header:'), + (None, 'Vertical header CSV:'), (None, '|tablev.csv'), - (None, 'Both headers:'), + (None, 'Both headers CSV:'), (None, '/tablehv.csv'), ] -print("result:", fedit(datalist, title="Table examples")) +print('result:', fedit(datalist, title='Table examples', + type='questions', scrollbar=True)) diff --git a/formlayout.py b/formlayout.py index c0e3b95..211e671 100644 --- a/formlayout.py +++ b/formlayout.py @@ -361,14 +361,17 @@ def apply(self, callback): self.dialog.formwidget.get_widgets()) -class CSVTableModel(QAbstractTableModel): - def __init__(self, csvdata, header, parent=None): +class TableModel(QAbstractTableModel): + def __init__(self, data, header, parent=None): QAbstractTableModel.__init__(self, parent) - if PY2: - self.tabledata = [[el.decode('utf-8') for el in row] - for row in csvdata] + if isinstance(data, list): + self.tabledata = list(data) else: - self.tabledata = [row for row in csvdata] + if PY2: + self.tabledata = [[el.decode('utf-8') for el in row] + for row in data] + else: + self.tabledata = [row for row in data] if header in ['_', '/']: self.hdata = self.tabledata.pop(0) elif header == '#': @@ -400,6 +403,8 @@ def data(self, index, role): def setData(self, index, value, role): if index.isValid() and role == Qt.EditRole: + if PY2: + value = to_text_string(value.toString()) self.tabledata[index.row()][index.column()] = value return True else: @@ -577,13 +582,14 @@ def setup(self): else: csvfile = open(value, 'r') csvdata = csv.reader(csvfile) - tablemodel = CSVTableModel(csvdata, header) + tablemodel = TableModel(csvdata, header) table = QTableView() if header in ['_', None]: table.verticalHeader().hide() if header in ['|', None]: table.horizontalHeader().hide() table.setModel(tablemodel) + table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.formlayout.addRow(table) else: # Comment @@ -612,6 +618,17 @@ def setup(self): field = QTextEdit(value, self) else: field = QLineEdit(value, self) + elif isinstance(value, list) and isinstance(value[0], + (list, tuple)): + header = None + field = QTableView() + if isinstance(value[0], tuple): + header = '_' + else: + field.horizontalHeader().hide() + field.verticalHeader().hide() + tablemodel = TableModel(value, header) + field.setModel(tablemodel) elif isinstance(value, (list, tuple)): save_value = value value = list(value) # always needed to protect self.data @@ -743,6 +760,11 @@ def get(self): value = field.value() else: value = to_text_string(field.text()) + elif isinstance(value, list) and isinstance(value[0], + (list, tuple)): + value = list(field.model().tabledata) + if hasattr(field.model(), 'hdata'): + value.insert(0, field.model().hdata) elif isinstance(value, (list, tuple)): index = int(field.currentIndex()) if isinstance(value[0], int):