From 98e430b9ab638e96fc8a30f5cd0d1a5546024463 Mon Sep 17 00:00:00 2001 From: Zeke Date: Sat, 3 Dec 2016 19:30:31 -0800 Subject: [PATCH 01/27] Update setup.py fixed some bad exception catches (for py 3+) --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 5c3aac4..e630e5c 100644 --- a/setup.py +++ b/setup.py @@ -12,13 +12,13 @@ try: import sip except ImportError as e: - raise ImportError, "install sip first (comming with PyQt4)" + raise ImportError("install sip first (comming with PyQt4)") try: import PyQt4 except ImportError as e: # TODO: try to import PySide. - raise ImportError, "install PyQt4 or PySide" + raise ImportError("install PyQt4 or PySide") here = os.path.abspath(os.path.dirname(__file__)) @@ -87,4 +87,4 @@ def run_tests(self): extras_require={ 'testing': tests_require, } -) \ No newline at end of file +) From 907bcd67b1c41cee590f956398a85bf1f7054a9a Mon Sep 17 00:00:00 2001 From: Zeke Date: Sat, 3 Dec 2016 19:41:20 -0800 Subject: [PATCH 02/27] Update setup.py Fixed setup.py to check for pyside and pyqt4 and only fail if neither can be imported. --- setup.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index e630e5c..c544d5c 100644 --- a/setup.py +++ b/setup.py @@ -8,17 +8,25 @@ import re import sys -# TODO: sip is only needed for PyQt4, they should be imported together. +has_qt4 = True try: + # TODO: sip is only needed for PyQt4, they should be imported together. + import PyQt4 import sip except ImportError as e: - raise ImportError("install sip first (comming with PyQt4)") - + has_qt4 = False + try: - import PyQt4 + import PySide except ImportError as e: # TODO: try to import PySide. - raise ImportError("install PyQt4 or PySide") + if not has_qt4: + #We know we failed to import PyQt4/sip... + #And we failed to import pyside. + raise ImportError( "\n\ninstall PyQt4 and sip or PySide") + else: + print("Using PyQt4") + here = os.path.abspath(os.path.dirname(__file__)) From af425f03070402d55f9846512726766eeb388a99 Mon Sep 17 00:00:00 2001 From: Zeke Date: Sat, 3 Dec 2016 22:28:17 -0800 Subject: [PATCH 03/27] Compatibility for Python 3.5 at least to install and use CSVImportDialog so far. --- README | 23 +++- pandasqt/compat.py | 4 +- pandasqt/encoding.py | 10 +- pandasqt/models/ColumnDtypeModel.py | 4 +- pandasqt/models/DataFrameModel.py | 8 +- pandasqt/models/SupportedDtypes.py | 4 +- pandasqt/utils.py | 164 +++++++++++++++++++++++++++- pandasqt/views/CSVDialogs.py | 36 +++--- pandasqt/views/CustomDelegates.py | 4 +- pandasqt/views/_ui/icons_rc.py | 15 +-- 10 files changed, 233 insertions(+), 39 deletions(-) diff --git a/README b/README index 3f68a6a..dd70955 100644 --- a/README +++ b/README @@ -1 +1,22 @@ -Utilities to use pandas (the data analysis / manipulation library for Python) with Qt. \ No newline at end of file +Utilities to use pandas (the data analysis / manipulation library for Python) with Qt. + +I forked this library because I liked the great detail and effort datalyze-solutions put into this package. +I have converted some of this to Python 3+ so it will at least install, but more work is needed. +I think it would be fun to bring this package back to life and get some upgrades put into it. + + +To install: + +pip install git+https://github.com/zbarge/QtPandas.git + + +To use: + +from pandasqt.views.CSVDialogs import CSVImportDialog + +dialog = CSVImportDialog() +dialog.exec_() + + +There are many more features but I don't have documentation. Maybe I'll add it sometime. + diff --git a/pandasqt/compat.py b/pandasqt/compat.py index c75f805..835ea35 100644 --- a/pandasqt/compat.py +++ b/pandasqt/compat.py @@ -10,14 +10,14 @@ sip.setapi('QTextStream', 2) sip.setapi('QTime', 2) sip.setapi('QUrl', 2) -except ValueError, e: +except ValueError as e: log.error(e) try: from PyQt4 import QtCore as QtCore_ from PyQt4 import QtGui as QtGui_ from PyQt4.QtCore import pyqtSlot as Slot, pyqtSignal as Signal -except ImportError, e: +except ImportError as e: from PySide import QtCore as QtCore_ from PySide import QtGui as QtGui_ from PySide.QtCore import Slot, Signal diff --git a/pandasqt/encoding.py b/pandasqt/encoding.py index a5e345a..011990a 100644 --- a/pandasqt/encoding.py +++ b/pandasqt/encoding.py @@ -1,6 +1,6 @@ import sys import os - +import warnings BASEDIR = os.path.dirname(os.path.abspath(__file__)) if sys.platform == 'win32': @@ -12,9 +12,13 @@ try: import magic AUTODETECT = True -except ImportError, e: +except ImportError as e: #if sys.platform == 'darwin': - raise ImportError('Please install libmagic') + #raise ImportError('Please install libmagic') + warnings.warn("Please install libmagic - got an error: {}".format(e)) + AUTODETECT = False +except OSError as e: + warnings.warn("Detector.Issues importing libmagic - got an error: {}".format(e)) AUTODETECT = False diff --git a/pandasqt/models/ColumnDtypeModel.py b/pandasqt/models/ColumnDtypeModel.py index 372398f..4efe214 100644 --- a/pandasqt/models/ColumnDtypeModel.py +++ b/pandasqt/models/ColumnDtypeModel.py @@ -206,7 +206,7 @@ def setData(self, index, value, role=DTYPE_CHANGE_ROLE): try: if dtype == np.dtype(' 0} + + if count_seps_header: + return max(count_seps_header.__iter__(), + key=(lambda key: count_seps_header[key])) + else: + raise Exception("Couldn't identify the sep from the header... here's the information:\n HEADER: {}\n SEPS SEARCHED: {}".format(header,maybe_seps)) + +def superReadText(filepath,**kwargs): + """ + A wrapper to superReadCSV which wraps pandas.read_csv(). + The benefit of using this function is that it automatically identifies the column separator. + .tsv files are assumed to have a \t (tab) separation + .csv files are assumed to have a comma separation. + .txt (or any other type) get the first line of the file opened + and get tested for various separators as defined in the identify_sep function. + """ + if isinstance(filepath,pd.DataFrame): + return filepath + sep = kwargs.get('sep',None) + ext = os.path.splitext(filepath)[1].lower() + + if sep is None: + if ext == '.tsv': + kwargs['sep'] = '\t' + + elif ext == '.csv': + kwargs['sep'] = ',' + + else: + found_sep = identify_sep(filepath) + print(found_sep) + kwargs['sep'] = found_sep + + return superReadCSV(filepath,**kwargs) + +def superReadFile(filepath,**kwargs): + """ + Uses pandas.read_excel (on excel files) and returns a dataframe of the first sheet (unless sheet is specified in kwargs) + Uses superReadText (on .txt,.tsv, or .csv files) and returns a dataframe of the data. + One function to read almost all types of data files. + """ + if isinstance(filepath,pd.DataFrame): return filepath + + excels = ['.xlsx','.xls'] + texts = ['.txt','.tsv','.csv'] + ext = os.path.splitext(filepath)[1].lower() + + if ext in excels: + return pd.read_excel(filepath,**kwargs) + elif ext in texts: + return superReadText(filepath,**kwargs) + else: + raise Exception("Unsupported filetype: {}\n Supported filetypes: {}".format(ext,excels + texts)) + + +def dedupe_cols(frame): + """Need to dedupe columns that have the same name. """ + cols = list(frame.columns) + for i,item in enumerate(frame.columns): + if item in frame.columns[:i]: + cols[i] = "toDROP" + frame.columns = cols + return frame.drop("toDROP", 1, errors='ignore') + +def rename_dupe_cols(cols): + """Takes a list of strings and appends 2,3,4 etc to duplicates. Never appends a 0 or 1. + Appended #s are not always in order...but if you wrap this in a dataframe.to_sql function you're guaranteed + to not have dupe column name errors importing data to SQL...you'll just have to check yourself to see which fields were renamed.""" + counts = {} + positions = {pos:fld for pos,fld in enumerate(cols)} + + for c in cols: + if c in counts.keys(): + counts[c] +=1 + else: + counts[c] = 1 + + fixed_cols = {} + + for pos,col in positions.items(): + if counts[col] > 1: + fix_cols = {pos:fld for pos,fld in positions.items() if fld == col} + keys = [p for p in fix_cols.keys()] + min_pos = min(keys) + cnt = 1 + for p,c in fix_cols.items(): + if not p == min_pos: + cnt += 1 + c = c + str(cnt) + fixed_cols.update({p:c}) + + positions.update(fixed_cols) + + cols = [x for x in positions.values()] + + return cols \ No newline at end of file diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index fa33a0c..b14de43 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -2,7 +2,6 @@ import os from encodings.aliases import aliases as _encodings - import pandas from pandasqt.compat import Qt, QtCore, QtGui, Slot, Signal @@ -11,7 +10,7 @@ from pandasqt.views.CustomDelegates import DtypeComboDelegate from pandasqt.views._ui import icons_rc -from pandasqt.utils import fillNoneValues, convertTimestamps +from pandasqt.utils import fillNoneValues, convertTimestamps, superReadFile class DelimiterValidator(QtGui.QRegExpValidator): """A Custom RegEx Validator. @@ -94,7 +93,7 @@ def _initUI(self): self.commaRadioButton = QtGui.QRadioButton(u'Comma') self.tabRadioButton = QtGui.QRadioButton(u'Tab') self.otherRadioButton = QtGui.QRadioButton(u'Other') - self.semicolonRadioButton.setChecked(True) + self.commaRadioButton.setChecked(True) self.otherSeparatorLineEdit = QtGui.QLineEdit(self) self.otherSeparatorLineEdit.setEnabled(False) @@ -229,7 +228,7 @@ def _initUI(self): self._encodingLabel = QtGui.QLabel(u'File Encoding', self) - encoding_names = map(lambda x: x.upper(), sorted(list(set(_encodings.viewvalues())))) + encoding_names = list(map(lambda x: x.upper(), sorted(list(set(_encodings.values()))))) self._encodingComboBox = QtGui.QComboBox(self) self._encodingComboBox.addItems(encoding_names) self._encodingComboBox.activated.connect(self._updateEncoding) @@ -241,6 +240,7 @@ def _initUI(self): self._headerCheckBox = QtGui.QCheckBox(self) self._headerCheckBox.toggled.connect(self._updateHeader) + layout.addWidget(self._hasHeaderLabel, 2, 0) layout.addWidget(self._headerCheckBox, 2, 1) @@ -281,6 +281,7 @@ def _initUI(self): self._statusBar = QtGui.QStatusBar(self) self._statusBar.setSizeGripEnabled(False) + self._headerCheckBox.setChecked(True) layout.addWidget(self._statusBar, 8, 0, 1, 4) self.setLayout(layout) @@ -305,6 +306,9 @@ def _openFile(self): """ ret = QtGui.QFileDialog.getOpenFileName(self, self.tr(u'open file'), filter='Comma Separated Values (*.csv)') + if isinstance(ret, tuple): + ret = ret[0] #PySide compatibility maybe? + if ret: self._filenameLineEdit.setText(ret) self._updateFilename() @@ -422,22 +426,23 @@ def _loadCSVDataFrame(self): information of the csv file. """ - if self._filename and os.path.exists(self._filename) and self._filename.endswith('.csv'): + if self._filename and os.path.exists(self._filename): # default fallback if no encoding was found/selected - encoding = self._encodingKey or 'uft8' + encoding = self._encodingKey or 'UTF_8' try: - dataFrame = pandas.read_csv(self._filename, - sep=self._delimiter, encoding=encoding, + dataFrame = superReadFile(self._filename, + sep=self._delimiter, first_codec=encoding, header=self._header) dataFrame = dataFrame.apply(fillNoneValues) dataFrame = dataFrame.apply(convertTimestamps) - except Exception, err: + except Exception as err: self.updateStatusBar(str(err)) + print(err) return pandas.DataFrame() self.updateStatusBar('Preview generated.') return dataFrame - self.updateStatusBar('File does not exists or does not end with .csv') + self.updateStatusBar('File could not be read.') return pandas.DataFrame() def _resetWidgets(self): @@ -466,6 +471,7 @@ def accepted(self): df = model.dataFrame().copy() dfModel = DataFrameModel(df) self.load.emit(dfModel, self._filename) + print("Emitted model for {}".format(self._filename)) self._resetWidgets() self.accept() @@ -586,14 +592,14 @@ def _saveModel(self): try: dataFrame = self._model.dataFrame() - except AttributeError, err: + except AttributeError as err: raise AttributeError('No data loaded to export.') else: try: dataFrame.to_csv(filename, encoding=encoding, header=header, index=index, sep=delimiter) - except IOError, err: + except IOError as err: raise IOError('No filename given') - except UnicodeError, err: + except UnicodeError as err: raise UnicodeError('Could not encode all data. Choose a different encoding') except Exception: raise @@ -620,7 +626,7 @@ def accepted(self): """ try: self._saveModel() - except Exception, err: + except Exception as err: self._statusBar.showMessage(str(err)) else: self._resetWidgets() @@ -654,7 +660,7 @@ def _calculateEncodingKey(comparator): """ encodingName = None - for k, v in _encodings.viewitems(): + for k, v in _encodings.items(): if v == comparator: encodingName = k break diff --git a/pandasqt/views/CustomDelegates.py b/pandasqt/views/CustomDelegates.py index c9e472b..3afce08 100644 --- a/pandasqt/views/CustomDelegates.py +++ b/pandasqt/views/CustomDelegates.py @@ -75,7 +75,7 @@ def createEditor(self, parent, option, index): editor.setMinimum(self.minimum) editor.setMaximum(self.maximum) editor.setSingleStep(self.singleStep) - except TypeError, err: + except TypeError as err: # initiate the editor with default values pass return editor @@ -157,7 +157,7 @@ def createEditor(self, parent, option, index): editor.setMaximum(self.maximum) editor.setSingleStep(self.singleStep) editor.setDecimals(self.decimals) - except TypeError, err: + except TypeError as err: # initiate the spinbox with default values. pass return editor diff --git a/pandasqt/views/_ui/icons_rc.py b/pandasqt/views/_ui/icons_rc.py index 3a8c982..9181046 100644 --- a/pandasqt/views/_ui/icons_rc.py +++ b/pandasqt/views/_ui/icons_rc.py @@ -7,9 +7,9 @@ # # WARNING! All changes made in this file will be lost! -from PyQt4 import QtCore +from pandasqt.compat import QtCore -qt_resource_data = "\ +qt_resource_data = u"\ \x00\x00\x0a\x69\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -1955,7 +1955,7 @@ \x4b\x62\x59\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ " -qt_resource_name = "\ +qt_resource_name = u"\ \x00\x05\ \x00\x6f\xa6\x53\ \x00\x69\ @@ -2012,7 +2012,7 @@ \x00\x70\x00\x6e\x00\x67\ " -qt_resource_struct = "\ +qt_resource_struct = u"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x02\ \x00\x00\x00\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x2f\x76\ @@ -2026,11 +2026,12 @@ \x00\x00\x01\xde\x00\x00\x00\x00\x00\x01\x00\x00\x69\x47\ \x00\x00\x01\x46\x00\x00\x00\x00\x00\x01\x00\x00\x42\xbf\ " - +rsc_args = [qt_resource_struct, qt_resource_name, qt_resource_data] +rsc_args = [str.encode(s) for s in rsc_args] def qInitResources(): - QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + QtCore.qRegisterResourceData(int(0x01), *rsc_args) def qCleanupResources(): - QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + QtCore.qUnregisterResourceData(int(0x01), *rsc_args) qInitResources() From af27399919bc2381466730b49b0d7f21c743f9b1 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 10:13:16 -0500 Subject: [PATCH 04/27] Added small a functional example. --- README | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README b/README index dd70955..39ff489 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Utilities to use pandas (the data analysis / manipulation library for Python) with Qt. +# Utilities to use pandas (the data analysis / manipulation library for Python) with Qt. I forked this library because I liked the great detail and effort datalyze-solutions put into this package. I have converted some of this to Python 3+ so it will at least install, but more work is needed. @@ -10,13 +10,21 @@ To install: pip install git+https://github.com/zbarge/QtPandas.git -To use: +To use, create a new Python script containing the following: +``` +from PyQt4.QtCore import * +from PyQt4.QtGui import * from pandasqt.views.CSVDialogs import CSVImportDialog -dialog = CSVImportDialog() -dialog.exec_() +if __name__ == "__main__": + from sys import argv, exit + app = QApplication(argv) + dialog = CSVImportDialog() + dialog.show() + app.exec_() +``` There are many more features but I don't have documentation. Maybe I'll add it sometime. From bddab1f18d9042e1c9b88ce2ab42846beeb93474 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 10:58:57 -0500 Subject: [PATCH 05/27] Added some more choices to csv import view. Some times .csv files may have the .txt or other file extentions despite thier encoding. --- pandasqt/views/CSVDialogs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index b14de43..1f790f4 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -305,7 +305,9 @@ def _openFile(self): This method is also a `SLOT`. """ - ret = QtGui.QFileDialog.getOpenFileName(self, self.tr(u'open file'), filter='Comma Separated Values (*.csv)') + ret = QtGui.QFileDialog.getOpenFileName(self, + self.tr(u'open file'), + filter='Comma Separated Values (*.csv);;Text files (*.txt);;All Files (*)') if isinstance(ret, tuple): ret = ret[0] #PySide compatibility maybe? @@ -664,4 +666,4 @@ def _calculateEncodingKey(comparator): if v == comparator: encodingName = k break - return encodingName \ No newline at end of file + return encodingName From d46ad0c43da92f4f71e852fe753004e8d2eb9f91 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 11:05:52 -0500 Subject: [PATCH 06/27] Revert "Added some more choices to csv import view." This reverts commit bddab1f18d9042e1c9b88ce2ab42846beeb93474. --- pandasqt/views/CSVDialogs.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index 1f790f4..b14de43 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -305,9 +305,7 @@ def _openFile(self): This method is also a `SLOT`. """ - ret = QtGui.QFileDialog.getOpenFileName(self, - self.tr(u'open file'), - filter='Comma Separated Values (*.csv);;Text files (*.txt);;All Files (*)') + ret = QtGui.QFileDialog.getOpenFileName(self, self.tr(u'open file'), filter='Comma Separated Values (*.csv)') if isinstance(ret, tuple): ret = ret[0] #PySide compatibility maybe? @@ -666,4 +664,4 @@ def _calculateEncodingKey(comparator): if v == comparator: encodingName = k break - return encodingName + return encodingName \ No newline at end of file From 80afb5c0a4bfb9f6327e12e4163b633140ee5a19 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 11:19:46 -0500 Subject: [PATCH 07/27] minor pep8 change --- pandasqt/views/CSVDialogs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index 1f790f4..6147419 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -305,9 +305,10 @@ def _openFile(self): This method is also a `SLOT`. """ + file_types = "Comma Separated Values (*.csv);;Text files (*.txt);;All Files (*)" ret = QtGui.QFileDialog.getOpenFileName(self, self.tr(u'open file'), - filter='Comma Separated Values (*.csv);;Text files (*.txt);;All Files (*)') + filter=file_types) if isinstance(ret, tuple): ret = ret[0] #PySide compatibility maybe? From e02de77566a0963233b60df6121e83917e138c61 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 11:30:37 -0500 Subject: [PATCH 08/27] Rename README to README.md --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From d345e0fcbd10b54aa4fd7e9f33344d4f784fba4b Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 11:45:35 -0500 Subject: [PATCH 09/27] Attempting to make test py3 compatible Trying to fix errors that came up when running test_dataTableView.py --- pandasqt/views/EditDialogs.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pandasqt/views/EditDialogs.py b/pandasqt/views/EditDialogs.py index c25b38e..03d6f35 100644 --- a/pandasqt/views/EditDialogs.py +++ b/pandasqt/views/EditDialogs.py @@ -7,6 +7,7 @@ import numpy from pandas import Timestamp from pandas.tslib import NaTType +import warnings class DefaultValueValidator(QtGui.QValidator): def __init__(self, parent=None): @@ -44,7 +45,7 @@ def validate(self, s, pos): elif self.dtype in SupportedDtypes.datetimeTypes(): try: ts = Timestamp(s) - except ValueError, e: + except ValueError as e: return (QtGui.QValidator.Intermediate, s, pos) return (QtGui.QValidator.Acceptable, s, pos) @@ -55,7 +56,7 @@ def validate(self, s, pos): if match: try: value = int(match.string) - except ValueError, e: + except ValueError as e: return (QtGui.QValidator.Invalid, s, pos) dtypeInfo = numpy.iinfo(self.dtype) @@ -65,7 +66,7 @@ def validate(self, s, pos): if match: try: value = int(match.string) - except ValueError, e: + except ValueError as e: return (QtGui.QValidator.Invalid, s, pos) dtypeInfo = numpy.iinfo(self.dtype) @@ -76,7 +77,7 @@ def validate(self, s, pos): if match: try: value = float(match.string) - except ValueError, e: + except ValueError as e: return (QtGui.QValidator.Invalid, s, pos) dtypeInfo = numpy.finfo(self.dtype) @@ -169,7 +170,7 @@ def accept(self): defaultValue = Timestamp('') else: defaultValue = dtype.type() - except ValueError, e: + except ValueError as e: defaultValue = dtype.type() self.accepted.emit(newColumn, dtype, defaultValue) @@ -232,4 +233,4 @@ def accept(self): names.append((position, index.data(QtCore.Qt.DisplayRole))) super(RemoveAttributesDialog, self).accept() - self.accepted.emit(names) \ No newline at end of file + self.accepted.emit(names) From 0603b38726ea5c99bc5031f98adae9773532d5e7 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 11:49:10 -0500 Subject: [PATCH 10/27] readme.md fix --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index c544d5c..f132c22 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ import sip except ImportError as e: has_qt4 = False - + try: import PySide except ImportError as e: @@ -26,7 +26,7 @@ raise ImportError( "\n\ninstall PyQt4 and sip or PySide") else: print("Using PyQt4") - + here = os.path.abspath(os.path.dirname(__file__)) @@ -48,7 +48,7 @@ def read(*filenames, **kwargs): buf.append(f.read()) return sep.join(buf) -long_description = read('README') +long_description = read('README.md') class PyTest(TestCommand): def finalize_options(self): @@ -75,10 +75,10 @@ def run_tests(self): author_email='m.Ludwig@datalyze-solutions.com', description='Utilities to use pandas (the data analysis / manipulation library for Python) with Qt.', long_description=long_description, - + include_package_data=True, packages=['pandasqt'], - + platforms='any', test_suite='tests', classifiers = [ From b8ab2003cad1f21bbb7e53537857d105641b96c4 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 12:39:00 -0500 Subject: [PATCH 11/27] print statement update --- pandasqt/views/EditDialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandasqt/views/EditDialogs.py b/pandasqt/views/EditDialogs.py index 03d6f35..972bde7 100644 --- a/pandasqt/views/EditDialogs.py +++ b/pandasqt/views/EditDialogs.py @@ -73,7 +73,7 @@ def validate(self, s, pos): elif self.dtype in SupportedDtypes.floatTypes(): match = re.search(self.floatPattern, s) - print match + print(match) if match: try: value = float(match.string) From 5af560eb4e0ad5909f60514093e09688df29c8f2 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 12:57:32 -0500 Subject: [PATCH 12/27] Ran 2to3 on pandasqt directory I haven't used the 2to3 command extensively. Hopefully this dosen't break everything. --- .gitignore | 3 +- pandasqt/excepthook.py | 24 ++++----- pandasqt/models/ColumnDtypeModel.py | 2 +- pandasqt/models/DataFrameModel.py | 6 +-- pandasqt/models/DataSearch.py | 4 +- pandasqt/models/mime.py | 4 +- .../ui/fallback/easygui/boxes/base_boxes.py | 14 +++--- .../fallback/easygui/boxes/derived_boxes.py | 2 +- pandasqt/ui/fallback/easygui/boxes/egstore.py | 2 +- .../ui/fallback/easygui/boxes/text_box.py | 4 +- .../easygui/boxes/updatable_text_box.py | 16 +++--- pandasqt/ui/fallback/easygui/boxes/utils.py | 10 ++-- pandasqt/views/BigIntSpinbox.py | 10 ++-- pandasqt/views/CSVDialogs.py | 50 +++++++++---------- pandasqt/views/CustomDelegates.py | 2 +- pandasqt/views/DataTableView.py | 20 ++++---- pandasqt/views/OverlayProgressView.py | 1 - pandasqt/views/_ui/icons_rc.py | 6 +-- 18 files changed, 90 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index a8d6d00..ed33d42 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,5 @@ docs/_build/ target/ .eric4project -*.e4p \ No newline at end of file +*.e4p +*.bak \ No newline at end of file diff --git a/pandasqt/excepthook.py b/pandasqt/excepthook.py index 1661b89..4ca0921 100644 --- a/pandasqt/excepthook.py +++ b/pandasqt/excepthook.py @@ -1,7 +1,7 @@ # copied and modified from Eric IDE ( credits goes to author ) import time -import cStringIO +import io import traceback from pandasqt.compat import QtGui import codecs @@ -18,14 +18,14 @@ def excepthook(excType, excValue, tracebackobj): @param excValue exception value @param tracebackobj traceback object """ - separator = u'-' * 80 + separator = '-' * 80 logFile = os.path.join(tempfile.gettempdir(), "error.log") notice = """An unhandled exception occurred. Please report the problem.\n""" notice += """A log has been written to "{}".\n\nError information:""".format(logFile) timeString = time.strftime("%Y-%m-%d, %H:%M:%S") - tbinfofile = cStringIO.StringIO() + tbinfofile = io.StringIO() traceback.print_tb(tracebackobj, None, tbinfofile) tbinfofile.seek(0) tbinfo = tbinfofile.read() @@ -33,26 +33,26 @@ def excepthook(excType, excValue, tracebackobj): try: excValueStr = str(excValue).decode('utf-8') - except UnicodeEncodeError, e: - excValueStr = unicode(excValue) + except UnicodeEncodeError as e: + excValueStr = str(excValue) - errmsg = u'{0}: \n{1}'.format(excType, excValueStr) - sections = [u'\n', separator, timeString, separator, errmsg, separator, tbinfo] - msg = u'\n'.join(sections) + errmsg = '{0}: \n{1}'.format(excType, excValueStr) + sections = ['\n', separator, timeString, separator, errmsg, separator, tbinfo] + msg = '\n'.join(sections) try: f = codecs.open(logFile, "a+", encoding='utf-8') f.write(msg) f.close() - except IOError, e: - msgbox(u"unable to write to {0}".format(logFile), u"Writing error") + except IOError as e: + msgbox("unable to write to {0}".format(logFile), "Writing error") # always show an error message try: if not _isQAppRunning(): app = QtGui.QApplication([]) - _showMessageBox(unicode(notice) + unicode(msg)) + _showMessageBox(str(notice) + str(msg)) except: - msgbox(unicode(notice) + unicode(msg), u"Error") + msgbox(str(notice) + str(msg), "Error") def _isQAppRunning(): if QtGui.QApplication.instance() is None: diff --git a/pandasqt/models/ColumnDtypeModel.py b/pandasqt/models/ColumnDtypeModel.py index 4efe214..6eb825b 100644 --- a/pandasqt/models/ColumnDtypeModel.py +++ b/pandasqt/models/ColumnDtypeModel.py @@ -206,7 +206,7 @@ def setData(self, index, value, role=DTYPE_CHANGE_ROLE): try: if dtype == np.dtype('".format(selectionEvent), @@ -1097,7 +1097,7 @@ def __put_buttons_in_buttonframe(choices, default_choice, cancel_choice): buttons[default_choice]['default_choice'] = True buttons[default_choice]['widget'].focus_force() # Bind hotkeys - for hk in [button['hotkey'] for button in buttons.values() if button['hotkey']]: + for hk in [button['hotkey'] for button in list(buttons.values()) if button['hotkey']]: boxRoot.bind_all(hk, lambda e: __buttonEvent(e, buttons), add=True) return diff --git a/pandasqt/ui/fallback/easygui/boxes/derived_boxes.py b/pandasqt/ui/fallback/easygui/boxes/derived_boxes.py index 66e6828..4fe6851 100644 --- a/pandasqt/ui/fallback/easygui/boxes/derived_boxes.py +++ b/pandasqt/ui/fallback/easygui/boxes/derived_boxes.py @@ -203,7 +203,7 @@ def msgbox(msg="(Your message goes here)", title=" ", :param tk_widget root: Top-level Tk widget :return: the text of the ok_button """ - if not isinstance(ok_button, ut.basestring): + if not isinstance(ok_button, ut.str): raise AssertionError( "The 'ok_button' argument to msgbox must be a string.") diff --git a/pandasqt/ui/fallback/easygui/boxes/egstore.py b/pandasqt/ui/fallback/easygui/boxes/egstore.py index 23efd44..d7ff228 100644 --- a/pandasqt/ui/fallback/easygui/boxes/egstore.py +++ b/pandasqt/ui/fallback/easygui/boxes/egstore.py @@ -145,7 +145,7 @@ def __str__(self): # find the length of the longest attribute name longest_key_length = 0 keys = list() - for key in self.__dict__.keys(): + for key in list(self.__dict__.keys()): keys.append(key) longest_key_length = max(longest_key_length, len(key)) diff --git a/pandasqt/ui/fallback/easygui/boxes/text_box.py b/pandasqt/ui/fallback/easygui/boxes/text_box.py index 01d1fcd..922eae9 100644 --- a/pandasqt/ui/fallback/easygui/boxes/text_box.py +++ b/pandasqt/ui/fallback/easygui/boxes/text_box.py @@ -174,7 +174,7 @@ def __textboxOK(event): def to_string(something): - if isinstance(something, ut.basestring): + if isinstance(something, ut.str): return something try: text = "".join(something) # convert a list or a tuple to a string @@ -196,4 +196,4 @@ def demo_textbox(): title = "Demo of textbox" msg = "Here is some sample text. " * 16 reply = textbox(msg, title, text_snippet) - print("Reply was: {!s}".format(reply)) + print(("Reply was: {!s}".format(reply))) diff --git a/pandasqt/ui/fallback/easygui/boxes/updatable_text_box.py b/pandasqt/ui/fallback/easygui/boxes/updatable_text_box.py index b6fa594..e15d46e 100644 --- a/pandasqt/ui/fallback/easygui/boxes/updatable_text_box.py +++ b/pandasqt/ui/fallback/easygui/boxes/updatable_text_box.py @@ -32,22 +32,22 @@ import tkinter.filedialog as tk_FileDialog from io import StringIO else: - from Tkinter import * - import tkFileDialog as tk_FileDialog - from StringIO import StringIO + from tkinter import * + import tkinter.filedialog as tk_FileDialog + from io import StringIO # Set up basestring appropriately if runningPython3: - basestring = str + str = str if TkVersion < 8.0: stars = "*" * 75 - print("""\n\n\n""" + stars + """ + print(("""\n\n\n""" + stars + """ You are running Tk version: """ + str(TkVersion) + """ You must be using Tk version 8.0 or greater to use EasyGui. Terminating. -""" + stars + """\n\n\n""") +""" + stars + """\n\n\n""")) sys.exit(0) rootWindowPosition = "+300+200" @@ -223,7 +223,7 @@ def __update_myself(event): # ----------------- the action begins ------------------------------------ try: # load the text into the textArea - if isinstance(text, basestring): + if isinstance(text, str): pass else: try: @@ -267,4 +267,4 @@ def _demo_textbox(): text_snippet = (( "Update button!!!. " * 5) + "\n\n") * 10 reply = textbox(msg, title, text_snippet, get_updated_text=update) - print("Reply was: {!s}".format(reply)) \ No newline at end of file + print(("Reply was: {!s}".format(reply))) \ No newline at end of file diff --git a/pandasqt/ui/fallback/easygui/boxes/utils.py b/pandasqt/ui/fallback/easygui/boxes/utils.py index 3f8fbb7..7694dc4 100644 --- a/pandasqt/ui/fallback/easygui/boxes/utils.py +++ b/pandasqt/ui/fallback/easygui/boxes/utils.py @@ -29,9 +29,9 @@ import tkinter.filedialog as tk_FileDialog except ImportError: try: - import Tkinter as tk # python2 - from Tkinter import * - import tkFileDialog as tk_FileDialog + import tkinter as tk # python2 + from tkinter import * + import tkinter.filedialog as tk_FileDialog except ImportError: raise ImportError("Unable to find tkinter package.") @@ -49,9 +49,9 @@ # Code should use 'basestring' anywhere you might think to use the system 'str'. This is all to support # Python 2. If 2 ever goes away, this logic can go away and uses of utils.basestring should be changed to just str if runningPython27: - basestring = basestring + str = str if runningPython34: - basestring = str + str = str def lower_case_sort(things): if runningPython34: diff --git a/pandasqt/views/BigIntSpinbox.py b/pandasqt/views/BigIntSpinbox.py index 9d1bb16..80c6111 100644 --- a/pandasqt/views/BigIntSpinbox.py +++ b/pandasqt/views/BigIntSpinbox.py @@ -53,11 +53,11 @@ def setValue(self, value): True if all went fine. """ if value >= self.minimum() and value <= self.maximum(): - self._lineEdit.setText(unicode(value)) + self._lineEdit.setText(str(value)) elif value < self.minimum(): - self._lineEdit.setText(unicode(self.minimum())) + self._lineEdit.setText(str(self.minimum())) elif value > self.maximum(): - self._lineEdit.setText(unicode(self.maximum())) + self._lineEdit.setText(str(self.maximum())) return True def stepBy(self, steps): @@ -116,7 +116,7 @@ def setMinimum(self, minimum): Raises: TypeError: If the given argument is not an integer. """ - if not isinstance(minimum, (int, long)): + if not isinstance(minimum, int): raise TypeError("Argument is not of type int or long") self._minimum = minimum @@ -130,6 +130,6 @@ def setMaximum(self, maximum): Args: maximum (int or long): new _maximum value """ - if not isinstance(maximum, (int, long)): + if not isinstance(maximum, int): raise TypeError("Argument is not of type int or long") self._maximum = maximum \ No newline at end of file diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index ceae88d..28638a0 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -89,10 +89,10 @@ def _initUI(self): """ #layout = QtGui.QHBoxLayout(self) - self.semicolonRadioButton = QtGui.QRadioButton(u'Semicolon') - self.commaRadioButton = QtGui.QRadioButton(u'Comma') - self.tabRadioButton = QtGui.QRadioButton(u'Tab') - self.otherRadioButton = QtGui.QRadioButton(u'Other') + self.semicolonRadioButton = QtGui.QRadioButton('Semicolon') + self.commaRadioButton = QtGui.QRadioButton('Comma') + self.tabRadioButton = QtGui.QRadioButton('Tab') + self.otherRadioButton = QtGui.QRadioButton('Other') self.commaRadioButton.setChecked(True) self.otherSeparatorLineEdit = QtGui.QLineEdit(self) @@ -194,7 +194,7 @@ def __init__(self, parent=None): """ super(CSVImportDialog, self).__init__(parent) self._modal = True - self._windowTitle = u'Import CSV' + self._windowTitle = 'Import CSV' self._encodingKey = None self._filename = None self._delimiter = None @@ -211,7 +211,7 @@ def _initUI(self): layout = QtGui.QGridLayout() - self._filenameLabel = QtGui.QLabel(u'Choose File', self) + self._filenameLabel = QtGui.QLabel('Choose File', self) self._filenameLineEdit = QtGui.QLineEdit(self) self._filenameLineEdit.textEdited.connect(self._updateFilename) chooseFileButtonIcon = QtGui.QIcon(QtGui.QPixmap(':/icons/document-open.png')) @@ -226,9 +226,9 @@ def _initUI(self): layout.addWidget(self._filenameLineEdit, 0, 1, 1, 2) layout.addWidget(self._chooseFileButton, 0, 3) - self._encodingLabel = QtGui.QLabel(u'File Encoding', self) + self._encodingLabel = QtGui.QLabel('File Encoding', self) - encoding_names = list(map(lambda x: x.upper(), sorted(list(set(_encodings.values()))))) + encoding_names = list([x.upper() for x in sorted(list(set(_encodings.values())))]) self._encodingComboBox = QtGui.QComboBox(self) self._encodingComboBox.addItems(encoding_names) self._encodingComboBox.activated.connect(self._updateEncoding) @@ -236,7 +236,7 @@ def _initUI(self): layout.addWidget(self._encodingLabel, 1, 0) layout.addWidget(self._encodingComboBox, 1, 1, 1, 1) - self._hasHeaderLabel = QtGui.QLabel(u'Header Available?', self) + self._hasHeaderLabel = QtGui.QLabel('Header Available?', self) self._headerCheckBox = QtGui.QCheckBox(self) self._headerCheckBox.toggled.connect(self._updateHeader) @@ -244,7 +244,7 @@ def _initUI(self): layout.addWidget(self._hasHeaderLabel, 2, 0) layout.addWidget(self._headerCheckBox, 2, 1) - self._delimiterLabel = QtGui.QLabel(u'Column Delimiter', self) + self._delimiterLabel = QtGui.QLabel('Column Delimiter', self) self._delimiterBox = DelimiterSelectionWidget(self) self._delimiter = self._delimiterBox.currentSelected() self._delimiterBox.delimiter.connect(self._updateDelimiter) @@ -255,18 +255,18 @@ def _initUI(self): self._tabWidget = QtGui.QTabWidget(self) self._previewTableView = QtGui.QTableView(self) self._datatypeTableView = QtGui.QTableView(self) - self._tabWidget.addTab(self._previewTableView, u'Preview') - self._tabWidget.addTab(self._datatypeTableView, u'Change Column Types') + self._tabWidget.addTab(self._previewTableView, 'Preview') + self._tabWidget.addTab(self._datatypeTableView, 'Change Column Types') layout.addWidget(self._tabWidget, 4, 0, 3, 4) self._datatypeTableView.horizontalHeader().setDefaultSectionSize(200) self._datatypeTableView.setItemDelegateForColumn(1, DtypeComboDelegate(self._datatypeTableView)) - self._loadButton = QtGui.QPushButton(u'Load Data', self) + self._loadButton = QtGui.QPushButton('Load Data', self) #self.loadButton.setAutoDefault(False) - self._cancelButton = QtGui.QPushButton(u'Cancel', self) + self._cancelButton = QtGui.QPushButton('Cancel', self) # self.cancelButton.setDefault(False) # self.cancelButton.setAutoDefault(True) @@ -308,7 +308,7 @@ def _openFile(self): file_types = "Comma Separated Values (*.csv);;Text files (*.txt);;All Files (*)" ret = QtGui.QFileDialog.getOpenFileName(self, - self.tr(u'open file'), + self.tr('open file'), filter=file_types) if isinstance(ret, tuple): @@ -476,7 +476,7 @@ def accepted(self): df = model.dataFrame().copy() dfModel = DataFrameModel(df) self.load.emit(dfModel, self._filename) - print("Emitted model for {}".format(self._filename)) + print(("Emitted model for {}".format(self._filename))) self._resetWidgets() self.accept() @@ -502,7 +502,7 @@ def __init__(self, model=None, parent=None): super(CSVExportDialog, self).__init__(parent) self._model = model self._modal = True - self._windowTitle = u'Export to CSV' + self._windowTitle = 'Export to CSV' self._idx = -1 self._initUI() @@ -515,7 +515,7 @@ def _initUI(self): layout = QtGui.QGridLayout() - self._filenameLabel = QtGui.QLabel(u'Output File', self) + self._filenameLabel = QtGui.QLabel('Output File', self) self._filenameLineEdit = QtGui.QLineEdit(self) chooseFileButtonIcon = QtGui.QIcon(QtGui.QPixmap(':/icons/document-save-as.png')) self._chooseFileAction = QtGui.QAction(self) @@ -529,9 +529,9 @@ def _initUI(self): layout.addWidget(self._filenameLineEdit, 0, 1, 1, 2) layout.addWidget(self._chooseFileButton, 0, 3) - self._encodingLabel = QtGui.QLabel(u'File Encoding', self) + self._encodingLabel = QtGui.QLabel('File Encoding', self) - encoding_names = map(lambda x: x.upper(), sorted(list(set(_encodings.viewvalues())))) + encoding_names = [x.upper() for x in sorted(list(set(_encodings.values())))] self._encodingComboBox = QtGui.QComboBox(self) self._encodingComboBox.addItems(encoding_names) self._idx = encoding_names.index('UTF_8') @@ -541,21 +541,21 @@ def _initUI(self): layout.addWidget(self._encodingLabel, 1, 0) layout.addWidget(self._encodingComboBox, 1, 1, 1, 1) - self._hasHeaderLabel = QtGui.QLabel(u'Header Available?', self) + self._hasHeaderLabel = QtGui.QLabel('Header Available?', self) self._headerCheckBox = QtGui.QCheckBox(self) #self._headerCheckBox.toggled.connect(self._updateHeader) layout.addWidget(self._hasHeaderLabel, 2, 0) layout.addWidget(self._headerCheckBox, 2, 1) - self._delimiterLabel = QtGui.QLabel(u'Column Delimiter', self) + self._delimiterLabel = QtGui.QLabel('Column Delimiter', self) self._delimiterBox = DelimiterSelectionWidget(self) layout.addWidget(self._delimiterLabel, 3, 0) layout.addWidget(self._delimiterBox, 3, 1, 1, 3) - self._exportButton = QtGui.QPushButton(u'Export Data', self) - self._cancelButton = QtGui.QPushButton(u'Cancel', self) + self._exportButton = QtGui.QPushButton('Export Data', self) + self._cancelButton = QtGui.QPushButton('Cancel', self) self._buttonBox = QtGui.QDialogButtonBox(self) self._buttonBox.addButton(self._exportButton, QtGui.QDialogButtonBox.AcceptRole) @@ -665,7 +665,7 @@ def _calculateEncodingKey(comparator): """ encodingName = None - for k, v in _encodings.items(): + for k, v in list(_encodings.items()): if v == comparator: encodingName = k break diff --git a/pandasqt/views/CustomDelegates.py b/pandasqt/views/CustomDelegates.py index 3afce08..6c693e4 100644 --- a/pandasqt/views/CustomDelegates.py +++ b/pandasqt/views/CustomDelegates.py @@ -225,7 +225,7 @@ def setEditorData(self, editor, index): """ if index.isValid(): value = index.model().data(index, QtCore.Qt.EditRole) - editor.setText(unicode(value)) + editor.setText(str(value)) def setModelData(self, editor, model, index): """Gets data from the editor widget and stores it in the specified model at the item index. diff --git a/pandasqt/views/DataTableView.py b/pandasqt/views/DataTableView.py index f3a827f..5c6ab8a 100644 --- a/pandasqt/views/DataTableView.py +++ b/pandasqt/views/DataTableView.py @@ -101,40 +101,40 @@ def initUi(self): self.editButton = QtGui.QToolButton(self.buttonFrame) self.editButton.setObjectName('editbutton') - self.editButton.setText(self.tr(u'edit')) - self.editButton.setToolTip(self.tr(u'toggle editing mode')) + self.editButton.setText(self.tr('edit')) + self.editButton.setToolTip(self.tr('toggle editing mode')) icon = QtGui.QIcon(QtGui.QPixmap(_fromUtf8(':/icons/document-edit.png'))) self.editButton.setIcon(icon) self.addColumnButton = QtGui.QToolButton(self.buttonFrame) self.addColumnButton.setObjectName('addcolumnbutton') - self.addColumnButton.setText(self.tr(u'+col')) - self.addColumnButton.setToolTip(self.tr(u'add new column')) + self.addColumnButton.setText(self.tr('+col')) + self.addColumnButton.setToolTip(self.tr('add new column')) icon = QtGui.QIcon(QtGui.QPixmap(_fromUtf8(':/icons/edit-table-insert-column-right.png'))) self.addColumnButton.setIcon(icon) self.addRowButton = QtGui.QToolButton(self.buttonFrame) self.addRowButton.setObjectName('addrowbutton') - self.addRowButton.setText(self.tr(u'+row')) - self.addRowButton.setToolTip(self.tr(u'add new row')) + self.addRowButton.setText(self.tr('+row')) + self.addRowButton.setToolTip(self.tr('add new row')) icon = QtGui.QIcon(QtGui.QPixmap(_fromUtf8(':/icons/edit-table-insert-row-below.png'))) self.addRowButton.setIcon(icon) self.removeColumnButton = QtGui.QToolButton(self.buttonFrame) self.removeColumnButton.setObjectName('removecolumnbutton') - self.removeColumnButton.setText(self.tr(u'-col')) - self.removeColumnButton.setToolTip(self.tr(u'remove a column')) + self.removeColumnButton.setText(self.tr('-col')) + self.removeColumnButton.setToolTip(self.tr('remove a column')) icon = QtGui.QIcon(QtGui.QPixmap(_fromUtf8(':/icons/edit-table-delete-column.png'))) self.removeColumnButton.setIcon(icon) self.removeRowButton = QtGui.QToolButton(self.buttonFrame) self.removeRowButton.setObjectName('removerowbutton') - self.removeRowButton.setText(self.tr(u'-row')) - self.removeRowButton.setToolTip(self.tr(u'remove selected rows')) + self.removeRowButton.setText(self.tr('-row')) + self.removeRowButton.setToolTip(self.tr('remove selected rows')) icon = QtGui.QIcon(QtGui.QPixmap(_fromUtf8(':/icons/edit-table-delete-row.png'))) self.removeRowButton.setIcon(icon) diff --git a/pandasqt/views/OverlayProgressView.py b/pandasqt/views/OverlayProgressView.py index 2ebce7d..ecfc029 100644 --- a/pandasqt/views/OverlayProgressView.py +++ b/pandasqt/views/OverlayProgressView.py @@ -95,7 +95,6 @@ def _addProgressBar(self, worker): worker.progressChanged.connect(self.debugProgressChanged) def debugProgressChanged(self, value): - print "debugProgressChanged", value def addWorker(self, worker): self._workers.append(worker) diff --git a/pandasqt/views/_ui/icons_rc.py b/pandasqt/views/_ui/icons_rc.py index 9181046..e60d90e 100644 --- a/pandasqt/views/_ui/icons_rc.py +++ b/pandasqt/views/_ui/icons_rc.py @@ -9,7 +9,7 @@ from pandasqt.compat import QtCore -qt_resource_data = u"\ +qt_resource_data = "\ \x00\x00\x0a\x69\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -1955,7 +1955,7 @@ \x4b\x62\x59\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ " -qt_resource_name = u"\ +qt_resource_name = "\ \x00\x05\ \x00\x6f\xa6\x53\ \x00\x69\ @@ -2012,7 +2012,7 @@ \x00\x70\x00\x6e\x00\x67\ " -qt_resource_struct = u"\ +qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x02\ \x00\x00\x00\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x2f\x76\ From c02702944221e2c67047d8f1866850a55f74f613 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 12:58:01 -0500 Subject: [PATCH 13/27] missed one --- pandasqt/views/OverlayProgressView.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandasqt/views/OverlayProgressView.py b/pandasqt/views/OverlayProgressView.py index ecfc029..958faac 100644 --- a/pandasqt/views/OverlayProgressView.py +++ b/pandasqt/views/OverlayProgressView.py @@ -95,6 +95,7 @@ def _addProgressBar(self, worker): worker.progressChanged.connect(self.debugProgressChanged) def debugProgressChanged(self, value): + print(("debugProgressChanged", value)) def addWorker(self, worker): self._workers.append(worker) From 1f8a66e8ac0ef659f2a844a8940624a6928d9289 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 13:37:50 -0500 Subject: [PATCH 14/27] CONVERT IT ALL!!! Ran 2to3 everything. Let's see what happens. --- doc/source/conf.py | 16 ++++++++-------- examples/TestApp.py | 8 ++++---- examples/ThreadedExample.py | 2 +- examples/util.py | 2 +- setup.py | 2 +- tests/test_BigIntSpinbox.py | 12 ++++++------ tests/test_CSVDialogs.py | 12 ++++++------ tests/test_DataFrameModel.py | 18 +++++++++--------- tests/test_DataTableView.py | 12 +++++++++++- tests/test_SupportedDtypes.py | 2 +- tests/test_excepthook.py | 8 ++++---- 11 files changed, 52 insertions(+), 42 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 8f7fb65..0f7cd88 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -53,8 +53,8 @@ master_doc = 'index' # General information about the project. -project = u'pandas-qt' -copyright = u'2014, Matthias Ludwig - Datalyze Solutions' +project = 'pandas-qt' +copyright = '2014, Matthias Ludwig - Datalyze Solutions' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -225,8 +225,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'pandas-qt.tex', u'pandas-qt Documentation', - u'Matthias Ludwig - Datalyze Solutions', 'manual'), + ('index', 'pandas-qt.tex', 'pandas-qt Documentation', + 'Matthias Ludwig - Datalyze Solutions', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -255,8 +255,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pandas-qt', u'pandas-qt Documentation', - [u'Matthias Ludwig - Datalyze Solutions'], 1) + ('index', 'pandas-qt', 'pandas-qt Documentation', + ['Matthias Ludwig - Datalyze Solutions'], 1) ] # If true, show URL addresses after external links. @@ -269,8 +269,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'pandas-qt', u'pandas-qt Documentation', - u'Matthias Ludwig - Datalyze Solutions', 'pandas-qt', 'One line description of project.', + ('index', 'pandas-qt', 'pandas-qt Documentation', + 'Matthias Ludwig - Datalyze Solutions', 'pandas-qt', 'One line description of project.', 'Miscellaneous'), ] diff --git a/examples/TestApp.py b/examples/TestApp.py index ea0a888..bdc0d88 100644 --- a/examples/TestApp.py +++ b/examples/TestApp.py @@ -46,7 +46,7 @@ def dropEvent(self, event): """ super(DropLineEdit, self).dropEvent(event) mimeDataPayload = event.mimeData().data() - self.setText(u"dropped column: {0}".format(mimeDataPayload.column)) + self.setText("dropped column: {0}".format(mimeDataPayload.column)) class ComplexDropWidget(QtGui.QLineEdit): @@ -217,13 +217,13 @@ def setModelColumn(self, index): self.dataComboBox.setModelColumn(index) def goToColumn(self): - print "go to column 7" + print("go to column 7") index = self.dataTableView.view().model().index(7, 0) self.dataTableView.view().setCurrentIndex(index) def changeColumnValue(self, columnName, index, dtype): - print "failed to change", columnName, "to", dtype - print index.data(), index.isValid() + print("failed to change", columnName, "to", dtype) + print(index.data(), index.isValid()) self.dataTableView.view().setCurrentIndex(index) def setFilter(self): diff --git a/examples/ThreadedExample.py b/examples/ThreadedExample.py index 3573624..83d04c7 100644 --- a/examples/ThreadedExample.py +++ b/examples/ThreadedExample.py @@ -66,7 +66,7 @@ def initUI(self): @Slot() def debugPrint(self): - print 'THREAD %s ended' % (self.sender().name, ) + print('THREAD %s ended' % (self.sender().name, )) if __name__ == '__main__': diff --git a/examples/util.py b/examples/util.py index 1828be0..cb51dd9 100644 --- a/examples/util.py +++ b/examples/util.py @@ -46,7 +46,7 @@ def getCsvData(): return df def getRandomData(rows=100, columns=5): - columns = [u"column {}".format(column) for column in range(columns) ] + columns = ["column {}".format(column) for column in range(columns) ] data = {} for column in columns: data[column] = numpy.random.rand(rows) diff --git a/setup.py b/setup.py index f132c22..6e4d194 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ -from __future__ import print_function + from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand import io diff --git a/tests/test_BigIntSpinbox.py b/tests/test_BigIntSpinbox.py index 97eefa7..0373173 100644 --- a/tests/test_BigIntSpinbox.py +++ b/tests/test_BigIntSpinbox.py @@ -29,21 +29,21 @@ def test_minimumMaximum(self, spinbox): def test_setMinimumMaximum(self, spinbox): spinbox.setMinimum(0) - spinbox.setMinimum(long(0)) + spinbox.setMinimum(int(0)) spinbox.setMinimum(1) - spinbox.setMinimum(long(1)) + spinbox.setMinimum(int(1)) spinbox.setMinimum(-1) - spinbox.setMinimum(long(-1)) + spinbox.setMinimum(int(-1)) with pytest.raises(TypeError) as excinfo: spinbox.setMinimum('') assert "int or long" in str(excinfo.value) spinbox.setMaximum(0) - spinbox.setMaximum(long(0)) + spinbox.setMaximum(int(0)) spinbox.setMaximum(1) - spinbox.setMaximum(long(1)) + spinbox.setMaximum(int(1)) spinbox.setMaximum(-1) - spinbox.setMaximum(long(-1)) + spinbox.setMaximum(int(-1)) with pytest.raises(TypeError) as excinfo: spinbox.setMaximum('') assert "int or long" in str(excinfo.value) diff --git a/tests/test_CSVDialogs.py b/tests/test_CSVDialogs.py index 61f5900..d1fbfe7 100644 --- a/tests/test_CSVDialogs.py +++ b/tests/test_CSVDialogs.py @@ -92,18 +92,18 @@ def test_init(self, qtbot): qtbot.addWidget(csvwidget) csvwidget.show() assert csvwidget.isModal() - assert csvwidget.windowTitle() == u'Import CSV' + assert csvwidget.windowTitle() == 'Import CSV' def test_fileinput(self, qtbot, csv_file): csvwidget = CSVImportDialog() qtbot.addWidget(csvwidget) csvwidget.show() labels = csvwidget.findChildren(QtGui.QLabel) - assert labels[0].text() == u'Choose File' + assert labels[0].text() == 'Choose File' lineedits = csvwidget.findChildren(QtGui.QLineEdit) qtbot.keyClicks(lineedits[0], csv_file) assert csvwidget._previewTableView.model() is not None - assert csvwidget._delimiter == u';' + assert csvwidget._delimiter == ';' assert csvwidget._header is None def test_header(self, qtbot): @@ -180,7 +180,7 @@ def _assert(x, path): assert x assert isinstance(x, DataFrameModel) assert path - assert isinstance(path, basestring) + assert isinstance(path, str) csvwidget.load.connect(_assert) with qtbot.waitSignal(csvwidget.load): @@ -193,14 +193,14 @@ def test_init(self, qtbot): qtbot.addWidget(csvwidget) csvwidget.show() assert csvwidget.isModal() - assert csvwidget.windowTitle() == u'Export to CSV' + assert csvwidget.windowTitle() == 'Export to CSV' def test_fileoutput(self, qtbot, csv_file): csvwidget = CSVExportDialog() qtbot.addWidget(csvwidget) csvwidget.show() labels = csvwidget.findChildren(QtGui.QLabel) - assert labels[0].text() == u'Output File' + assert labels[0].text() == 'Output File' lineedits = csvwidget.findChildren(QtGui.QLineEdit) qtbot.keyClicks(lineedits[0], csv_file) assert csvwidget._filenameLineEdit.text() == csv_file diff --git a/tests/test_DataFrameModel.py b/tests/test_DataFrameModel.py index d1ade75..2dc0b6c 100644 --- a/tests/test_DataFrameModel.py +++ b/tests/test_DataFrameModel.py @@ -34,7 +34,7 @@ def test_setDataFrame(): with pytest.raises(TypeError) as excinfo: model.setDataFrame(None) - assert "pandas.core.frame.DataFrame" in unicode(excinfo.value) + assert "pandas.core.frame.DataFrame" in str(excinfo.value) @pytest.mark.parametrize( "copy, operator", @@ -54,13 +54,13 @@ def test_copyDataFrame(copy, operator): def test_TimestampFormat(): model = DataFrameModel() assert model.timestampFormat == Qt.ISODate - newFormat = u"yy-MM-dd hh:mm" + newFormat = "yy-MM-dd hh:mm" model.timestampFormat = newFormat assert model.timestampFormat == newFormat with pytest.raises(TypeError) as excinfo: model.timestampFormat = "yy-MM-dd hh:mm" - assert "unicode" in unicode(excinfo.value) + assert "unicode" in str(excinfo.value) #def test_signalUpdate(qtbot): #model = DataFrameModel() @@ -194,7 +194,7 @@ def test_unhandledDtype(self, model, index): @pytest.mark.parametrize( "value, dtype", [ ("test", object), - (u"äöü", object), + ("äöü", object), ] ) def test_strAndUnicode(self, model, index, value, dtype): @@ -348,19 +348,19 @@ def test_unhandledDtype(self, model, index): model.enableEditing(True) with pytest.raises(TypeError) as excinfo: model.setData(index, numpy.complex64(92+151j)) - assert "unhandled data type" in unicode(excinfo.value) + assert "unhandled data type" in str(excinfo.value) @pytest.mark.parametrize( "value, dtype", [ ("test", object), - (u"äöü", object), + ("äöü", object), ] ) def test_strAndUnicode(self, model, index, value, dtype): dataFrame = pandas.DataFrame([value], columns=['A']) dataFrame['A'] = dataFrame['A'].astype(dtype) model.setDataFrame(dataFrame) - newValue = u"{}123".format(value) + newValue = "{}123".format(value) model.enableEditing(True) assert model.setData(index, newValue) assert model.data(index) == newValue @@ -611,7 +611,7 @@ def test_add_column(self, model, newColumns): defaultVal = _type() assert model.addDataFrameColumn(desc, _type, defaultVal) - for row in xrange(rowCount): + for row in range(rowCount): idx = model.index(row, columnCount + index) newVal = idx.data(DATAFRAME_ROLE) assert newVal == defaultVal @@ -642,7 +642,7 @@ def test_remove_columns_random(self, dataFrame): columnNames = dataFrame.columns.tolist() columnNames = [(i, n) for i, n in enumerate(columnNames)] - for cycle in xrange(1000): + for cycle in range(1000): elements = random.randint(1, len(columnNames)) names = random.sample(columnNames, elements) df = dataFrame.copy() diff --git a/tests/test_DataTableView.py b/tests/test_DataTableView.py index d0dab75..fa815dd 100644 --- a/tests/test_DataTableView.py +++ b/tests/test_DataTableView.py @@ -145,7 +145,7 @@ def test_addColumn(self, qtbot, dataModel): dlg_buttons = dlg.findChildren(QtGui.QPushButton) comboBox = dlg.findChildren(QtGui.QComboBox)[-1] - for i in xrange(comboBox.count()): + for i in range(comboBox.count()): columns.append(comboBox.itemText(i)) for b in dlg_buttons: @@ -206,3 +206,13 @@ def test_removeColumns(self, qtbot, dataModel2): break assert widget.view().model().columnCount() == 0 + +if __name__ == "__main__": + TestTableViewWidget.Test_removeColumns() + # from PyQt4.QtCore import * + # from PyQt4.QtGui import * + # from sys import argv,exit + # app = QApplication(argv) + # w = TestTableViewWidget.test_init() + # # w.show() + # app.exec_() diff --git a/tests/test_SupportedDtypes.py b/tests/test_SupportedDtypes.py index f22871c..4254d34 100644 --- a/tests/test_SupportedDtypes.py +++ b/tests/test_SupportedDtypes.py @@ -43,7 +43,7 @@ def test_description(self, expected_support, obj): for datatype in expected_support: assert obj.description(datatype) is not None - from StringIO import StringIO + from io import StringIO s = StringIO() assert obj.description(s) is None assert obj.description(str) is None diff --git a/tests/test_excepthook.py b/tests/test_excepthook.py index 9db8a1e..fa8049a 100644 --- a/tests/test_excepthook.py +++ b/tests/test_excepthook.py @@ -10,16 +10,16 @@ # TODO write it with pytest... def exception(): - raise ValueError, "Test Test ä" + raise ValueError("Test Test ä") def exception2(): - raise ValueError, u"Test Test ä" + raise ValueError("Test Test ä") def exception3(): - raise ValueError, u"Test Test" + raise ValueError("Test Test") def exception4(): - raise ValueError, "Test Test" + raise ValueError("Test Test") app = QtGui.QApplication([]) sys.excepthook = excepthook From e96718a9d109eb305a9a53b5cbf9df9eb44431e7 Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 14:54:48 -0500 Subject: [PATCH 15/27] Updates to README.md --- README.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 39ff489..9289933 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,17 @@ -# Utilities to use pandas (the data analysis / manipulation library for Python) with Qt. +# QtPandas -I forked this library because I liked the great detail and effort datalyze-solutions put into this package. -I have converted some of this to Python 3+ so it will at least install, but more work is needed. -I think it would be fun to bring this package back to life and get some upgrades put into it. +### Utilities to use pandas (the data analysis/manipulation library for Python) with Qt. +Requirements; +> Python 3.x +> Pandas 20.0 +> PyQt 4.7.8 -To install: - -pip install git+https://github.com/zbarge/QtPandas.git - +To install run the following in the command prompt; +``` +pip install git+https://github.com/drapja/QtPandas.git +pip install --upgrade git+https://github.com/robertlugg/easygui.git +``` To use, create a new Python script containing the following: ``` @@ -25,6 +28,16 @@ if __name__ == "__main__": dialog.show() app.exec_() ``` - -There are many more features but I don't have documentation. Maybe I'll add it sometime. - +Several examples can also be found in the tests directory. + +## TO DO: +- [ ] Add documentation. +- [ ] Add screen shots +- [ ] Create Wiki +- [ ] Make verison agnostic. +- [ ] Create specific Python version tests. +- [ ] Add Windows, Apple, and Linux tests. +- [ ] Consider adding functions seen in [Spyder IDE's dataframeeditor](https://github.com/spyder-ide/spyder/blob/f2b36f00f873cf4080087bfb529e6256b3e24792/spyder/widgets/variableexplorer/dataframeeditor.py) + - [ ] Sort + - [ ] Color coding +Forked from @zbarge's fork of @datalyze-solutions's [master](https://github.com/datalyze-solutions/pandas-qt). From 250a6a37ce1126a362248f8c980cbcc72d428d8a Mon Sep 17 00:00:00 2001 From: James Draper Date: Mon, 5 Dec 2016 16:16:07 -0500 Subject: [PATCH 16/27] Correction of a few small mistakes --- README.md | 2 +- tests/test_DataTableView.py | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index 9289933..d950f12 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ if __name__ == "__main__": dialog.show() app.exec_() ``` -Several examples can also be found in the tests directory. +Several examples can also be found in the exmples directory. ## TO DO: - [ ] Add documentation. diff --git a/tests/test_DataTableView.py b/tests/test_DataTableView.py index fa815dd..303e391 100644 --- a/tests/test_DataTableView.py +++ b/tests/test_DataTableView.py @@ -206,13 +206,3 @@ def test_removeColumns(self, qtbot, dataModel2): break assert widget.view().model().columnCount() == 0 - -if __name__ == "__main__": - TestTableViewWidget.Test_removeColumns() - # from PyQt4.QtCore import * - # from PyQt4.QtGui import * - # from sys import argv,exit - # app = QApplication(argv) - # w = TestTableViewWidget.test_init() - # # w.show() - # app.exec_() From aa3f3798361534bc5c0ac831f2a2edcf2e74d8e6 Mon Sep 17 00:00:00 2001 From: Zeke Date: Mon, 5 Dec 2016 13:47:46 -0800 Subject: [PATCH 17/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d950f12..3715cbd 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Requirements; To install run the following in the command prompt; ``` -pip install git+https://github.com/drapja/QtPandas.git +pip install git+https://github.com/zbarge/QtPandas.git pip install --upgrade git+https://github.com/robertlugg/easygui.git ``` From b5c320469c126842f20fc8403f77469fb34a701b Mon Sep 17 00:00:00 2001 From: Zeke Date: Mon, 5 Dec 2016 14:25:30 -0800 Subject: [PATCH 18/27] DataFrame Model can store the filepath it originated from now. --- pandasqt/models/DataFrameModel.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pandasqt/models/DataFrameModel.py b/pandasqt/models/DataFrameModel.py index cffc833..33acd80 100644 --- a/pandasqt/models/DataFrameModel.py +++ b/pandasqt/models/DataFrameModel.py @@ -65,7 +65,7 @@ class DataFrameModel(QtCore.QAbstractTableModel): dataChanged = Signal() dataFrameChanged = Signal() - def __init__(self, dataFrame=None, copyDataFrame=False): + def __init__(self, dataFrame=None, copyDataFrame=False, filePath=None): """the __init__ method. Args: @@ -74,6 +74,7 @@ def __init__(self, dataFrame=None, copyDataFrame=False): copyDataFrame (bool, optional): create a copy of dataFrame or use it as is. defaults to False. If you use it as is, you can change it from outside otherwise you have to reset the dataFrame after external changes. + filePath (str, optional): stores the original path for tracking. """ super(DataFrameModel, self).__init__() @@ -86,6 +87,11 @@ def __init__(self, dataFrame=None, copyDataFrame=False): self._dataFrameOriginal = None self._search = DataSearch("nothing", "") self.editable = False + self._filePath = filePath + + @property + def filePath(self): + return self._filePath def dataFrame(self): """getter function to _dataFrame. Holds all data. @@ -96,7 +102,7 @@ def dataFrame(self): """ return self._dataFrame - def setDataFrame(self, dataFrame, copyDataFrame=False): + def setDataFrame(self, dataFrame, copyDataFrame=False, filePath=None): """setter function to _dataFrame. Holds all data. Note: @@ -126,6 +132,8 @@ def setDataFrame(self, dataFrame, copyDataFrame=False): self._columnDtypeModel.changeFailed.connect( lambda columnName, index, dtype: self.changingDtypeFailed.emit(columnName, index, dtype) ) + if filePath is not None: + self._filePath = filePath self.layoutChanged.emit() self.dataChanged.emit() self.dataFrameChanged.emit() From 62639f562ab0ae858c5cffe40e609b1616a2e496 Mon Sep 17 00:00:00 2001 From: Zeke Date: Mon, 5 Dec 2016 14:26:42 -0800 Subject: [PATCH 19/27] CSVDialogs updated --- pandasqt/views/CSVDialogs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index b14de43..d39df0f 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -96,6 +96,7 @@ def _initUI(self): self.commaRadioButton.setChecked(True) self.otherSeparatorLineEdit = QtGui.QLineEdit(self) + #TODO: Enable this or add BAR radio and option. self.otherSeparatorLineEdit.setEnabled(False) self.semicolonRadioButton.toggled.connect(self._delimiter) @@ -406,7 +407,7 @@ def _previewFile(self): """ dataFrame = self._loadCSVDataFrame() - dataFrameModel = DataFrameModel(dataFrame) + dataFrameModel = DataFrameModel(dataFrame, filePath=self._filename) dataFrameModel.enableEditing(True) self._previewTableView.setModel(dataFrameModel) columnModel = dataFrameModel.columnDtypeModel() @@ -526,7 +527,7 @@ def _initUI(self): self._encodingLabel = QtGui.QLabel(u'File Encoding', self) - encoding_names = map(lambda x: x.upper(), sorted(list(set(_encodings.viewvalues())))) + encoding_names = list(map(lambda x: x.upper(), sorted(list(set(_encodings.values()))))) self._encodingComboBox = QtGui.QComboBox(self) self._encodingComboBox.addItems(encoding_names) self._idx = encoding_names.index('UTF_8') @@ -578,6 +579,8 @@ def setExportModel(self, model): @Slot() def _createFile(self): ret = QtGui.QFileDialog.getSaveFileName(self, 'Save File', filter='Comma Separated Value (*.csv)') + if isinstance(ret, tuple): + ret = ret[0] self._filenameLineEdit.setText(ret) def _saveModel(self): From f01548f80ffe5d729cd65b233293532ece931eeb Mon Sep 17 00:00:00 2001 From: Zeke Date: Mon, 5 Dec 2016 14:27:29 -0800 Subject: [PATCH 20/27] Dialogs started to manage any type of file --- pandasqt/views/MultiFileDialogs.py | 105 +++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 pandasqt/views/MultiFileDialogs.py diff --git a/pandasqt/views/MultiFileDialogs.py b/pandasqt/views/MultiFileDialogs.py new file mode 100644 index 0000000..765f8be --- /dev/null +++ b/pandasqt/views/MultiFileDialogs.py @@ -0,0 +1,105 @@ +import os +from pandasqt.compat import QtCore +from pandasqt.views.CSVDialogs import (CSVExportDialog, CSVImportDialog, + _calculateEncodingKey, Slot, + DataFrameModel) + + + +class DataFrameExportDialog(CSVExportDialog): + """ + Extends the CSVExportDialog with support for + exporting to .txt and .xlsx + """ + signalExportFilenames = QtCore.Signal(str, str) + signalModelChanged = QtCore.Signal(DataFrameModel) + + def __init__(self, model=None, parent=None): + CSVExportDialog.__init__(self, model=None, parent=None) + if model is not None: + self._filename = model.filePath + else: + self._filename = None + self._windowTitle = "Export Data" + self.setWindowTitle(self._windowTitle) + + @Slot(DataFrameModel) + def swapModel(self, model): + good = self.setExportModel(model) + if good: + self.signalModelChanged.emit(model) + + @Slot() + def accepted(self): + """Successfully close the widget and emit an export signal. + + This method is also a `SLOT`. + The dialog will be closed, when the `Export Data` button is + pressed. If errors occur during the export, the status bar + will show the error message and the dialog will not be closed. + + """ + #return super(DataFrameExportDialog, self).accepted + try: + self._saveModel() + except Exception as err: + self._statusBar.showMessage(str(err)) + raise + else: + self._resetWidgets() + self.exported.emit(True) + self.accept() + + @Slot() + def rejected(self): + """Close the widget and reset its inital state. + + This method is also a `SLOT`. + The dialog will be closed and all changes reverted, when the + `cancel` button is pressed. + + """ + self._resetWidgets() + self.exported.emit(False) + self.reject() + + def _saveModel(self): + """ + Reimplements _saveModel to utilize all of the + Pandas export options based on file extension. + :return: None + """ + delimiter = self._delimiterBox.currentSelected() + header = self._headerCheckBox.isChecked() # column labels + if self._filename is None: + filename = self._filenameLineEdit.text() + else: + filename = self._filename + ext = os.path.splitext(filename)[1].lower() + index = False # row labels + + encodingIndex = self._encodingComboBox.currentIndex() + encoding = self._encodingComboBox.itemText(encodingIndex) + encoding = _calculateEncodingKey(encoding.lower()) + + try: + dataFrame = self._model.dataFrame() + except AttributeError as err: + raise AttributeError('No data loaded to export.') + else: + print("Identifying export type for {}".format(filename)) + try: + if ext in ['.txt','.csv']: + dataFrame.to_csv(filename, encoding=encoding, header=header, index=index, sep=delimiter) + elif ext == '.tsv': + sep = '\t' + dataFrame.to_csv(filename, encoding=encoding, header=header, index=index, sep=delimiter) + elif ext in ['.xlsx','.xls']: + dataFrame.to_excel(filename, encoding=encoding, header=header, index=index, sep=delimiter) + except IOError as err: + raise IOError('No filename given') + except UnicodeError as err: + raise UnicodeError('Could not encode all data. Choose a different encoding') + except Exception: + raise + self.signalExportFilenames.emit(self._model._filePath, filename) From 37593b17df40cc4c245497fd3189015b59dd0419 Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 06:29:25 -0500 Subject: [PATCH 21/27] Added some checklist items --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3715cbd..33b0eaa 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,11 @@ if __name__ == "__main__": Several examples can also be found in the exmples directory. ## TO DO: +- [x] Reach out to @kaotika and @datalyze-solutions +- [ ] Secure qtpandas namespace on pip. +- [ ] Wait for reply + - [ ] If no reply create new repo for QtPandas. +- [ ] Create .travis.yaml file. - [ ] Add documentation. - [ ] Add screen shots - [ ] Create Wiki @@ -39,5 +44,6 @@ Several examples can also be found in the exmples directory. - [ ] Add Windows, Apple, and Linux tests. - [ ] Consider adding functions seen in [Spyder IDE's dataframeeditor](https://github.com/spyder-ide/spyder/blob/f2b36f00f873cf4080087bfb529e6256b3e24792/spyder/widgets/variableexplorer/dataframeeditor.py) - [ ] Sort - - [ ] Color coding -Forked from @zbarge's fork of @datalyze-solutions's [master](https://github.com/datalyze-solutions/pandas-qt). + - [ ] Color coding + +Forked from @datalyze-solutions's [master](https://github.com/datalyze-solutions/pandas-qt). From 7b302e0e66311e1963284b0272a156e33e1704ed Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 06:46:48 -0500 Subject: [PATCH 22/27] Trial travis --- .travis.yml | 28 ++++++++++++++++++++++++++++ README.md | 10 +++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4e4dc4e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +language: python +sudo: false + +env: + global: + - MINICONDA_VERSION="latest" + - MINICONDA_LINUX="Linux-x86_64" + - MINICONDA_OSX="MacOSX-x86_64" + +matrix: + include: + # Linux using pip packages + - python: "3.5" + env: USE_QT_API=PyQt5 USE_CONDA=false + os: linux + # Linux using conda packages + - python: "2.7" + env: USE_QT_API=PyQt4 USE_CONDA=true + os: linux + - python: "2.7" + env: USE_QT_API=PyQt5 USE_CONDA=true + os: linux + - python: "3.5" + env: USE_QT_API=PyQt4 USE_CONDA=true + os: linux + - python: "3.5" + env: USE_QT_API=PyQt5 USE_CONDA=true + os: linux diff --git a/README.md b/README.md index 33b0eaa..3047079 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,13 @@ if __name__ == "__main__": ``` Several examples can also be found in the exmples directory. -## TO DO: +# Development + +## Wanna contribute? + +Join us on [gitter](https://gitter.im/qtpandas/Lobby#) + +### TO DO: - [x] Reach out to @kaotika and @datalyze-solutions - [ ] Secure qtpandas namespace on pip. - [ ] Wait for reply @@ -46,4 +52,6 @@ Several examples can also be found in the exmples directory. - [ ] Sort - [ ] Color coding + + Forked from @datalyze-solutions's [master](https://github.com/datalyze-solutions/pandas-qt). From 8e5b725007f302a6e1eba0a1ac0613d23690a69b Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 06:53:48 -0500 Subject: [PATCH 23/27] small change --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3047079..88edb95 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ ### Utilities to use pandas (the data analysis/manipulation library for Python) with Qt. +## Build status +[![Travis status](https://travis-ci.org/draperjames/QtPandas)] + Requirements; > Python 3.x > Pandas 20.0 From c2e11786790579d8a30ffb19e55380b4429b5502 Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 09:03:53 -0500 Subject: [PATCH 24/27] fixed travis.yml --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 88edb95..994c316 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,13 @@ ### Utilities to use pandas (the data analysis/manipulation library for Python) with Qt. -## Build status -[![Travis status](https://travis-ci.org/draperjames/QtPandas)] +## Project Information + +[![Join the chat at https://gitter.im/qtpandas/Lobby#](https://badges.gitter.im/qtpandas/lobby.svg)]((https://gitter.im/qtpandas/Lobby#) + +## Build Status + +[![Travis status](https://travis-ci.org/draperjames/QtPandas.svg?branch=master)](https://travis-ci.org/draperjames/QtPandas) Requirements; > Python 3.x @@ -44,7 +49,8 @@ Join us on [gitter](https://gitter.im/qtpandas/Lobby#) - [ ] Secure qtpandas namespace on pip. - [ ] Wait for reply - [ ] If no reply create new repo for QtPandas. -- [ ] Create .travis.yaml file. +- [x] Create .travis.yml file. + - [x] integrate into README.md - [ ] Add documentation. - [ ] Add screen shots - [ ] Create Wiki @@ -56,5 +62,4 @@ Join us on [gitter](https://gitter.im/qtpandas/Lobby#) - [ ] Color coding - Forked from @datalyze-solutions's [master](https://github.com/datalyze-solutions/pandas-qt). From cabcbbf2975b9d66c1496399b2f2934c29b9dd00 Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 09:05:10 -0500 Subject: [PATCH 25/27] gitter chat link fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 994c316..d2bb6a5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Project Information -[![Join the chat at https://gitter.im/qtpandas/Lobby#](https://badges.gitter.im/qtpandas/lobby.svg)]((https://gitter.im/qtpandas/Lobby#) +[![Join the chat at https://gitter.im/qtpandas/Lobby#](https://badges.gitter.im/qtpandas/lobby.svg)](https://gitter.im/qtpandas/Lobby#) ## Build Status From 241f0ef76a1c43a7f5b6aede74c6af472c5cd3d0 Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 09:55:52 -0500 Subject: [PATCH 26/27] adding windows tests to travis --- .travis.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4e4dc4e..2dcb6ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,28 @@ env: matrix: include: + # Python3 + # Windows using pip packages + - python: "3.5" + env: USE_QT_API=PyQt5 USE_CONDA=false + os: windows + - python: "3.5" + env: USE_QT_API=PyQt4 USE_CONDA=false + os: windows # Linux using pip packages - python: "3.5" env: USE_QT_API=PyQt5 USE_CONDA=false os: linux + - python: "3.5" + env: USE_QT_API=PyQt4 USE_CONDA=false + os: linux + # Windows using conda packages + - python: "3.5" + env: USE_QT_API=PyQt5 USE_CONDA=true + os: windows + - python: "3.5" + env: USE_QT_API=PyQt4 USE_CONDA=true + os: windows # Linux using conda packages - python: "2.7" env: USE_QT_API=PyQt4 USE_CONDA=true From 8edde1addf725c2cf97bd7b9584ad955e487b69a Mon Sep 17 00:00:00 2001 From: James Draper Date: Tue, 6 Dec 2016 12:00:30 -0500 Subject: [PATCH 27/27] Fixed conflict. I must have overlooked this at some point. Everything seems okay now. --- pandasqt/views/CSVDialogs.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandasqt/views/CSVDialogs.py b/pandasqt/views/CSVDialogs.py index 6c162c1..6e21ae6 100644 --- a/pandasqt/views/CSVDialogs.py +++ b/pandasqt/views/CSVDialogs.py @@ -532,11 +532,9 @@ def _initUI(self): self._encodingLabel = QtGui.QLabel('File Encoding', self) -<<<<<<< HEAD + encoding_names = list(map(lambda x: x.upper(), sorted(list(set(_encodings.values()))))) -======= - encoding_names = [x.upper() for x in sorted(list(set(_encodings.values())))] ->>>>>>> aa3f3798361534bc5c0ac831f2a2edcf2e74d8e6 + self._encodingComboBox = QtGui.QComboBox(self) self._encodingComboBox.addItems(encoding_names) self._idx = encoding_names.index('UTF_8')