def _do_init(self) -> Gtk.Widget: self._widget = Gtk.Grid(row_spacing=10, column_spacing=10) file_chooser_lbl = Gtk.Label('Image files:', xalign=0) self._widget.attach(file_chooser_lbl, 0, 0, 1, 1) self._file_chooser_inp = FileChooserButton( file_filter=self._FILE_INPUT_FILTER, select_multiple=True ) self._file_chooser_inp.get_style_context().add_class('small-pad') self._widget.attach_next_to(self._file_chooser_inp, file_chooser_lbl, Gtk.PositionType.RIGHT, 1, 1) frame_interval_lbl = Gtk.Label('Frame interval (s):') self._widget.attach(frame_interval_lbl, 0, 1, 1, 1) frame_interval_inp_container = Gtk.Grid() self._widget.attach_next_to(frame_interval_inp_container, frame_interval_lbl, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_inp = FloatEntry(lower=0, width_chars=6, invisible_char='\0', caps_lock_warning=False) self._frame_interval_inp.get_style_context().add_class('small-pad') frame_interval_inp_container.add(self._frame_interval_inp) # Error message labels self._file_chooser_err_msg_lbl = Gtk.Label(xalign=0) self._file_chooser_err_msg_lbl.get_style_context().add_class('error-text') self._widget.attach_next_to(self._file_chooser_err_msg_lbl, self._file_chooser_inp, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_err_msg_lbl = Gtk.Label(xalign=0) self._frame_interval_err_msg_lbl.get_style_context().add_class('error-text') self._widget.attach_next_to(self._frame_interval_err_msg_lbl, frame_interval_inp_container, Gtk.PositionType.RIGHT, 1, 1) self._widget.show_all() self._frame_interval_inp.bind_property( 'sensitive', self._frame_interval_inp, 'visibility', GObject.BindingFlags.SYNC_CREATE ) self.bn_selected_image_paths = GObjectPropertyBindable(self._file_chooser_inp, 'file-paths') self.bn_frame_interval = GObjectPropertyBindable(self._frame_interval_inp, 'value') self.bn_frame_interval_sensitive = GObjectPropertyBindable(self._frame_interval_inp, 'sensitive') # Set which widget is first focused self._file_chooser_inp.grab_focus() self.presenter.view_ready() return self._widget
def _do_init(self, view_context: ImageProcessorPluginViewContext) -> None: self._view_context = view_context self._tool_ref = self._view_context.get_tool_item( ToolID.FOREGROUND_DETECTION) self.bn_tool_button_is_active = self._tool_ref.bn_is_active self._button_body = Gtk.Grid(hexpand=True, vexpand=True) self._tool_ref.button_interior.add(self._button_body) button_lbl = Gtk.Label( label="Foreground detection", vexpand=True, valign=Gtk.Align.CENTER, ) self._button_body.add(button_lbl) self._button_body.show_all() self._popover = Gtk.Popover( relative_to=self._button_body, modal=False, ) # Prevent the popover from being dismissed by the user clicking on it. self._popover.connect('button-release-event', lambda *_: True) popover_body = Gtk.Grid( margin=10, row_spacing=10, column_spacing=10, width_request=250, ) self._popover.add(popover_body) thresh_inp = Gtk.Adjustment(value=255, lower=1, upper=255) thresh_lbl = Gtk.Label('Threshold:', halign=Gtk.Align.START) popover_body.attach(thresh_lbl, 0, 0, 1, 1) thresh_scl = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=thresh_inp, hexpand=True, draw_value=False) thresh_scl.get_style_context().add_class('small-pad') thresh_scl.get_style_context().add_provider( self._STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) thresh_scl.show() popover_body.attach(thresh_scl, 1, 0, 1, 1) thresh_ety = FloatEntry(width_chars=5, xalign=0) thresh_ety.get_style_context().add_class('small-pad') thresh_ety.get_style_context().add_provider( self._STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) thresh_ety.show() popover_body.attach(thresh_ety, 2, 0, 1, 1) # Bind the properties of Gtk.Adjustment to the Gtk.Entry widget. for (src, targ), prop in itertools.product( ((thresh_inp, thresh_ety), ), ('value', 'lower', 'upper')): src.bind_property( prop, # source_property targ, prop, # target, target_property GObject.BindingFlags.BIDIRECTIONAL # flags | GObject.BindingFlags.SYNC_CREATE, lambda _, v: round(v, 1), # transform_to lambda _, v: v # transform_from ) self.bn_thresh = GObjectPropertyBindable( g_obj=thresh_inp, prop_name='value', ) popover_body.show_all() self.presenter.view_ready()
def _do_init(self) -> Gtk.Widget: self._change_camera_dialog_cid = None # type: Any self._widget = Gtk.Grid(row_spacing=10, column_spacing=10) # Populate self.widget camera_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) self._widget.attach(camera_container, 0, 0, 2, 1) camera_lbl = Gtk.Label('Camera:', xalign=0) camera_container.add(camera_lbl) self._current_camera_lbl = Gtk.Label(xalign=0) camera_container.add(self._current_camera_lbl) self._change_camera_btn = Gtk.Button('Connect camera') self._change_camera_btn.get_style_context().add_class('small-pad') camera_container.add(self._change_camera_btn) num_frames_lbl = Gtk.Label('Number of images to capture:', xalign=0) self._widget.attach(num_frames_lbl, 0, 1, 1, 1) num_frames_inp_container = Gtk.Grid() self._widget.attach_next_to(num_frames_inp_container, num_frames_lbl, Gtk.PositionType.RIGHT, 1, 1) self._num_frames_inp = IntegerEntry(lower=1, upper=200, value=1, width_chars=6) self._num_frames_inp.get_style_context().add_class('small-pad') num_frames_inp_container.add(self._num_frames_inp) frame_interval_lbl = Gtk.Label('Frame interval (s):', xalign=0) self._widget.attach(frame_interval_lbl, 0, 2, 1, 1) frame_interval_inp_container = Gtk.Grid() self._widget.attach_next_to(frame_interval_inp_container, frame_interval_lbl, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_inp = FloatEntry(lower=0, width_chars=6, sensitive=False, invisible_char='\0') self._frame_interval_inp.get_style_context().add_class('small-pad') frame_interval_inp_container.add(self._frame_interval_inp) self._current_camera_err_msg_lbl = Gtk.Label(xalign=0) self._current_camera_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._current_camera_err_msg_lbl, camera_container, Gtk.PositionType.RIGHT, 1, 1) self._num_frames_err_msg_lbl = Gtk.Label(xalign=0) self._num_frames_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._num_frames_err_msg_lbl, num_frames_inp_container, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_err_msg_lbl = Gtk.Label(xalign=0) self._frame_interval_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._frame_interval_err_msg_lbl, frame_interval_inp_container, Gtk.PositionType.RIGHT, 1, 1) self._widget.show_all() # Wiring things up self._change_camera_btn.connect( 'clicked', lambda *_: self.presenter.hdl_change_camera_btn_clicked()) self.bn_frame_interval = GObjectPropertyBindable( self._frame_interval_inp, 'value') self.bn_num_frames = GObjectPropertyBindable(self._num_frames_inp, 'value') self.bn_frame_interval_sensitive = GObjectPropertyBindable( self._frame_interval_inp, 'sensitive') self._frame_interval_inp.bind_property( 'sensitive', self._frame_interval_inp, 'visibility', GObject.BindingFlags.SYNC_CREATE) self.presenter.view_ready() return self._widget
class USBCameraView(View['USBCameraPresenter', Gtk.Widget]): STYLE = ''' .change-cam-dialog-view-footer { background-color: gainsboro; } .small-pad { min-height: 0px; min-width: 0px; padding: 6px 4px 6px 4px; } .dialog-footer-button { min-height: 0px; min-width: 0px; padding: 8px 6px 8px 6px; } .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) -> Gtk.Widget: self._change_camera_dialog_cid = None # type: Any self._widget = Gtk.Grid(row_spacing=10, column_spacing=10) # Populate self.widget camera_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) self._widget.attach(camera_container, 0, 0, 2, 1) camera_lbl = Gtk.Label('Camera:', xalign=0) camera_container.add(camera_lbl) self._current_camera_lbl = Gtk.Label(xalign=0) camera_container.add(self._current_camera_lbl) self._change_camera_btn = Gtk.Button('Connect camera') self._change_camera_btn.get_style_context().add_class('small-pad') camera_container.add(self._change_camera_btn) num_frames_lbl = Gtk.Label('Number of images to capture:', xalign=0) self._widget.attach(num_frames_lbl, 0, 1, 1, 1) num_frames_inp_container = Gtk.Grid() self._widget.attach_next_to(num_frames_inp_container, num_frames_lbl, Gtk.PositionType.RIGHT, 1, 1) self._num_frames_inp = IntegerEntry(lower=1, upper=200, value=1, width_chars=6) self._num_frames_inp.get_style_context().add_class('small-pad') num_frames_inp_container.add(self._num_frames_inp) frame_interval_lbl = Gtk.Label('Frame interval (s):', xalign=0) self._widget.attach(frame_interval_lbl, 0, 2, 1, 1) frame_interval_inp_container = Gtk.Grid() self._widget.attach_next_to(frame_interval_inp_container, frame_interval_lbl, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_inp = FloatEntry(lower=0, width_chars=6, sensitive=False, invisible_char='\0') self._frame_interval_inp.get_style_context().add_class('small-pad') frame_interval_inp_container.add(self._frame_interval_inp) self._current_camera_err_msg_lbl = Gtk.Label(xalign=0) self._current_camera_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._current_camera_err_msg_lbl, camera_container, Gtk.PositionType.RIGHT, 1, 1) self._num_frames_err_msg_lbl = Gtk.Label(xalign=0) self._num_frames_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._num_frames_err_msg_lbl, num_frames_inp_container, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_err_msg_lbl = Gtk.Label(xalign=0) self._frame_interval_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._frame_interval_err_msg_lbl, frame_interval_inp_container, Gtk.PositionType.RIGHT, 1, 1) self._widget.show_all() # Wiring things up self._change_camera_btn.connect( 'clicked', lambda *_: self.presenter.hdl_change_camera_btn_clicked()) self.bn_frame_interval = GObjectPropertyBindable( self._frame_interval_inp, 'value') self.bn_num_frames = GObjectPropertyBindable(self._num_frames_inp, 'value') self.bn_frame_interval_sensitive = GObjectPropertyBindable( self._frame_interval_inp, 'sensitive') self._frame_interval_inp.bind_property( 'sensitive', self._frame_interval_inp, 'visibility', GObject.BindingFlags.SYNC_CREATE) self.presenter.view_ready() return self._widget def set_camera_index(self, camera_index: Optional[int]) -> None: if camera_index is None: self._current_camera_lbl.props.label = '' self._current_camera_lbl.props.visible = False self._change_camera_btn.props.label = 'Connect camera' else: self._current_camera_lbl.props.label = '(Connected to index {})'.format( camera_index) self._current_camera_lbl.props.visible = True self._change_camera_btn.props.label = 'Change camera' def show_change_camera_dialog(self) -> None: if self._change_camera_dialog_cid is not None: return self._change_camera_dialog_cid, _ = self.new_component( _change_camera_dialog_cs.factory( on_response=self.presenter.hdl_change_camera_dialog_response, parent_window=self._get_window(), )) def hide_change_camera_dialog(self) -> None: if self._change_camera_dialog_cid is None: return self.remove_component(self._change_camera_dialog_cid) self._change_camera_dialog_cid = None def _get_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()
def _do_init(self, footer_area: Gtk.Grid) -> Gtk.Widget: self._widget = Gtk.Grid(margin=20, row_spacing=10, column_spacing=10) # Label widgets inner_density_lbl = Gtk.Label('Inner density (kg/m³):', xalign=0) self._widget.attach(inner_density_lbl, 0, 0, 1, 1) outer_density_lbl = Gtk.Label('Outer density (kg/m³):', xalign=0) self._widget.attach(outer_density_lbl, 0, 1, 1, 1) needle_width_lbl = Gtk.Label('Needle diameter (mm):', xalign=0) self._widget.attach(needle_width_lbl, 0, 2, 1, 1) gravity_lbl = Gtk.Label('Gravity (m/s²):', xalign=0) self._widget.attach(gravity_lbl, 0, 3, 1, 1) # Input widgets self._inner_density_inp = FloatEntry(lower=0, width_chars=10) self._widget.attach_next_to(self._inner_density_inp, inner_density_lbl, Gtk.PositionType.RIGHT, 1, 1) self._outer_density_inp = FloatEntry(lower=0, width_chars=10) self._widget.attach_next_to(self._outer_density_inp, outer_density_lbl, Gtk.PositionType.RIGHT, 1, 1) self._needle_width_inp = FloatEntry(lower=0, width_chars=10) self._widget.attach_next_to(self._needle_width_inp, needle_width_lbl, Gtk.PositionType.RIGHT, 1, 1) self._gravity_inp = FloatEntry(lower=0, width_chars=10) self._widget.attach_next_to(self._gravity_inp, gravity_lbl, Gtk.PositionType.RIGHT, 1, 1) self._inner_density_err_msg_lbl = Gtk.Label(xalign=0) self._inner_density_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._inner_density_err_msg_lbl, self._inner_density_inp, Gtk.PositionType.RIGHT, 1, 1) self._outer_density_err_msg_lbl = Gtk.Label(xalign=0) self._outer_density_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._outer_density_err_msg_lbl, self._outer_density_inp, Gtk.PositionType.RIGHT, 1, 1) self._needle_width_err_msg_lbl = Gtk.Label(xalign=0) self._needle_width_err_msg_lbl.get_style_context().add_class( 'error-text') self._widget.attach_next_to(self._needle_width_err_msg_lbl, self._needle_width_inp, Gtk.PositionType.RIGHT, 1, 1) self._gravity_err_msg_lbl = Gtk.Label(xalign=0) self._gravity_err_msg_lbl.get_style_context().add_class('error-text') self._widget.attach_next_to(self._gravity_err_msg_lbl, self._gravity_inp, Gtk.PositionType.RIGHT, 1, 1) self._widget.show_all() self.bn_inner_density = GObjectPropertyBindable( g_obj=self._inner_density_inp, prop_name='value', ) self.bn_outer_density = GObjectPropertyBindable( g_obj=self._outer_density_inp, prop_name='value', ) self.bn_needle_width = GObjectPropertyBindable( g_obj=self._needle_width_inp, prop_name='value', # Needle width shown to user is in millimetres. transform_from=lambda x: x / 1000 if x is not None else None, transform_to=lambda x: x * 1000 if x is not None else None, ) self.bn_gravity = GObjectPropertyBindable( g_obj=self._gravity_inp, prop_name='value', ) _, footer_inside = self.new_component( linear_navigator_footer_cs.factory( do_back=self.presenter.prev_page, do_next=self.presenter.next_page, )) footer_inside.show() footer_area.add(footer_inside) self.presenter.view_ready() return self._widget
class LocalStorageView(View['LocalStoragePresenter', Gtk.Widget]): STYLE = ''' .small-pad { min-height: 0px; min-width: 0px; padding: 6px 4px 6px 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) _FILE_INPUT_FILTER = Gtk.FileFilter() _FILE_INPUT_FILTER.add_mime_type('image/png') _FILE_INPUT_FILTER.add_mime_type('image/jpg') def _do_init(self) -> Gtk.Widget: self._widget = Gtk.Grid(row_spacing=10, column_spacing=10) file_chooser_lbl = Gtk.Label('Image files:', xalign=0) self._widget.attach(file_chooser_lbl, 0, 0, 1, 1) self._file_chooser_inp = FileChooserButton( file_filter=self._FILE_INPUT_FILTER, select_multiple=True ) self._file_chooser_inp.get_style_context().add_class('small-pad') self._widget.attach_next_to(self._file_chooser_inp, file_chooser_lbl, Gtk.PositionType.RIGHT, 1, 1) frame_interval_lbl = Gtk.Label('Frame interval (s):') self._widget.attach(frame_interval_lbl, 0, 1, 1, 1) frame_interval_inp_container = Gtk.Grid() self._widget.attach_next_to(frame_interval_inp_container, frame_interval_lbl, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_inp = FloatEntry(lower=0, width_chars=6, invisible_char='\0', caps_lock_warning=False) self._frame_interval_inp.get_style_context().add_class('small-pad') frame_interval_inp_container.add(self._frame_interval_inp) # Error message labels self._file_chooser_err_msg_lbl = Gtk.Label(xalign=0) self._file_chooser_err_msg_lbl.get_style_context().add_class('error-text') self._widget.attach_next_to(self._file_chooser_err_msg_lbl, self._file_chooser_inp, Gtk.PositionType.RIGHT, 1, 1) self._frame_interval_err_msg_lbl = Gtk.Label(xalign=0) self._frame_interval_err_msg_lbl.get_style_context().add_class('error-text') self._widget.attach_next_to(self._frame_interval_err_msg_lbl, frame_interval_inp_container, Gtk.PositionType.RIGHT, 1, 1) self._widget.show_all() self._frame_interval_inp.bind_property( 'sensitive', self._frame_interval_inp, 'visibility', GObject.BindingFlags.SYNC_CREATE ) self.bn_selected_image_paths = GObjectPropertyBindable(self._file_chooser_inp, 'file-paths') self.bn_frame_interval = GObjectPropertyBindable(self._frame_interval_inp, 'value') self.bn_frame_interval_sensitive = GObjectPropertyBindable(self._frame_interval_inp, 'sensitive') # Set which widget is first focused self._file_chooser_inp.grab_focus() self.presenter.view_ready() return self._widget def _do_destroy(self) -> None: self._widget.destroy()
def _do_init(self, figure_name: str) -> Gtk.Widget: self._widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) self._should_save_figure_inp = Gtk.CheckButton( label='Save {}'.format(figure_name)) self._widget.add(self._should_save_figure_inp) self._more_options = Gtk.Grid(margin_left=30, row_spacing=5, column_spacing=10) self._widget.add(self._more_options) dpi_lbl = Gtk.Label('Figure DPI:', xalign=0) self._more_options.attach(dpi_lbl, 0, 0, 1, 1) dpi_inp_ctn = Gtk.Grid() self._more_options.attach_next_to(dpi_inp_ctn, dpi_lbl, Gtk.PositionType.RIGHT, 1, 1) dpi_inp = IntegerEntry(value=300, lower=72, upper=10000, width_chars=5) dpi_inp.get_style_context().add_class('small-pad') dpi_inp_ctn.add(dpi_inp) dpi_err_lbl = Gtk.Label(xalign=0, width_request=190) dpi_err_lbl.get_style_context().add_class('error-text') self._more_options.attach_next_to(dpi_err_lbl, dpi_inp_ctn, Gtk.PositionType.RIGHT, 1, 1) size_lbl = Gtk.Label('Figure size (cm):', xalign=0) self._more_options.attach(size_lbl, 0, 1, 1, 1) size_inp_ctn = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) self._more_options.attach_next_to(size_inp_ctn, size_lbl, Gtk.PositionType.RIGHT, 1, 1) size_w_lbl = Gtk.Label('W:') size_inp_ctn.add(size_w_lbl) size_w_inp = FloatEntry(value=10, lower=0, upper=10000, width_chars=6) size_w_inp.get_style_context().add_class('small-pad') size_inp_ctn.add(size_w_inp) size_h_lbl = Gtk.Label('H:') size_inp_ctn.add(size_h_lbl) size_h_inp = FloatEntry(value=10, lower=0, upper=10000, width_chars=6) size_h_inp.get_style_context().add_class('small-pad') size_inp_ctn.add(size_h_inp) size_err_lbl = Gtk.Label(xalign=0, width_request=190) size_err_lbl.get_style_context().add_class('error-text') self._more_options.attach_next_to(size_err_lbl, size_inp_ctn, Gtk.PositionType.RIGHT, 1, 1) self._should_save_figure_inp.bind_property( 'active', # source_property self._more_options, # target 'sensitive', # target_property GObject.BindingFlags.SYNC_CREATE, # flags ) self.bn_should_save = GObjectPropertyBindable( g_obj=self._should_save_figure_inp, prop_name='active', ) self.bn_dpi = GObjectPropertyBindable( g_obj=dpi_inp, prop_name='value', ) self.bn_size_w = GObjectPropertyBindable( g_obj=size_w_inp, prop_name='value', ) self.bn_size_h = GObjectPropertyBindable( g_obj=size_h_inp, prop_name='value', ) self.presenter.view_ready() self._widget.show_all() return self._widget
def __init__(self) -> None: super().__init__(row_spacing=10, column_spacing=10) self._max_thresh_inp = Gtk.Adjustment(value=255, lower=1, upper=255) self._min_thresh_inp = Gtk.Adjustment(value=0, lower=0, upper=255) self._min_thresh_rel_inp = Gtk.Adjustment(value=0, lower=0, upper=1) max_thresh_lbl = Gtk.Label('Max threshold:', halign=Gtk.Align.START) self.attach(max_thresh_lbl, 0, 0, 1, 1) max_thresh_ety = FloatEntry(width_chars=5, halign=Gtk.Align.END, xalign=0) max_thresh_ety.get_style_context().add_class('small-pad') max_thresh_ety.get_style_context().add_provider( CannyParameters._STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) self.attach(max_thresh_ety, 2, 0, 1, 1) max_thresh_scl = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self._max_thresh_inp, hexpand=True, draw_value=False) max_thresh_scl.get_style_context().add_class('small-pad') max_thresh_scl.get_style_context().add_provider( CannyParameters._STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) self.attach(max_thresh_scl, 1, 0, 1, 1) min_thresh_lbl = Gtk.Label('Min threshold:', halign=Gtk.Align.START) self.attach(min_thresh_lbl, 0, 1, 1, 1) min_thresh_ety = FloatEntry(width_chars=5, halign=Gtk.Align.END, xalign=0) min_thresh_ety.get_style_context().add_class('small-pad') min_thresh_ety.get_style_context().add_provider( CannyParameters._STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) self.attach(min_thresh_ety, 2, 1, 1, 1) min_thresh_scl = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self._min_thresh_rel_inp, hexpand=True, draw_value=False) min_thresh_scl.get_style_context().add_class('small-pad') min_thresh_scl.get_style_context().add_provider( CannyParameters._STYLE_PROV, Gtk.STYLE_PROVIDER_PRIORITY_USER) self.attach(min_thresh_scl, 1, 1, 1, 1) self.show_all() # Bind the properties of Gtk.Adjustment to the Gtk.Entry widgets for max/min threshold. for (src, targ), prop in itertools.product( ((self._max_thresh_inp, max_thresh_ety), (self._min_thresh_inp, min_thresh_ety)), ('value', 'lower', 'upper')): src.bind_property( prop, # source_property targ, prop, # target, target_property GObject.BindingFlags.BIDIRECTIONAL # flags | GObject.BindingFlags.SYNC_CREATE, lambda _, v: round(v, 1), # transform_to lambda _, v: v # transform_from ) self._max_thresh_inp.bind_property('value', self._min_thresh_inp, 'upper') self._min_thresh_inp.bind_property( 'value', self._min_thresh_rel_inp, 'value', GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE, lambda _, v: v / self._min_thresh_inp.props.upper, lambda _, v: v * self._min_thresh_inp.props.upper) self._notify_min_thresh_rel_inp_when_max_thresh_inp_value_changed_hdl_id = \ self._max_thresh_inp.connect( 'notify::value', # Emit a notify signal on `min_thresh_rel_inp` so that `min_thresh_inp`'s value will be updated to # the correct absolute value (by the binding above) since `max_thresh_inp`'s value has changed. lambda *_: self._min_thresh_rel_inp.notify('value')) self._notify_max_thresh_when_max_thresh_inp_value_changed_hdl_id = \ self._max_thresh_inp.connect( 'value-changed', lambda *_: self.notify('max-thresh')) self._notify_min_thresh_when_min_thresh_inp_value_changed_hdl_id = \ self._min_thresh_inp.connect( 'value-changed', lambda *_: self.notify('min-thresh'))