diff --git a/array2xls/__init__.py b/array2xls/__init__.py index 4a271a8..06e1d21 100644 --- a/array2xls/__init__.py +++ b/array2xls/__init__.py @@ -2,12 +2,14 @@ import tkinter as tk import gui - -if __name__ == '__main__': +def run(): root = tk.Tk() app = gui.Application(master=root) app.mainloop() try: root.destroy() except tk.TclError: - pass \ No newline at end of file + pass + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/array2xls/gui.py b/array2xls/gui.py index e2cd678..a0397fb 100644 --- a/array2xls/gui.py +++ b/array2xls/gui.py @@ -1,46 +1,145 @@ import tkinter as tk +import tkinter.ttk as ttk +from tkinter import filedialog +import validators -class StatusBar(tk.Frame): +class StatusPanel(tk.Frame): - def __init__(self, master): - tk.Frame.__init__(self, master) - self.label = tk.Label(self, bd=1, relief=tk.SUNKEN, anchor=tk.SW) + def __init__(self, parent): + tk.Frame.__init__(self, parent.master) + self._parent = parent + self.label = tk.Label(self, bd=1, relief=tk.SUNKEN, anchor=tk.S) self.label.pack(fill=tk.X) self.pack(side=tk.BOTTOM, fill=tk.X) - def set(self, text): + def set_text(self, text): self.label.config(text=text) self.label.update_idletasks() - def clear(self): - self.label.config(text="") + def clear_text(self): + self.set_text('') + + +class FilePanel(tk.Frame): + + def __init__(self, parent): + tk.Frame.__init__(self, parent.master) + self._parent = parent + self.btn_files = tk.Button(self, text="Select Files", command=self._parent.select_files) + self.btn_files.pack(pady=5, padx=5) + self.label = tk.Label(self, anchor=tk.SW) + self.label.pack(fill=tk.X, pady=5, padx=5) + ttk.Separator(self, orient=tk.HORIZONTAL).pack(side=tk.BOTTOM, fill=tk.X) + self.pack(side=tk.TOP, fill=tk.X) + + def set_text(self, text): + self.label.config(text=text) self.label.update_idletasks() + def clear_text(self): + self.set_text('') + + +class FieldPanel(tk.Frame): + + def __init__(self, parent): + tk.Frame.__init__(self, parent.master) + self._parent = parent + self.is_single = tk.BooleanVar() + self.is_single.set(True) + self.rbtn_single = tk.Radiobutton(self, text='single excel files', variable=self.is_single, value=True, command=self.disable_listbox) + self.rbtn_combine = tk.Radiobutton(self, text='one combined excel file', variable=self.is_single, value=False, command=self.enable_listbox) + self.rbtn_single.pack(anchor=tk.W) + self.rbtn_combine.pack(anchor=tk.W) + self.listbox = tk.Listbox(self, selectmode=tk.MULTIPLE, activestyle='none') + self.listbox.pack(fill=tk.X, ipady=5, ipadx=5) + self.listbox.insert(0, "HELLO") + self.pack(side=tk.TOP, fill=tk.X) + + def disable_radio_btn(self): + self.rbtn_single.config(state="disabled") + self.rbtn_combine.config(state="disabled") + + def disable_listbox(self): + self.listbox.config(state="disabled") + + def disable(self): + self.disable_listbox() + self.disable_radio_btn() + + def enable_radio_btn(self): + self.rbtn_single.config(state="normal") + self.rbtn_combine.config(state="normal") + self.is_single.set(True) + + def enable_listbox(self): + self.listbox.config(state="normal") + + def enable(self): + self.enable_listbox() + self.enable_radio_btn() + + +class ActionPanel(tk.Frame): + + def __init__(self, parent): + tk.Frame.__init__(self, parent.master) + self._parent = parent + self.btn_go = tk.Button(self, text="GO!", command=self._parent.quit) + self.btn_go.pack(side=tk.LEFT, pady=5, padx=5) + self.btn_quit = tk.Button(self, text="Quit", command=self._parent.quit) + self.btn_quit.pack(side=tk.RIGHT, pady=5, padx=5) + self.pack(side=tk.BOTTOM, fill=tk.X) + + def disable(self): + self.btn_go.config(state='disabled') + + def enable(self): + self.btn_go.config(state='active') + class Application(tk.Frame): def __init__(self, master): - #master.geometry("500x500") - master.minsize(height=100, width=100) + master.minsize(height=330, width=200) tk.Frame.__init__(self, master) + self._master = master self.pack(fill=tk.BOTH) - self.status = StatusBar(self.master) - self.create_widgets() + self.status_panel = StatusPanel(self) + self.file_panel = FilePanel(self) + self.field_panel = FieldPanel(self) + self.action_panel = ActionPanel(self) + self.reset() - def create_widgets(self): - self.btn_files = tk.Button(self, text="Select Files", command=self.selected_files) - self.btn_files.pack(side=tk.LEFT, pady=5, padx=5) - self.btn_quit = tk.Button(self, text="Quit", command=self.quit) - self.btn_quit.pack(side=tk.RIGHT, pady=5, padx=5) + def reset(self): + self.field_panel.disable() + self.action_panel.disable() + self.file_validator = None + self.validated_files = None - def selected_files(self): + def select_files(self): + self.reset() + self.file_panel.clear_text() opts = { 'initialdir': '~', - 'filetypes': [('text files', '.txt'), ('python files', '.py')], - 'multiple':True} - result = tk.filedialog.askopenfilename(**opts) - print(type(result), ':', result) - + 'filetypes': [('Signalyse Statistics', '.stx'), ('Signalyse Data', '.dat'), ('Sensovation Data', '.csv')], + 'multiple': True} + file_selection = tk.filedialog.askopenfilename(**opts) + if not isinstance(file_selection, tuple) or len(file_selection) == 0: + return + self.file_validator = validators.guess_validator(file_selection) + if self.file_validator is None: + return + self.validated_files = [f for f in validators.validate_files(file_selection, self.file_validator)] + status = '%d of %d valid %s files found' % (len(self.validated_files), len(file_selection), self.file_validator.type) + self.file_panel.set_text(status) + if len(self.validated_files) == 0: + self.reset() + return + self.field_panel.enable_radio_btn() + + def quit(self): + self._master.destroy() diff --git a/array2xls/validators.py b/array2xls/validators.py new file mode 100644 index 0000000..73da97f --- /dev/null +++ b/array2xls/validators.py @@ -0,0 +1,67 @@ +import os + +from collections import namedtuple + +class ValidationError(ValueError): + pass + +Validator = namedtuple('Validator', 'type validate extension') +DataFile = namedtuple('DataFile', 'path separator') + +def validate_stx(lines): + iterator = iter(lines) + line = next(iterator) + if not line.startswith('Report_Format\t2'): + raise ValidationError('1 Unsupported File;' + line) + for line in iterator: + if line.startswith('Probe_Name\t'): + break + else: + raise ValidationError('1 Unsupported File') + for line in iterator: + probe_name, rest = line.split('\t', 1) + separator = ',' if rest.count(',') > rest.count('.') else '.' + return separator + else: + raise ValidationError('No Data Present') + + +def validate_dat(lines): + iterator = iter(lines) + if not next(iterator).startswith('Report_Format\t2'): + raise ValidationError('Unsupported File') + for line in iterator: + if line.startswith('Dot_Number\t'): + break + else: + raise ValidationError('Unsupported File') + for line in iterator: + probe_name, rest = line.split('\t', 3) + separator = ',' if rest.count(',') > rest.count('.') else '.' + return separator + else: + raise ValidationError('No Data Present') + +def validate_csv(lines): + pass + +validation_map = { + '.stx': Validator('Signalyse Statistic Files', validate_stx, '.stx'), + '.dat': Validator('Signalyse Data Files', validate_dat, '.dat'), + '.csv': Validator('Sensovation Data Files', validate_csv, '.csv') +} + +def guess_validator(unvalidated): + # get the validation method by examining the first file + _, extension = os.path.splitext(unvalidated[0]) + return validation_map.get(extension, None) + +def validate_files(unvalidated, selected_validator): + # get the validation method by examining the first file + for file_path in unvalidated: + try: + with open(file_path, mode='r', encoding='utf-8') as file_handle: + separator = selected_validator.validate(file_handle) + yield DataFile(file_path, separator) + except (IOError, UnicodeError, ValidationError) as e: + print(e)