├── README.md ├── QTableView_pandas.py └── Q6TableView_pandas.py /README.md: -------------------------------------------------------------------------------- 1 | # QTableView_pandas 2 | 3 | ## csv Reader/Writer 4 | 5 | ### Requirements: 6 | 7 | - PyQt5 8 | - pandas 9 | 10 | Example for reading / writing tab delimited csv using pandas and QTableView 11 | -------------------------------------------------------------------------------- /QTableView_pandas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import csv, codecs 6 | import os 7 | import pandas as pd 8 | from PyQt5.QtCore import Qt, QDir, QItemSelectionModel, QAbstractTableModel, QModelIndex, QVariant, QSize, QSettings 9 | from PyQt5.QtWidgets import (QMainWindow, QTableView, QApplication, QToolBar, QLineEdit, QComboBox, QDialog, 10 | QAction, QMenu, QFileDialog, QAbstractItemView, QMessageBox, QWidget) 11 | from PyQt5.QtGui import QStandardItemModel, QStandardItem, QCursor, QIcon, QKeySequence, QTextDocument, QTextCursor, QTextTableFormat 12 | from PyQt5 import QtPrintSupport 13 | 14 | class PandasModel(QAbstractTableModel): 15 | def __init__(self, df = pd.DataFrame(), parent=None): 16 | QAbstractTableModel.__init__(self, parent=None) 17 | self._df = df 18 | self.setChanged = False 19 | self.dataChanged.connect(self.setModified) 20 | 21 | def setModified(self): 22 | self.setChanged = True 23 | print(self.setChanged) 24 | 25 | def headerData(self, section, orientation, role=Qt.DisplayRole): 26 | if role != Qt.DisplayRole: 27 | return QVariant() 28 | if orientation == Qt.Horizontal: 29 | try: 30 | return self._df.columns.tolist()[section] 31 | except (IndexError, ): 32 | return QVariant() 33 | elif orientation == Qt.Vertical: 34 | try: 35 | return self._df.index.tolist()[section] 36 | except (IndexError, ): 37 | return QVariant() 38 | 39 | def flags(self, index): 40 | return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable 41 | 42 | def data(self, index, role=Qt.DisplayRole): 43 | if index.isValid(): 44 | if (role == Qt.EditRole): 45 | return self._df.values[index.row()][index.column()] 46 | elif (role == Qt.DisplayRole): 47 | return self._df.values[index.row()][index.column()] 48 | return None 49 | 50 | def setData(self, index, value, role): 51 | row = self._df.index[index.row()] 52 | col = self._df.columns[index.column()] 53 | self._df.values[row][col] = value 54 | self.dataChanged.emit(index, index) 55 | return True 56 | 57 | def rowCount(self, parent=QModelIndex()): 58 | return len(self._df.index) 59 | 60 | def columnCount(self, parent=QModelIndex()): 61 | return len(self._df.columns) 62 | 63 | def sort(self, column, order): 64 | colname = self._df.columns.tolist()[column] 65 | self.layoutAboutToBeChanged.emit() 66 | self._df.sort_values(colname, ascending= order == Qt.AscendingOrder, inplace=True) 67 | self._df.reset_index(inplace=True, drop=True) 68 | self.layoutChanged.emit() 69 | 70 | class Viewer(QMainWindow): 71 | def __init__(self, parent=None): 72 | super(Viewer, self).__init__(parent) 73 | self.MaxRecentFiles = 5 74 | self.windowList = [] 75 | self.recentFiles = [] 76 | self.settings = QSettings('Axel Schneider', 'QTableViewPandas') 77 | self.filename = "" 78 | self.setGeometry(0, 0, 800, 600) 79 | self.lb = QTableView() 80 | self.lb.verticalHeader().setVisible(True) 81 | self.lb.setGridStyle(1) 82 | self.model = PandasModel() 83 | self.lb.setModel(self.model) 84 | self.lb.setEditTriggers(QAbstractItemView.DoubleClicked) 85 | self.lb.setSelectionBehavior(self.lb.SelectRows) 86 | self.lb.setSelectionMode(self.lb.SingleSelection) 87 | self.setStyleSheet(stylesheet(self)) 88 | self.lb.setAcceptDrops(True) 89 | self.setCentralWidget(self.lb) 90 | self.setContentsMargins(10, 10, 10, 10) 91 | self.createToolBar() 92 | self.readSettings() 93 | self.lb.setFocus() 94 | self.statusBar().showMessage("Ready", 0) 95 | 96 | def readSettings(self): 97 | print("reading settings") 98 | if self.settings.contains("geometry"): 99 | self.setGeometry(self.settings.value('geometry')) 100 | if self.settings.contains("recentFiles"): 101 | self.recentFiles = self.settings.value('recentFiles') 102 | self.lastFiles.addItem("last Files") 103 | self.lastFiles.addItems(self.recentFiles[:15]) 104 | 105 | def saveSettings(self): 106 | print("saving settings") 107 | self.settings.setValue('geometry', self.geometry()) 108 | self.settings.setValue('recentFiles', self.recentFiles) 109 | 110 | def closeEvent(self, event): 111 | print(self.model.setChanged) 112 | if self.model.setChanged == True: 113 | print("is changed, saving?") 114 | quit_msg = "The document was changed.
Do you want to save the changes?" 115 | reply = QMessageBox.question(self, 'Save Confirmation', 116 | quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) 117 | if reply == QMessageBox.Yes: 118 | self.writeCSV_update() 119 | else: 120 | print("not saved, goodbye ...") 121 | return 122 | else: 123 | print("nothing changed. goodbye") 124 | self.saveSettings() 125 | 126 | def createToolBar(self): 127 | openAction = QAction(QIcon.fromTheme("document-open"), "Open", self, triggered=self.loadCSV, shortcut = QKeySequence.Open) 128 | saveAction = QAction(QIcon.fromTheme("document-save"), "Save", self, triggered= self.writeCSV_update, shortcut = QKeySequence.Save) 129 | saveAsAction = QAction(QIcon.fromTheme("document-save-as"), "Save as ...", self, triggered=self.writeCSV, shortcut = QKeySequence.SaveAs) 130 | self.tbar = self.addToolBar("File") 131 | self.tbar.setContextMenuPolicy(Qt.PreventContextMenu) 132 | self.tbar.setIconSize(QSize(16, 16)) 133 | self.tbar.setMovable(False) 134 | self.tbar.addAction(openAction) 135 | self.tbar.addAction(saveAction) 136 | self.tbar.addAction(saveAsAction) 137 | 138 | empty = QWidget() 139 | empty.setFixedWidth(10) 140 | self.tbar.addWidget(empty) 141 | 142 | self.lastFiles = QComboBox() 143 | self.lastFiles.setFixedWidth(300) 144 | self.lastFiles.currentIndexChanged.connect(self.loadRecent) 145 | self.tbar.addWidget(self.lastFiles) 146 | 147 | empty = QWidget() 148 | empty.setFixedWidth(10) 149 | self.tbar.addWidget(empty) 150 | 151 | findbyText = QAction(QIcon.fromTheme("edit-find-symbolic"), "find", self, triggered = self.findInTable) 152 | self.lineFind = QLineEdit() 153 | self.lineFind.addAction(findbyText, 0) 154 | self.lineFind.setPlaceholderText("find") 155 | self.lineFind.setClearButtonEnabled(True) 156 | self.lineFind.setFixedWidth(250) 157 | self.lineFind.returnPressed.connect(self.findInTable) 158 | self.tbar.addWidget(self.lineFind) 159 | self.tbar.addAction(findbyText) 160 | 161 | empty = QWidget() 162 | empty.setFixedWidth(10) 163 | self.tbar.addWidget(empty) 164 | 165 | self.previewAction = QAction(QIcon.fromTheme("document-print-preview"), "print", self, triggered = self.handlePreview) 166 | self.tbar.addAction(self.previewAction) 167 | self.printAction = QAction(QIcon.fromTheme("document-print"), "print", self, triggered = self.handlePrint) 168 | self.tbar.addAction(self.printAction) 169 | 170 | def loadRecent(self): 171 | if self.lastFiles.currentIndex() > 0: 172 | print(self.lastFiles.currentText()) 173 | print(self.model.setChanged) 174 | if self.model.setChanged == True: 175 | print("is changed, saving?") 176 | quit_msg = "The document was changed.
Do you want to save the changes?" 177 | reply = QMessageBox.question(self, 'Save Confirmation', 178 | quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) 179 | if reply == QMessageBox.Yes: 180 | self.openCSV(self.lastFiles.currentText()) 181 | else: 182 | self.openCSV(self.lastFiles.currentText()) 183 | else: 184 | self.openCSV(self.lastFiles.currentText()) 185 | 186 | def openCSV(self, path): 187 | f = open(path, 'r+b') 188 | with f: 189 | df = pd.read_csv(f, delimiter = '\t', keep_default_na = False, low_memory=False, header=None) 190 | f.close() 191 | self.model = PandasModel(df) 192 | self.lb.setModel(main.model) 193 | self.lb.resizeColumnsToContents() 194 | self.lb.selectRow(0) 195 | self.statusBar().showMessage("%s %s" % (path, "loaded"), 0) 196 | 197 | def findInTable(self): 198 | self.lb.clearSelection() 199 | text = self.lineFind.text() 200 | model = self.lb.model() 201 | for column in range(self.model.columnCount()): 202 | start = model.index(0, column) 203 | matches = model.match(start, Qt.DisplayRole, text, -1, Qt.MatchContains) 204 | if matches: 205 | for index in matches: 206 | print(index.row(), index.column()) 207 | self.lb.selectionModel().select(index, QItemSelectionModel.Select) 208 | 209 | def openFile(self, path=None): 210 | print(self.model.setChanged) 211 | if self.model.setChanged == True: 212 | print("is changed, saving?") 213 | quit_msg = "The document was changed.
Do you want to save the changes?" 214 | reply = QMessageBox.question(self, 'Save Confirmation', 215 | quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) 216 | if reply == QMessageBox.Yes: 217 | self.writeCSV_update() 218 | else: 219 | print("not saved, loading ...") 220 | return 221 | path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Dokumente/CSV/","CSV Files (*.csv)") 222 | if path: 223 | return path 224 | 225 | def loadCSV(self): 226 | fileName = self.openFile() 227 | if fileName: 228 | print(fileName + " loaded") 229 | f = open(fileName, 'r+b') 230 | with f: 231 | df = pd.read_csv(f, delimiter = '\t', keep_default_na = False, low_memory=False, header=None) 232 | f.close() 233 | self.model = PandasModel(df) 234 | self.lb.setModel(self.model) 235 | self.lb.resizeColumnsToContents() 236 | self.lb.selectRow(0) 237 | self.statusBar().showMessage("%s %s" % (fileName, "loaded"), 0) 238 | self.recentFiles.insert(0, fileName) 239 | self.lastFiles.insertItem(1, fileName) 240 | 241 | def writeCSV(self): 242 | fileName, _ = QFileDialog.getSaveFileName(self, "Open File", self.filename,"CSV Files (*.csv)") 243 | if fileName: 244 | print(fileName + " saved") 245 | f = open(fileName, 'w') 246 | newModel = self.model 247 | dataFrame = newModel._df.copy() 248 | dataFrame.to_csv(f, sep='\t', index = False, header = False) 249 | 250 | def writeCSV_update(self): 251 | if self.filename: 252 | f = open(self.filename, 'w') 253 | newModel = self.model 254 | dataFrame = newModel._df.copy() 255 | dataFrame.to_csv(f, sep='\t', index = False, header = False) 256 | self.model.setChanged = False 257 | print("%s %s" % (self.filename, "saved")) 258 | self.statusBar().showMessage("%s %s" % (self.filename, "saved"), 0) 259 | 260 | def handlePrint(self): 261 | if self.model.rowCount() == 0: 262 | self.msg("no rows") 263 | else: 264 | dialog = QtPrintSupport.QPrintDialog() 265 | if dialog.exec_() == QDialog.Accepted: 266 | self.handlePaintRequest(dialog.printer()) 267 | print("Document printed") 268 | 269 | def handlePreview(self): 270 | if self.model.rowCount() == 0: 271 | self.msg("no rows") 272 | else: 273 | dialog = QtPrintSupport.QPrintPreviewDialog() 274 | dialog.setFixedSize(1000,700) 275 | dialog.paintRequested.connect(self.handlePaintRequest) 276 | dialog.exec_() 277 | print("Print Preview closed") 278 | 279 | def handlePaintRequest(self, printer): 280 | printer.setDocName(self.filename) 281 | document = QTextDocument() 282 | cursor = QTextCursor(document) 283 | model = self.lb.model() 284 | tableFormat = QTextTableFormat() 285 | tableFormat.setBorder(0.2) 286 | tableFormat.setBorderStyle(3) 287 | tableFormat.setCellSpacing(0); 288 | tableFormat.setTopMargin(0); 289 | tableFormat.setCellPadding(4) 290 | table = cursor.insertTable(model.rowCount() + 1, model.columnCount(), tableFormat) 291 | model = self.lb.model() 292 | ### get headers 293 | myheaders = [] 294 | for i in range(0, model.columnCount()): 295 | myheader = model.headerData(i, Qt.Horizontal) 296 | cursor.insertText(str(myheader)) 297 | cursor.movePosition(QTextCursor.NextCell) 298 | ### get cells 299 | for row in range(0, model.rowCount()): 300 | for col in range(0, model.columnCount()): 301 | index = model.index( row, col ) 302 | cursor.insertText(str(index.data())) 303 | cursor.movePosition(QTextCursor.NextCell) 304 | document.print_(printer) 305 | 306 | def stylesheet(self): 307 | return """ 308 | QMainWindow 309 | { 310 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 311 | stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, 312 | stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); 313 | } 314 | QMenuBar 315 | { 316 | background: transparent; 317 | border: 0px; 318 | } 319 | QTableView 320 | { 321 | background: qlineargradient(y1:0, y2:1, 322 | stop:0 #d3d7cf, stop:1 #ffffff); 323 | border: 1px solid #d3d7cf; 324 | border-radius: 0px; 325 | font-size: 8pt; 326 | selection-color: #ffffff 327 | } 328 | QTableView::item:hover 329 | { 330 | color: #eeeeec; 331 | background: #c4a000;; 332 | } 333 | 334 | 335 | QTableView::item:selected { 336 | color: #F4F4F4; 337 | background: qlineargradient(y1:0, y2:1, 338 | stop:0 #2a82da, stop:1 #1f3c5d); 339 | } 340 | 341 | QTableView QTableCornerButton::section { 342 | background: transparent; 343 | border: 0px outset black; 344 | } 345 | QHeaderView 346 | { 347 | background: qlineargradient( y1: 0, y2: 1, 348 | stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, 349 | stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); 350 | color: #888a85; 351 | } 352 | 353 | QToolBar 354 | { 355 | background: transparent; 356 | border: 0px; 357 | } 358 | QStatusBar 359 | { 360 | background: transparent; 361 | border: 0px; 362 | color: #555753; 363 | font-size: 7pt; 364 | } 365 | 366 | """ 367 | 368 | if __name__ == "__main__": 369 | app = QApplication(sys.argv) 370 | main = Viewer() 371 | main.show() 372 | if len(sys.argv) > 1: 373 | main.openCSV(sys.argv[1]) 374 | sys.exit(app.exec_()) 375 | -------------------------------------------------------------------------------- /Q6TableView_pandas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import csv, codecs 6 | import os 7 | import pandas as pd 8 | from PyQt6.QtCore import (Qt, QDir, QItemSelectionModel, QAbstractTableModel, 9 | QModelIndex, QVariant, QSize, QSettings) 10 | from PyQt6.QtWidgets import (QMainWindow, QTableView, QApplication, QToolBar, QLineEdit, QComboBox, QDialog, 11 | QMenu, QFileDialog, QAbstractItemView, QMessageBox, QWidget) 12 | from PyQt6.QtGui import (QStandardItemModel, QStandardItem, QCursor, QIcon, QAction, 13 | QKeySequence, QTextDocument, QTextCursor, QTextTableFormat) 14 | from PyQt6 import QtPrintSupport 15 | 16 | class PandasModel(QAbstractTableModel): 17 | def __init__(self, df = pd.DataFrame(), parent=None): 18 | QAbstractTableModel.__init__(self, parent=None) 19 | self._df = df 20 | self.setChanged = False 21 | self.dataChanged.connect(self.setModified) 22 | 23 | def setModified(self): 24 | self.setChanged = True 25 | print(self.setChanged) 26 | 27 | def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole): 28 | if role != Qt.ItemDataRole.DisplayRole: 29 | return QVariant() 30 | if orientation == Qt.Orientation.Horizontal: 31 | try: 32 | return self._df.columns.tolist()[section] 33 | except (IndexError, ): 34 | return QVariant() 35 | elif orientation == Qt.Orientation.Vertical: 36 | try: 37 | return self._df.index.tolist()[section] 38 | except (IndexError, ): 39 | return QVariant() 40 | 41 | def flags(self, index): 42 | return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable 43 | 44 | def data(self, index, role=Qt.ItemDataRole.DisplayRole): 45 | if index.isValid(): 46 | if (role == Qt.ItemDataRole.EditRole): 47 | return self._df.values[index.row()][index.column()] 48 | elif (role == Qt.ItemDataRole.DisplayRole): 49 | return self._df.values[index.row()][index.column()] 50 | return None 51 | 52 | def setData(self, index, value, role): 53 | row = self._df.index[index.row()] 54 | col = self._df.columns[index.column()] 55 | self._df.values[row][col] = value 56 | self.dataChanged.emit(index, index) 57 | return True 58 | 59 | def rowCount(self, parent=QModelIndex()): 60 | return len(self._df.index) 61 | 62 | def columnCount(self, parent=QModelIndex()): 63 | return len(self._df.columns) 64 | 65 | def sort(self, column, order): 66 | colname = self._df.columns.tolist()[column] 67 | self.layoutAboutToBeChanged.emit() 68 | self._df.sort_values(colname, ascending= order == Qt.AscendingOrder, inplace=True) 69 | self._df.reset_index(inplace=True, drop=True) 70 | self.layoutChanged.emit() 71 | 72 | class Viewer(QMainWindow): 73 | def __init__(self, parent=None): 74 | super(Viewer, self).__init__(parent) 75 | self.MaxRecentFiles = 5 76 | self.windowList = [] 77 | self.recentFiles = [] 78 | self.settings = QSettings('Axel Schneider', 'QTableViewPandas') 79 | self.filename = "" 80 | self.setGeometry(0, 0, 800, 600) 81 | self.lb = QTableView() 82 | self.lb.verticalHeader().setVisible(True) 83 | self.lb.setGridStyle(Qt.PenStyle.SolidLine) 84 | self.model = PandasModel() 85 | self.lb.setModel(self.model) 86 | self.lb.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked) 87 | self.lb.setSelectionBehavior(self.lb.SelectionBehavior.SelectRows) 88 | self.lb.setSelectionMode(self.lb.SelectionMode.SingleSelection) 89 | self.setStyleSheet(stylesheet(self)) 90 | self.lb.setAcceptDrops(True) 91 | self.setCentralWidget(self.lb) 92 | self.setContentsMargins(10, 10, 10, 10) 93 | self.createToolBar() 94 | self.readSettings() 95 | self.lb.setFocus() 96 | self.statusBar().showMessage("Ready", 0) 97 | 98 | def readSettings(self): 99 | print("reading settings") 100 | if self.settings.contains("geometry"): 101 | self.setGeometry(self.settings.value('geometry')) 102 | if self.settings.contains("recentFiles"): 103 | self.recentFiles = self.settings.value('recentFiles') 104 | self.lastFiles.addItem("last Files") 105 | self.lastFiles.addItems(self.recentFiles[:15]) 106 | 107 | def saveSettings(self): 108 | print("saving settings") 109 | self.settings.setValue('geometry', self.geometry()) 110 | self.settings.setValue('recentFiles', self.recentFiles) 111 | 112 | def closeEvent(self, event): 113 | print(self.model.setChanged) 114 | if self.model.setChanged == True: 115 | print("is changed, saving?") 116 | quit_msg = "The document was changed.
Do you want to save the changes?" 117 | reply = QMessageBox.question(self, 'Save Confirmation', 118 | quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) 119 | if reply == QMessageBox.Yes: 120 | self.writeCSV_update() 121 | else: 122 | print("not saved, goodbye ...") 123 | return 124 | else: 125 | print("nothing changed. goodbye") 126 | self.saveSettings() 127 | 128 | def createToolBar(self): 129 | openAction = QAction(QIcon.fromTheme("document-open"), "Open", self, triggered=self.loadCSV, shortcut = QKeySequence.StandardKey.Open) 130 | saveAction = QAction(QIcon.fromTheme("document-save"), "Save", self, triggered= self.writeCSV_update, shortcut = QKeySequence.StandardKey.Save) 131 | saveAsAction = QAction(QIcon.fromTheme("document-save-as"), "Save as ...", self, triggered=self.writeCSV, shortcut = QKeySequence.StandardKey.SaveAs) 132 | self.tbar = self.addToolBar("File") 133 | self.tbar.setContextMenuPolicy(Qt.ContextMenuPolicy.PreventContextMenu) 134 | self.tbar.setIconSize(QSize(16, 16)) 135 | self.tbar.setMovable(False) 136 | self.tbar.addAction(openAction) 137 | self.tbar.addAction(saveAction) 138 | self.tbar.addAction(saveAsAction) 139 | 140 | empty = QWidget() 141 | empty.setFixedWidth(10) 142 | self.tbar.addWidget(empty) 143 | 144 | self.lastFiles = QComboBox() 145 | self.lastFiles.setFixedWidth(300) 146 | self.lastFiles.currentIndexChanged.connect(self.loadRecent) 147 | self.tbar.addWidget(self.lastFiles) 148 | 149 | empty = QWidget() 150 | empty.setFixedWidth(10) 151 | self.tbar.addWidget(empty) 152 | 153 | findbyText = QAction(QIcon.fromTheme("edit-find-symbolic"), "find", self, triggered = self.findInTable) 154 | self.lineFind = QLineEdit() 155 | self.lineFind.addAction(findbyText, QLineEdit.ActionPosition.LeadingPosition) 156 | self.lineFind.setPlaceholderText("find") 157 | self.lineFind.setClearButtonEnabled(True) 158 | self.lineFind.setFixedWidth(250) 159 | self.lineFind.returnPressed.connect(self.findInTable) 160 | self.tbar.addWidget(self.lineFind) 161 | self.tbar.addAction(findbyText) 162 | 163 | empty = QWidget() 164 | empty.setFixedWidth(10) 165 | self.tbar.addWidget(empty) 166 | 167 | self.previewAction = QAction(QIcon.fromTheme("document-print-preview"), "print", self, triggered = self.handlePreview) 168 | self.tbar.addAction(self.previewAction) 169 | self.printAction = QAction(QIcon.fromTheme("document-print"), "print", self, triggered = self.handlePrint) 170 | self.tbar.addAction(self.printAction) 171 | 172 | def loadRecent(self): 173 | if self.lastFiles.currentIndex() > 0: 174 | print(self.lastFiles.currentText()) 175 | print(self.model.setChanged) 176 | if self.model.setChanged == True: 177 | print("is changed, saving?") 178 | quit_msg = "The document was changed.
Do you want to save the changes?" 179 | reply = QMessageBox.question(self, 'Save Confirmation', 180 | quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) 181 | if reply == QMessageBox.Yes: 182 | self.openCSV(self.lastFiles.currentText()) 183 | else: 184 | self.openCSV(self.lastFiles.currentText()) 185 | else: 186 | self.openCSV(self.lastFiles.currentText()) 187 | 188 | def openCSV(self, path): 189 | f = open(path, 'r+b') 190 | with f: 191 | df = pd.read_csv(f, delimiter = '\t', keep_default_na = False, low_memory=False, header=None) 192 | f.close() 193 | self.model = PandasModel(df) 194 | self.lb.setModel(main.model) 195 | self.lb.resizeColumnsToContents() 196 | self.lb.selectRow(0) 197 | self.statusBar().showMessage("%s %s" % (path, "loaded"), 0) 198 | 199 | def findInTable(self): 200 | self.lb.clearSelection() 201 | text = self.lineFind.text() 202 | model = self.lb.model() 203 | for column in range(self.model.columnCount()): 204 | start = model.index(0, column) 205 | matches = model.match(start, Qt.ItemDataRole.DisplayRole, text, -1, Qt.MatchFlag.MatchContains) 206 | if matches: 207 | for index in matches: 208 | self.lb.selectionModel().select(index, QItemSelectionModel.SelectionFlag.Select) 209 | 210 | def openFile(self, path=None): 211 | print(self.model.setChanged) 212 | if self.model.setChanged == True: 213 | print("is changed, saving?") 214 | quit_msg = "The document was changed.
Do you want to save the changes?" 215 | reply = QMessageBox.question(self, 'Save Confirmation', 216 | quit_msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) 217 | if reply == QMessageBox.Yes: 218 | self.writeCSV_update() 219 | else: 220 | print("not saved, loading ...") 221 | return 222 | path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Dokumente/CSV/","CSV Files (*.csv)") 223 | if path: 224 | return path 225 | 226 | def loadCSV(self): 227 | fileName = self.openFile() 228 | if fileName: 229 | print(fileName + " loaded") 230 | f = open(fileName, 'r+b') 231 | with f: 232 | df = pd.read_csv(f, delimiter = '\t', keep_default_na = False, low_memory=False, header=None) 233 | f.close() 234 | self.model = PandasModel(df) 235 | self.lb.setModel(self.model) 236 | self.lb.resizeColumnsToContents() 237 | self.lb.selectRow(0) 238 | self.statusBar().showMessage("%s %s" % (fileName, "loaded"), 0) 239 | self.recentFiles.insert(0, fileName) 240 | self.lastFiles.insertItem(1, fileName) 241 | 242 | def writeCSV(self): 243 | fileName, _ = QFileDialog.getSaveFileName(self, "Open File", self.filename,"CSV Files (*.csv)") 244 | if fileName: 245 | print(fileName + " saved") 246 | f = open(fileName, 'w') 247 | newModel = self.model 248 | dataFrame = newModel._df.copy() 249 | dataFrame.to_csv(f, sep='\t', index = False, header = False) 250 | 251 | def writeCSV_update(self): 252 | if self.filename: 253 | f = open(self.filename, 'w') 254 | newModel = self.model 255 | dataFrame = newModel._df.copy() 256 | dataFrame.to_csv(f, sep='\t', index = False, header = False) 257 | self.model.setChanged = False 258 | print("%s %s" % (self.filename, "saved")) 259 | self.statusBar().showMessage("%s %s" % (self.filename, "saved"), 0) 260 | 261 | def handlePrint(self): 262 | if self.model.rowCount() == 0: 263 | self.msg("no rows") 264 | else: 265 | dialog = QtPrintSupport.QPrintDialog() 266 | if dialog.exec() == QDialog.Accepted: 267 | self.handlePaintRequest(dialog.printer()) 268 | print("Document printed") 269 | 270 | def handlePreview(self): 271 | if self.model.rowCount() == 0: 272 | self.msg("no rows") 273 | else: 274 | dialog = QtPrintSupport.QPrintPreviewDialog() 275 | dialog.setFixedSize(1000,700) 276 | dialog.paintRequested.connect(self.handlePaintRequest) 277 | dialog.exec() 278 | print("Print Preview closed") 279 | 280 | def handlePaintRequest(self, printer): 281 | printer.setDocName(self.filename) 282 | document = QTextDocument() 283 | cursor = QTextCursor(document) 284 | model = self.lb.model() 285 | tableFormat = QTextTableFormat() 286 | tableFormat.setBorder(0.2) 287 | tableFormat.setBorderStyle(QTextTableFormat.BorderStyle.BorderStyle_Solid) 288 | tableFormat.setCellSpacing(0); 289 | tableFormat.setTopMargin(0); 290 | tableFormat.setCellPadding(4) 291 | table = cursor.insertTable(model.rowCount() + 1, model.columnCount(), tableFormat) 292 | model = self.lb.model() 293 | ### get headers 294 | myheaders = [] 295 | for i in range(0, model.columnCount()): 296 | myheader = model.headerData(i, Qt.Orientation.Horizontal) 297 | cursor.insertText(str(myheader)) 298 | cursor.movePosition(QTextCursor.MoveOperation.NextCell) 299 | ### get cells 300 | for row in range(0, model.rowCount()): 301 | for col in range(0, model.columnCount()): 302 | index = model.index( row, col ) 303 | cursor.insertText(str(index.data())) 304 | cursor.movePosition(QTextCursor.MoveOperation.NextCell) 305 | document.print(printer) 306 | 307 | def stylesheet(self): 308 | return """ 309 | QMainWindow 310 | { 311 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 312 | stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, 313 | stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); 314 | } 315 | QMenuBar 316 | { 317 | background: transparent; 318 | border: 0px; 319 | } 320 | QTableView 321 | { 322 | background: qlineargradient(y1:0, y2:1, 323 | stop:0 #d3d7cf, stop:1 #ffffff); 324 | border: 1px solid #d3d7cf; 325 | border-radius: 0px; 326 | font-size: 8pt; 327 | selection-color: #ffffff 328 | } 329 | QTableView::item:hover 330 | { 331 | color: #eeeeec; 332 | background: #c4a000;; 333 | } 334 | 335 | 336 | QTableView::item:selected { 337 | color: #F4F4F4; 338 | background: qlineargradient(y1:0, y2:1, 339 | stop:0 #2a82da, stop:1 #1f3c5d); 340 | } 341 | 342 | QTableView QTableCornerButton::section { 343 | background: transparent; 344 | border: 0px outset black; 345 | } 346 | QHeaderView 347 | { 348 | background: qlineargradient( y1: 0, y2: 1, 349 | stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, 350 | stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); 351 | color: #888a85; 352 | } 353 | 354 | QToolBar 355 | { 356 | background: transparent; 357 | border: 0px; 358 | } 359 | QStatusBar 360 | { 361 | background: transparent; 362 | border: 0px; 363 | color: #555753; 364 | font-size: 7pt; 365 | } 366 | 367 | """ 368 | 369 | if __name__ == "__main__": 370 | app = QApplication(sys.argv) 371 | main = Viewer() 372 | main.show() 373 | if len(sys.argv) > 1: 374 | main.openCSV(sys.argv[1]) 375 | sys.exit(app.exec()) 376 | --------------------------------------------------------------------------------