首页 > 解决方案 > 用于打印熊猫数据框的 GUI

问题描述

我有 10 个 CSV 文件,每个 CSV 文件都有相同数量的列,我以 pandas 数据框的形式一一读取数据。我希望这些数据显示在窗口中或以某种表格形式显示。它应该就像每次数据进入新行时一样。对此有什么建议吗?

下面是我的示例 CSV 文件: 在此处输入图像描述

像这样,有 10 个或更多 CSV 文件,我将从这些文件中一一读取数据并希望在 GUI 中显示。

我的申请简介

我有一台机器在一定时间间隔后将 CSV 文件生成到文件夹中。我正在使用 Watchdog 库监视生成 CSV 文件的文件夹。当我收到一个 CSV 文件时,我将它读入 pandas 数据框。上面给出了示例 CSV 文件。

只要机器正在运行,它将继续生成 CSV 文件。因此,如果我想查看需要打开每个 CSV 文件的数据,相反,我想要一个视图,当生成新的 CSV 文件时,数据会在其中更新。

所以从技术上讲,一个 CSV 文件正在被读取并转换为数据框,然后插入到某种表格视图中。当生成一个新的 CSV 文件时,这个过程会再次发生,但是现在数据应该保存在同一个 Table View 的下一行中。

这是我的主要文件:

import time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import pandas as pd
from Append_Function import append_df_to_excel
import os.path
import sys

class Watcher:
    def __init__(self, args):
        self.watch_dir = os.getcwd()
        print(args[0])
        self.directory_to_watch = os.path.join(self.watch_dir, args[1])
        self.observer = Observer()
        self.event_handler = Handler(patterns=["*.CSV"], ignore_patterns=["*.tmp"], ignore_directories=True)

    def run(self):
        self.observer.schedule(self.event_handler, self.directory_to_watch, recursive=False)
        self.observer.start()
        try:
            while True:
                time.sleep(1)
        except:
            self.observer.stop()
            print("Error")

        self.observer.join()


class Handler(PatternMatchingEventHandler):
    @staticmethod
    def on_any_event(event):
        if event.is_directory:
            return None
        elif event.event_type == 'created':
            # Take any action here when a file is first created.
            print("Received created event - %s." % event.src_path)
            df = pd.read_csv(event.src_path, header=1, index_col=0)
            append_df_to_excel(os.path.join(os.getcwd(), "myfile.xlsx"), df)
        elif event.event_type == 'modified':
            # Taken any actionc here when a file is modified.
            df = pd.read_csv(event.src_path, header=0, index_col=0)
            append_df_to_excel(os.path.join(os.getcwd(), "myfile.xlsx"), df)
            print("Received modified event - %s." % event.src_path)


if __name__ == '__main__':
    print(sys.argv)
    w = Watcher(sys.argv)
    w.run()

这是我的附加功能:

import pandas as pd
import openpyxl as ox


def append_df_to_excel(filename, df, sheet_name='Sheet1', startrow=None,
                       truncate_sheet=False,
                       **to_excel_kwargs):
    # ignore [engine] parameter if it was passed

    if 'engine' in to_excel_kwargs:
        to_excel_kwargs.pop('engine')

    writer = pd.ExcelWriter(filename, engine='openpyxl')

    # Python 2.x: define [FileNotFoundError] exception if it doesn't exist
    try:
        FileNotFoundError
    except NameError:
        FileNotFoundError = IOError

    try:
        # try to open an existing workbook
        writer.book = ox.load_workbook(filename,keep_vba=True)

        # get the last row in the existing Excel sheet
        # if it was not specified explicitly
        if startrow is None and sheet_name in writer.book.sheetnames:
            startrow = writer.book[sheet_name].max_row

        # truncate sheet
        if truncate_sheet and sheet_name in writer.book.sheetnames:
            # index of [sheet_name] sheet
            idx = writer.book.sheetnames.index(sheet_name)
            # remove [sheet_name]
            writer.book.remove(writer.book.worksheets[idx])
            # create an empty sheet [sheet_name] using old index
            writer.book.create_sheet(sheet_name, idx)

        # copy existing sheets
        writer.sheets = {ws.title: ws for ws in writer.book.worksheets}
    except FileNotFoundError:
        # file does not exist yet, we will create it
        pass

    if startrow is None:
        startrow = 0

    # write out the new sheet
    df.to_excel(writer, sheet_name, startrow=startrow, **to_excel_kwargs, header=True)

    # save the workbook
    writer.save()

