diff --git a/umbra/controller.py b/umbra/controller.py index a817fe7a91ddae309824b1fd612d15990e78dfde..dc63a1bb2d81676321e9b2adbe39b3ac7376006a 100644 --- a/umbra/controller.py +++ b/umbra/controller.py @@ -1,7 +1,6 @@ from filereader import FileReader, CSVReader, TxtReader from filereader import FileWriter, CSVWriter, TxtWriter from utils import Utils as ut -import tkinter.filedialog as fd import os @@ -57,12 +56,14 @@ class Controller: Select a folder from which multiple files can be read. """ folder_path = self._view.dir_dialog() - file_names = os.listdir(folder_path) - file_paths = [folder_path + "/" + x for x in file_names] - self._set_path(file_paths, type) - self._view.update_files(file_paths, type) - if file_paths: - self._view.button_status("select {}".format(type), "disabled") + if folder_path: + file_names = os.listdir(folder_path) + file_paths = [folder_path + "/" + x for x in file_names \ + if ".csv" in x] + self._set_path(file_paths, type) + self._view.update_files(file_paths, type) + if file_paths: + self._view.button_status("select {}".format(type), "disabled") # TODO: Call 'buttons' by functionality, not object (type) def _set_path(self, paths, type): if type == 'source': diff --git a/umbra/view.py b/umbra/view.py index d97a6f3f095f995284f51ff2d913d9447cdf94cb..6617179ffdfbf541273052533ac3770151d2fb20 100644 --- a/umbra/view.py +++ b/umbra/view.py @@ -25,7 +25,7 @@ class View: self._elements = {} # Dict to avoid large number of attributes self._selected_source = "No file selected" self._selected_shadow = "No file selected" - # self._options = Options(self) + #self._options = Options(self) # Options are not redundant quite yet self._actionlistener = None @@ -67,7 +67,7 @@ class View: def display(self): """Start main loop, displaying GUI elements.""" - self._splash.terminate() + #self._splash.terminate() self._window.deiconify() self._window.mainloop() diff --git a/umbra_app/umbra/controller.py b/umbra_app/umbra/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..a54f8faf1bae9f3ae3ec86d5d442831766c32b31 --- /dev/null +++ b/umbra_app/umbra/controller.py @@ -0,0 +1,203 @@ +from filereader import CSVReader +from filereader import CSVWriter +from utils import Utils as ut +import os + + +class Controller: + """Control of general functionality and communication between Model + and View classes. + """ + + def __init__(self, view, model): + self._model = model + self._view = view + self._view.actionlistener = self.actionlistener + + # Assume .csv only + self._filereader = CSVReader() + self._filewriter = CSVWriter() + + # Lists of paths, represented as string + self._source_files = [] + self._shadow_files = [] + + @property + def source_files(self): + return self._source_files + + @source_files.setter + def shadow_files(self, val): + self._source_files = val + + @property + def shadow_files(self): + return self._shadow_files + + @shadow_files.setter + def shadow_files(self, val): + self._shadow_files = val + + def start(self): + """Start program user interface.""" + self._view.display() + + def actionlistener(self, key): + """Action listener for view events. Called by view. + + Args: + key(str): Identifier for view functionality + """ + action, *type = key.split(' ') # type is a (possibly empty) list + if action == 'select': + self._select_files(type[0]) + elif action == 'delete': + self._delete_files(type[0]) + elif action == 'compare': + self._compare_files() + elif action == 'save': + self._save_results() + elif action == 'save_csv': + self._filewriter = CSVWriter() + elif action == 'save_txt': + self._filewriter = TxtWriter() + elif action == 'select_folder': + self._select_folder(type[0]) + elif action == 'rm_all': + self._delete_files(type[0], True) + + def _select_folder(self, type="shadow"): + """ + Select a folder from which multiple files can be read. + """ + folder_path = self._view.dir_dialog() + if folder_path: + file_names = os.listdir(folder_path) + file_paths = [folder_path + "/" + x for x in file_names] + self._set_path(file_paths, type) + self._view.update_files(file_paths, type) + if file_paths: + self._view.button_status("select {}".format(type), "disabled") + # TODO: Call 'buttons' by functionality, not object (type) + else: + self._view.update_message('no_file') + + def _set_path(self, paths, type): + if type == 'source': + self._source_files = paths + else: + self._shadow_files = paths + + def _select_files(self, type): + """Select files and add them to files list corresponding to type. + + Args: + type (str): Role of files ('source' or 'shadow') + """ + files = getattr(self, '_{}_files'.format(type)) + selection = self._view.select_files(type) + # TODO: Check if not already present + files.extend(selection) + self._view.update_files(files, type) + if files: + self._view.button_status("select_folder {}".format(type), + "disabled") + + def _delete_files(self, type, all=False): # TODO: Not yet implemented fully + """Remove selected file from files list corresponding to type. + + Args: + type (str): Role of files ('source' or 'shadow') + all (boolean): whether to remove all files, by default false. + """ + selection = self._view.selected(type) + files = getattr(self, '_{}_files'.format(type)) + if selection is not None: # TODO: This is supposed to make sure deletion of 'No file selected' is impossible, but does not work. + if all: + selection = [file for file in files] + for s in selection: + files.remove(s) + self._view.button_status("select {}".format(type), "normal") + else: + selection = [file for file in files if selection.lower() in file.lower()][0] + files.remove(selection) + self._view.button_status("select_folder {}".format(type), "normal") + self._view.update_files(files, type) + + def _compare_files(self): + """Check source and shadow data availability, read in, and compare. + If multiple, read in and compare accordingly. + """ + if not self._source_files: + self._view.update_message('no source') + elif not self._shadow_files: + self._view.update_message('no shadow') + else: + self._view.update_message('files ok') + self._view.button_status("save", "normal") + + self._read_folder("shadow") + self._read_folder("source") + + self._model.compare() + if self._model.analysis_complete: + self._view.update_message('comparison complete') + self._view.button_status("compare", "disabled") + + def _save_results(self): + """Save analysis results to file. If there is multiple results, + save those. + """ + if not self._model.analysis_complete(): + self._view.update_message('no comparison') + else: + + if self._model.multi_results: + path = self._view.ask_save_location() + results = self._model.multi_results + self._filewriter.write_multiple(path, results) + self._view.update_message('saved') + elif not self._model.multi_results \ + and not self._model.analysis_results: + self._view.update_message('nonexistent') + + """ + def _read_files(self, path, type): + Read data from file paths and save to model. + + Args: + type (str): Role of file ('source' or 'shadow') + + data = self._filereader.read(path, type) + if type == "source": + self._model.data_source = data + self._model.id = ut.id_regex(path) + else: + self._model.data_shadow = data + """ + + def _read_folder(self, type): + """ + Read in multiple source files. + Extensible to consider "type". Now assume shadow + files. + """ + if type == "shadow": + for file_path in self._shadow_files: + if "AO" not in file_path or ".csv" not in file_path: + # Very dirty + pass # TODO: raise error + else: + dict = self._model.get_multi_data(type) + key = ut.shadow_regex(file_path) + data = self._filereader.read(file_path, type) + ut.add_to_dict(key, data, dict, alternative=True) + elif type == "source": + for file_path in self._source_files: + if ".csv" not in file_path: + pass + else: + dict = self._model.get_multi_data(type) + key = ut.id_regex(file_path) + data = self._filereader.read(file_path, type) + ut.add_to_dict(key, data, dict, alternative=True)