Как в python сохранить данные из "tableView" в dbf файл?

Рабочий вариант сохранения в dbf файл

При нажатии на "pushButton", открывается диалоговое окно для выбора dbf файла, он загружается в "tableView", выполняются различные подсчёты, и нужно итоговую таблицу из "tableView" сохранить в dbf файл. Как из "tableView" сохранить в dbf файл?

import sys
from PyQt5 import QtWidgets, uic
from PyQt5.QtWidgets import QTableView, QPushButton, QVBoxLayout, QWidget, QFileDialog, QMessageBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from dbfread import DBF
import pandas as pd
import os
import dbf


class ExcelViewer(QtWidgets.QMainWindow):
    def __init__(self):
        super(ExcelViewer, self).__init__()
        uic.loadUi('main.ui', self)

        self.model = QStandardItemModel(self)
        self.table_view = self.findChild(QTableView, 'tableView')
        self.table_view.setModel(self.model)

        self.button = self.findChild(QPushButton, 'pushButton')
        self.button.clicked.connect(self.load_dbf_data)
        self.skip_rows = 0 

    def load_dbf_data(self):
        options = QFileDialog.Options()
        filenames, _ = QFileDialog.getOpenFileNames(self, "Выберите файлы", "", "Dbf Files (*.dbf)", options=options)
        if filenames:
            self.model.clear()
            total_sum = 0
            for i, filename in enumerate(filenames):
                dbf = DBF(filename)
                df = pd.DataFrame(iter(dbf))

                if i != 0:
                    df = df.iloc[self.skip_rows:, :]
                if i == 0:  
                    df = df.iloc[:-1, :]  
                else:  
                    df = df.iloc[6:-1, :]  
                    self.skip_rows = 6  

                for _, row in df.iterrows():
                    items = [
                       QStandardItem(str(row[col]))
                       for col in df.columns
                    ]
                    self.model.appendRow(items)
                    if len(row) > 5:
                        value = pd.to_numeric(row[5], errors='coerce')
                        if not pd.isnull(value):
                            total_sum += value

            for i in range(6, self.model.rowCount()):
                self.model.setItem(i, 0, QStandardItem(str(i-5)))

            total_sum = round(total_sum, 2) 
            self.model.appendRow([
                QStandardItem(''),
                QStandardItem('ИТОГО ЗАЧИСЛЕНИЙ: ' + str(self.model.rowCount() - 1)),
                QStandardItem('ИТОГО СУММА: '),
                QStandardItem(''),
                QStandardItem(''),
                QStandardItem('' + format(total_sum, ',').replace(",", " "))
            ])

            self.save_dbf_data()  
            
    def save_dbf_data(self):
         options = QFileDialog.Options()
         save_filename, _ = QFileDialog.getSaveFileName(self, "Сохранить как", "Новый_файл.dbf", "Dbf Files (*.dbf)", options=options)
         if save_filename:
            if self.model.rowCount() == 0:
               QMessageBox.warning(self, "Внимание", "Нет данных для сохранения.")
               return
            try:
                
            # Определение полей таблицы dbf
               field_specs = '; '.join([f'{chr(97 + i)} C({max([len(self.model.item(row, i).text()) for row in range(self.model.rowCount()) if self.model.item(row, i) is not None])})' for i in range(self.model.columnCount())])

               with dbf.Table(save_filename, field_specs=field_specs, codepage='cp866') as db:
                   db.open(mode=dbf.READ_WRITE)
                   for row in range(self.model.rowCount()):
                       db.append(tuple([self.model.item(row, col).text() for col in range(self.model.columnCount()) if self.model.item(row, col) is not None]))

               QMessageBox.information(self, "Сохранение", "Данные успешно сохранены в файл " + save_filename)
            except Exception as e:
               QMessageBox.critical(self, "Ошибка", "Не удалось сохранить данные: " + str(e))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    viewer = ExcelViewer()
    viewer.show()
    sys.exit(app.exec_()) 

Ответы (1 шт):

Автор решения: GxdTxnz

Ну, для начала вам нужно эти самые данные достать из tableView, а потом засунуть в DBF. Можно заимпортить библиотеку dbf и реализовать функцию вот так:

import dbf

def save_dbf_data(self):
    save_filename, _ = QFileDialog.getSaveFileName(self, "Сохранить как", "", "Dbf Files (*.dbf)")
    if save_filename:
        try:
            with dbf.Table(save_filename, 'w') as db:
                for row in range(self.model.rowCount()):
                    db.insert([self.model.item(row, col).text() for col in range(self.model.columnCount())])
            QMessageBox.information(self, "Сохранение", "Данные успешно сохранены в файл ")
        except Exception as e:
            QMessageBox.critical(self, "Ошибка", "Не удалось сохранить данные: " + str(e))

Можно еще добавить проверку на наличие данных, чтобы случайно пустой файл не сохранить и добавить использование переменной options.

import dbf

def save_dbf_data(self):
    options = QFileDialog.Options()
    save_filename, _ = QFileDialog.getSaveFileName(self, "Сохранить как", "", "Dbf Files (*.dbf)", options=options)
    if save_filename:
        if self.model.rowCount() == 0:
            QMessageBox.warning(self, "Внимание", "Нет данных для сохранения.")
            return
# остальная часть функции

UPD 1

В качестве решения новых проблем, можем организовать функцию след. образом:

import dbf
from PyQt5.QtWidgets import QFileDialog, QMessageBox

def save_dbf_data(self):
    save_filename, _ = QFileDialog.getSaveFileName(self, "Сохранить как", "", "Dbf Files (*.dbf)")
    if save_filename:
        try:
            # Определение полей таблицы dbf
            field_names = [self.model.horizontalHeaderItem(i).text() for i in range(self.model.columnCount())]
            field_types = 'C' * self.model.columnCount()  # Все поля будут строковыми

            with dbf.Table(save_filename, field_specs=field_types) as db:
                db.open(mode=dbf.READ_WRITE)
                for row in range(self.model.rowCount()):
                    db.append([self.model.item(row, col).text() for col in range(self.model.columnCount())])

            QMessageBox.information(self, "Сохранение", "Данные успешно сохранены в файл " + save_filename)
        except Exception as e:
            QMessageBox.critical(self, "Ошибка", "Не удалось сохранить данные: " + str(e))

Ну и плюсом можно добавить проверку на наличие данных и использование переменной options, как я описывал до этого.

UPD 2

проверка на наличие элемента и значения текста элемента в каждой ячейке:

import dbf
from PyQt5.QtWidgets import QFileDialog, QMessageBox

def save_dbf_data(self):
    save_filename, _ = QFileDialog.getSaveFileName(self, "Сохранить как", "", "Dbf Files (*.dbf)")
    if save_filename:
        try:
            # Определение полей таблицы dbf
            field_names = [self.model.horizontalHeaderItem(i).text() for i in range(self.model.columnCount())]
            field_types = 'C' * self.model.columnCount()  # Все поля будут строковыми

            with dbf.Table(save_filename, zip(field_names, field_types)) as db:
                    db.open(mode=dbf.READ_WRITE)
                    for row in range(self.model.rowCount()):
                        row_data = []
                        for col in range(self.model.columnCount()):
                            item = self.model.item(row, col)
                            if item is None or item.text() is None:
                                row_data.append('')
                            else:
                                row_data.append(item.text())
                        db.append(row_data)

                QMessageBox.information(self, "Сохранение", "Данные успешно сохранены в файл " + save_filename)
            except Exception as e:
                QMessageBox.critical(self, "Ошибка", "Не удалось сохранить данные: " + str(e))
→ Ссылка