Tkinter GUI для преобразования файла с фиксированной шириной в файл с разделителями - PullRequest
1 голос
/ 12 января 2012

Я пишу код конвертера для нашего отдела данных, чтобы конвертировать файлы фиксированной ширины в файлы с разделителями. Обычно мы используем импорт файла в Excel, используем мастер импорта текста, чтобы установить длины полей, а затем просто сохраняем как CSV. Однако мы столкнулись с ограничением, когда мы начали получать файлы длиной в миллионы записей и, следовательно, не могут быть импортированы в Excel. Файлы не всегда имеют пробелы между полями, особенно между полями значений, такими как номера телефонов или почтовые индексы. Заголовки также часто заполняются полностью без пробелов.

Пример типичного файла фиксированной ширины, с которым мы имеем дело:

SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar
000001T1  P1     Sample A Sample                                                                                         123 Any Street                                                                  Anytown 12345-6789                                12345678900
000002T1  P1     Sample A Sample                       Director of Medicine                                              123 Any Street                          Po Box 1234                             Anytown 12345-6789                                12345678900

Программа должна разбить файл на следующие разделенные поля:

Sequen
Мешок и па
Полное имя
Должность
Имя госпиталя
Адрес доставки
Альтернативный адрес 1
Вычисляемый текст
POSTNET Bar

Каждый файл немного отличается шириной каждого поля в зависимости от остальной части задания. То, что я ищу, это ориентированный на GUI разделитель, очень похожий на мастер импорта Excel для файлов с фиксированной шириной. Я пишу этот инструмент на Python как часть более крупного инструмента, который выполняет множество других файловых операций, таких как разбиение файлов на несколько, переворачивание файла, преобразование из разделителя в фиксированную ширину и проверка проверки цифр. Я использую Tkinter для остальных инструментов, и было бы идеально, если бы решение использовало его также.

Любая помощь приветствуется

Ответы [ 2 ]

3 голосов
/ 13 января 2012

Если я правильно понимаю проблему (и есть большая вероятность, что я не ...), простейшим решением может быть использование текстового виджета.

Сделать первую строку серией пробеловтой же длины, что и ряд.Используйте пару чередующихся тегов (например, «четный» и «нечетный»), чтобы дать каждому символу альтернативный цвет, чтобы они выделялись друг от друга.Вторая строка будет заголовком, а любые оставшиеся строки будут парой строк образцов данных.

Затем установите привязки в первой строке, чтобы преобразовать пробел в «x», когда пользователь нажимает нахарактер.Если они нажимают на «х», преобразовать его обратно в пробел.Затем они могут пойти и нажать на символ, который является началом каждого столбца.Когда пользователь закончил, вы можете получить первую строку текстового виджета, и он будет иметь «x» для каждого столбца.Тогда вам просто нужна небольшая функция, которая переводит это в любой формат, который вам нужен.

Это будет выглядеть примерно так (хотя, очевидно, цвета будут отличаться от тех, которые появляются на этом сайте)

      x          x                                     x  ...
SequenSack and PaFull Name****************************]JOB...
000001T1  P1     Sample A Sample                          ...

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

import sys
import Tkinter as tk
import tkFont

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        header = "SequenSack and PaFull Name****************************]JOB TITLE****************]HOSP NAME******************************]Delivery Address***********************]Alternate 1 Address********************]Calculated Text**********************************]POSTNET Bar"
        sample = "000001T1  P1     Sample A Sample                                                                                         123 Any Street                                                                  Anytown 12345-6789                                12345678900"
        widget = DelimiterWidget(self, header, sample)
        hsb = tk.Scrollbar(orient="horizontal", command=widget.xview)
        widget.configure(xscrollcommand=hsb.set)
        hsb.pack(side="bottom", fill="x")
        widget.pack(side="top", fill="x")

class DelimiterWidget(tk.Text):
    def __init__(self, parent, header, samplerow):
        fixedFont = tkFont.nametofont("TkFixedFont")
        tk.Text.__init__(self, parent, wrap="none", height=3, font=fixedFont)
        self.configure(cursor="left_ptr")
        self.tag_configure("header", background="gray")
        self.tag_configure("even", background="#ffffff")
        self.tag_configure("header_even", background="bisque")
        self.tag_configure("header_odd", background="lightblue")
        self.tag_configure("odd", background="#eeeeee")
        markers = " "*len(header)
        for i in range(len(header)):
            tag = "even" if i%2==0 else "odd"
            self.insert("end", " ", (tag,))
        self.insert("end", "\n")
        self.insert("end", header+"\n", "header")
        self.insert("end", samplerow, "sample")
        self.configure(state="disabled")
        self.bind("<1>", self.on_click)
        self.bind("<Double-1>", self.on_click)
        self.bind("<Triple-1>", self.on_click)

    def on_click(self, event):
        '''Handle a click on a marker'''
        index = self.index("@%s,%s" % (event.x, event.y))
        current = self.get(index)
        self.configure(state="normal")
        self.delete(index)
        (line, column) = index.split(".")
        tag = "even" if int(column)%2 == 0 else "odd"
        char = " " if current == "x" else "x"
        self.insert(index, char, tag)
        self.configure(state="disabled")
        self.highlight_header()
        return "break"

    def highlight_header(self):
        '''Highlight the header based on marker positions'''
        self.tag_remove("header_even", 1.0, "end")
        self.tag_remove("header_odd", 1.0, "end")
        markers = self.get(1.0, "1.0 lineend")

        i = 0
        start = "2.0"
        tag = "header_even"
        while True:
            try:
                i = markers.index("x", i+1)
                end = "2.%s" % i
                self.tag_add(tag, start, end)
                start = self.index(end)
                tag = "header_even" if tag == "header_odd" else "header_odd"
            except ValueError:
                break

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()
0 голосов
/ 12 января 2012

edit: Теперь я вижу, что вы ищете графический интерфейс.Я оставлю этот неправильный ответ для потомков.

import csv

def fixedwidth2csv(fw_name, csv_name, field_info, headings=None):
    with open(fw_name, 'r') as fw_in:
        with open(csv_name, 'rb') as csv_out: # 'rb' => 'r' for python 3
            wtr = csv.writer(csv_out)
            if headings:
                wtr.writerow(headings)
            for line in fw_in:
                wtr.writerow(line[pos:pos+width].strip() for pos, width in field_info)
...