标签: pythonpython-3.xpandaspyqtpyqt5

解决方案


您必须通过循环添加数据框:

import pandas as pd
from PyQt5 import QtCore, QtWidgets

class DataFrameTableWidget(QtWidgets.QTableWidget):
    def append_dataframe(self, df):
        df = df.copy()
        if df.columns.size > self.columnCount():
            self.setColumnCount(df.columns.size)
        r = self.rowCount()
        self.insertRow(r)
        for c, column in enumerate(df):
            it = QtWidgets.QTableWidgetItem(column)
            self.setItem(r, c, it)
        i = self.rowCount()
        for r, row in df.iterrows():
            self.insertRow(self.rowCount())
            for c, (column, value) in enumerate(row.iteritems()):
                it = QtWidgets.QTableWidgetItem(str(value))
                self.setItem(i+r , c, it)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    import numpy as np
    w = DataFrameTableWidget()
    df = pd.DataFrame(np.random.randint(0, 100,size=(4, 4)), columns=list('ABCD'))
    w.append_dataframe(df)

    def after_show():
        df = pd.DataFrame(np.random.randint(0, 100,size=(4, 4)), columns=list('ABCD'))
        w.append_dataframe(df)
    QtCore.QTimer.singleShot(2*1000, after_show)
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

更新:

观察者在另一个线程上运行,因此它无法从该线程更新 GUI,因此必须使用信号来传输信息:

import os
import time
import pandas as pd
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
from PyQt5 import QtCore, QtWidgets

from Append_Function import append_df_to_excel

class Emitter(QtCore.QObject):
    newDataFrameSignal = QtCore.pyqtSignal(pd.DataFrame)

class Watcher:
    def __init__(self, filename):
        self.watch_dir = os.getcwd()
        self.directory_to_watch = os.path.join(self.watch_dir, filename)
        self.emitter = Emitter()
        self.observer = Observer()
        self.event_handler = Handler(
            emitter=self.emitter,
            patterns=["*.CSV"], 
            ignore_patterns=["*.tmp"], 
            ignore_directories=True
        )

    def run(self):
        self.observer.schedule(self.event_handler, self.directory_to_watch, recursive=False)
        self.observer.start()


class Handler(PatternMatchingEventHandler):
    def __init__(self, *args, emitter=None, **kwargs):
        super(Handler, self).__init__(*args, **kwargs)
        self._emitter = emitter
    def on_any_event(self, event):
        if event.is_directory:
            return None
        elif event.event_type == 'created':
            # Take any action here when a file is first created.
            print("Received created event - %s." % event.src_path)
            df = pd.read_csv(event.src_path, header=1)
            self._emitter.newDataFrameSignal.emit(df.copy())
            df.set_index(df.columns.values.tolist()[0], inplace=True)
            append_df_to_excel(os.path.join(os.getcwd(), "myfile.xlsx"), df)
        elif event.event_type == 'modified':
            # Taken any actionc here when a file is modified.
            df = pd.read_csv(event.src_path, header=1)
            self._emitter.newDataFrameSignal.emit(df.copy())
            df.set_index(df.columns.values.tolist()[0], inplace=True)
            append_df_to_excel(os.path.join(os.getcwd(), "myfile.xlsx"), df)
            print("Received modified event - %s." % event.src_path)

class DataFrameTableWidget(QtWidgets.QTableWidget):
    @QtCore.pyqtSlot(pd.DataFrame)
    def append_dataframe(self, df):
        df = df.copy()
        if df.columns.size > self.columnCount():
            self.setColumnCount(df.columns.size)
        r = self.rowCount()
        self.insertRow(r)
        for c, column in enumerate(df):
            it = QtWidgets.QTableWidgetItem(column)
            self.setItem(r, c, it)
        i = self.rowCount()
        for r, row in df.iterrows():
            self.insertRow(self.rowCount())
            for c, (column, value) in enumerate(row.iteritems()):
                it = QtWidgets.QTableWidgetItem(str(value))
                self.setItem(i+r , c, it)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = DataFrameTableWidget()
    w.resize(640, 480)
    w.show()
    watcher = Watcher(sys.argv[1])
    watcher.run()
    watcher.emitter.newDataFrameSignal.connect(w.append_dataframe)
    sys.exit(app.exec_())

推荐阅读