class ConanSaveDialogView(View['ConanSaveDialogPresenter', Gtk.Window]): STYLE = ''' .small-pad { min-height: 0px; min-width: 0px; padding: 6px 4px 6px 4px; } .small-combobox .combo { min-height: 0px; min-width: 0px; } .conan-analysis-saver-view-footer-button { min-height: 0px; min-width: 60px; padding: 10px 4px 10px 4px; } .error { color: red; border: 1px solid red; } .error-text { color: red; } ''' _STYLE_PROV = Gtk.CssProvider() _STYLE_PROV.load_from_data(bytes(STYLE, 'utf-8')) Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), _STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) def _do_init(self, parent_window: Optional[Gtk.Window] = None) -> Gtk.Window: self._window = Gtk.Window( title='Save analysis', resizable=False, modal=True, transient_for=parent_window, window_position=Gtk.WindowPosition.CENTER, ) body = Gtk.Grid(margin=10, row_spacing=10) self._window.add(body) content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) body.attach(content, 0, 0, 1, 1) save_location_frame = Gtk.Frame(label='Save location') content.add(save_location_frame) save_location_content = Gtk.Grid(margin=10, column_spacing=10, row_spacing=5) save_location_frame.add(save_location_content) save_dir_lbl = Gtk.Label('Parent:', xalign=0) save_location_content.attach(save_dir_lbl, 0, 0, 1, 1) self._save_dir_parent_inp = Gtk.FileChooserButton( action=Gtk.FileChooserAction.SELECT_FOLDER, hexpand=True) self._save_dir_parent_inp.get_style_context().add_class( 'small-combobox') save_location_content.attach_next_to(self._save_dir_parent_inp, save_dir_lbl, Gtk.PositionType.RIGHT, 1, 1) save_dir_parent_err_lbl = Gtk.Label(xalign=0, width_request=190) save_dir_parent_err_lbl.get_style_context().add_class('error-text') save_location_content.attach_next_to(save_dir_parent_err_lbl, self._save_dir_parent_inp, Gtk.PositionType.RIGHT, 1, 1) save_name_lbl = Gtk.Label('Name:', xalign=0) save_location_content.attach(save_name_lbl, 0, 1, 1, 1) save_dir_name_inp = Gtk.Entry() save_dir_name_inp.get_style_context().add_class('small-pad') save_location_content.attach_next_to(save_dir_name_inp, save_name_lbl, Gtk.PositionType.RIGHT, 1, 1) save_dir_name_err_lbl = Gtk.Label(xalign=0, width_request=190) save_dir_name_err_lbl.get_style_context().add_class('error-text') save_location_content.attach_next_to(save_dir_name_err_lbl, save_dir_name_inp, Gtk.PositionType.RIGHT, 1, 1) figures_frame = Gtk.Frame(label='Figures') content.add(figures_frame) figures_content = Gtk.Grid(margin=10, column_spacing=10, row_spacing=5) figures_frame.add(figures_content) _, angles_figure_options_area = self.new_component( figure_options_cs.factory( model=self.presenter.angle_figure_options, figure_name='contact angles plot', )) angles_figure_options_area.show() figures_content.attach(angles_figure_options_area, 0, 0, 1, 1) footer = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) body.attach_next_to(footer, content, Gtk.PositionType.BOTTOM, 1, 1) ok_btn = Gtk.Button('OK') ok_btn.get_style_context().add_class( 'conan-analysis-saver-view-footer-button') footer.pack_end(ok_btn, expand=False, fill=False, padding=0) cancel_btn = Gtk.Button('Cancel') cancel_btn.get_style_context().add_class( 'conan-analysis-saver-view-footer-button') footer.pack_end(cancel_btn, expand=False, fill=False, padding=0) self._window.show_all() # Wiring things up ok_btn.connect('clicked', lambda *_: self.presenter.ok()) cancel_btn.connect('clicked', lambda *_: self.presenter.cancel()) self._window.connect('delete-event', self._hdl_window_delete_event) self.bn_save_dir_parent = AccessorBindable(self._get_save_dir_parent, self._set_save_dir_parent) self.bn_save_dir_name = GObjectPropertyBindable( save_dir_name_inp, 'text') self._confirm_overwrite_dialog = None self._file_exists_info_dialog = None self.presenter.view_ready() return self._window def _hdl_window_delete_event(self, widget: Gtk.Dialog, event: Gdk.Event) -> bool: self.presenter.cancel() # return True to prevent the dialog from closing. return True def show_confirm_overwrite_dialog(self, path: Path) -> None: if self._confirm_overwrite_dialog is not None: return self._confirm_overwrite_dialog = YesNoDialog(message_format=( "This save location '{!s}' already exists, do you want to clear its contents?" .format(path)), parent=self._window) self._confirm_overwrite_dialog.connect( 'response', self._hdl_confirm_overwrite_dialog_response) self._confirm_overwrite_dialog.connect('delete-event', lambda *_: True) self._confirm_overwrite_dialog.show() def _hdl_confirm_overwrite_dialog_response( self, widget: Gtk.Dialog, response: Gtk.ResponseType) -> None: accept = (response == Gtk.ResponseType.YES) self.presenter.hdl_confirm_overwrite_dialog_response(accept) def hide_confirm_overwrite_dialog(self) -> None: if self._confirm_overwrite_dialog is None: return self._confirm_overwrite_dialog.destroy() self._confirm_overwrite_dialog = None def tell_user_file_exists_and_is_not_a_directory(self, path: Path) -> None: if self._file_exists_info_dialog is not None: return self._file_exists_info_dialog = ErrorDialog(message_format=( "Cannot save to '{!s}', the path already exists and is a non-directory file." .format(path)), parent=self._window) self._file_exists_info_dialog.show() def hdl_delete_event(*_) -> None: self._file_exists_info_dialog = None def hdl_response(dialog: Gtk.Window, *_) -> None: self._file_exists_info_dialog = None dialog.destroy() self._file_exists_info_dialog.connect('delete-event', hdl_delete_event) self._file_exists_info_dialog.connect('response', hdl_response) def _get_save_dir_parent(self) -> Path: path_str = self._save_dir_parent_inp.get_filename() path = Path(path_str) if path_str is not None else None return path def _set_save_dir_parent(self, path: Optional[Path]) -> None: if path is None: self._save_dir_parent_inp.unselect_all() return path = str(path) self._save_dir_parent_inp.set_filename(path) def flush_save_dir_parent(self) -> None: self.bn_save_dir_parent.poke() def _do_destroy(self) -> None: self._window.destroy() self.hide_confirm_overwrite_dialog()
class IFTRootView(View['IFTRootPresenter', Gtk.Widget]): def _do_init(self) -> Gtk.Widget: self._window = Gtk.Window( title='Interfacial Tension', window_position=Gtk.WindowPosition.CENTER, width_request=800, height_request=600, ) _, wizard_area = self.new_component( wizard_cs.factory( controller=self.presenter.wizard_controller, titles=OrderedDict([ ( PageID.IMAGE_ACQUISITION, 'Image acquisition', ), ( PageID.PHYS_PARAMS, 'Physical parameters', ), ( PageID.IMAGE_PROCESSING, 'Image processing', ), ( PageID.RESULTS, 'Results', ), ]), pages={ PageID.IMAGE_ACQUISITION: image_acquisition_cs.factory( model=self.presenter.image_acquisition_model, ), PageID.PHYS_PARAMS: physical_parameters_cs.factory( model=self.presenter.physical_parameters_model), PageID.IMAGE_PROCESSING: ift_image_processing_cs.factory( model=self.presenter.image_processing_model, ), PageID.RESULTS: ift_results_cs.factory( model=self.presenter.results_model, ), })) wizard_area.show() self._window.add(wizard_area) self._window.connect('delete-event', self._hdl_window_delete_event) self._confirm_discard_dialog = None return self._window def _hdl_window_delete_event(self, window: Gtk.Window, data: Gdk.Event) -> bool: self.presenter.exit() return True def show_confirm_discard_dialog(self) -> None: if self._confirm_discard_dialog is not None: return self._confirm_discard_dialog = YesNoDialog( message_format='Discard unsaved results?', parent=self._window, ) self._confirm_discard_dialog.connect('delete-event', lambda *_: True) self._confirm_discard_dialog.connect( 'response', self._hdl_confirm_discard_dialog_response) self._confirm_discard_dialog.show() def _hdl_confirm_discard_dialog_response( self, widget: Gtk.Dialog, response: Gtk.ResponseType) -> None: discard = (response == Gtk.ResponseType.YES) self.presenter.hdl_confirm_discard_dialog_response(discard) def hide_confirm_discard_dialog(self) -> None: if self._confirm_discard_dialog is None: return self._confirm_discard_dialog.destroy() self._confirm_discard_dialog = None def _do_destroy(self) -> None: self._window.destroy()
class ConanResultsView(View['ConanResultsPresenter', Gtk.Widget]): def _do_init(self, footer_area: Gtk.Grid) -> Gtk.Widget: self._widget = Gtk.Grid() frame = Gtk.Frame(margin=10, label_xalign=0.5) frame.show() self._widget.attach(frame, 0, 1, 1, 1) self._stack = Gtk.Stack() self._stack.show() frame.add(self._stack) _, self._individual_area = self.new_component( individual_cs.factory(model=self.presenter.individual_model)) self._individual_area.show() self._stack.add_titled(self._individual_area, name='Individual Fit', title='Individual Fit') _, self._graphs_area = self.new_component( graphs_cs.factory(model=self.presenter.graphs_model)) self._graphs_area.show() self._stack.add_titled(self._graphs_area, name='Graphs', title='Graphs') self._stack_switcher = Gtk.StackSwitcher(stack=self._stack) self._stack_switcher.show() frame.props.label_widget = self._stack_switcher self._stack.props.visible_child = self._individual_area _, footer_inner = self.new_component( results_footer_cs.factory( in_status=self.presenter.bn_footer_status, in_progress=self.presenter.bn_completion_progress, in_time_elapsed=self.presenter.bn_time_elapsed, in_time_remaining=self.presenter.bn_time_remaining, do_back=self.presenter.back, do_cancel=self.presenter.cancel, do_save=self.presenter.save, )) footer_inner.show() footer_area.add(footer_inner) self._confirm_cancel_dialog = None self._confirm_discard_dialog = None self._save_dialog_cid = None self.presenter.view_ready() return self._widget def show_graphs(self) -> None: self._stack_switcher.show() def hide_graphs(self) -> None: self._stack_switcher.hide() self._stack.set_visible_child(self._individual_area) def show_confirm_cancel_dialog(self) -> None: if self._confirm_cancel_dialog is not None: return self._confirm_cancel_dialog = YesNoDialog( parent=self._get_parent_window(), message_format='Confirm cancel analysis?', ) self._confirm_cancel_dialog.connect('delete-event', lambda *_: True) self._confirm_cancel_dialog.connect( 'response', self._hdl_confirm_cancel_dialog_response) self._confirm_cancel_dialog.show() def _hdl_confirm_cancel_dialog_response( self, widget: Gtk.Dialog, response: Gtk.ResponseType) -> None: accept = (response == Gtk.ResponseType.YES) self.presenter.hdl_confirm_cancel_response(accept) def hide_confirm_cancel_dialog(self) -> None: if self._confirm_cancel_dialog is None: return self._confirm_cancel_dialog.destroy() self._confirm_cancel_dialog = None def show_confirm_discard_dialog(self) -> None: if self._confirm_discard_dialog is not None: return self._confirm_discard_dialog = YesNoDialog( parent=self._get_parent_window(), message_format='Confirm discard results?', ) self._confirm_discard_dialog.connect('delete-event', lambda *_: True) self._confirm_discard_dialog.connect( 'response', self._hdl_confirm_discard_dialog_response) self._confirm_discard_dialog.show() def _hdl_confirm_discard_dialog_response( self, widget: Gtk.Dialog, response: Gtk.ResponseType) -> None: accept = (response == Gtk.ResponseType.YES) self.presenter.hdl_confirm_discard_response(accept) def hide_confirm_discard_dialog(self) -> None: if self._confirm_discard_dialog is None: return self._confirm_discard_dialog.destroy() self._confirm_discard_dialog = None def show_save_dialog(self, options: ConanAnalysisSaverOptions) -> None: if self._save_dialog_cid is not None: return self._save_dialog_cid, save_dialog = self.new_component( conan_save_dialog_cs.factory( model=options, do_ok=(lambda: self.presenter.hdl_save_dialog_response( should_save=True)), do_cancel=(lambda: self.presenter.hdl_save_dialog_response( should_save=False)), parent_window=self._get_parent_window(), )) save_dialog.show() def hide_save_dialog(self) -> None: if self._save_dialog_cid is None: return self.remove_component(self._save_dialog_cid) self._save_dialog_cid = None def _get_parent_window(self) -> Optional[Gtk.Window]: toplevel = self._widget.get_toplevel() if isinstance(toplevel, Gtk.Window): return toplevel else: return None def _do_destroy(self) -> None: self._widget.destroy() self.hide_confirm_cancel_dialog() self.hide_confirm_discard_dialog()