Ejemplo n.º 1
0
class GradientEditor(tk.Toplevel):
    """The gradient editor window, i.e. the thing that contains the gradient
    display, the function controls and the buttons."""
    def __init__(self, master, vtk_table, on_change_color_table=None):
        """Initialize the gradient editor window.

        Parameters
        ----------
        master
            Owning widget, for example a tk root object.
        VtkTable
            Instance of vtkLookupTable, designating the table which is
            to be edited.
        OnChangeColorTable
            Callback function taking no arguments. Called
            when the color table was changed and rendering is requested."""

        # Inner dimensions of the color control gui-elements in pixels.
        gradient_preview_width = 300
        gradient_preview_height = 50
        channel_function_width = gradient_preview_width
        channel_function_height = 80

        tk.Toplevel.__init__(self, master)
        self.title("Color Gradient Editor")
        self.minsize( gradient_preview_width+4, gradient_preview_height + 5 * \
                      channel_function_height + 50 )

        self.gradient_table = GradientTable(gradient_preview_width)
        self.vtk_color_table = vtk_table

        # create controls.
        self.gradient_control = GradientControl(self, self.gradient_table,
                                                gradient_preview_width,
                                                gradient_preview_height)
        self.gradient_control.grid(row=0, column=1, sticky="we")

        def on_gradient_table_changed(final_update):
            # update all function controls.
            self.function_control_rgb.update()
            for control in self.function_controls_hsv:
                control.update()
            # repaint the gradient display or the external windows only
            # when the instant*** options are set or when the update was final.
            if final_update or (1 == self.show_instant_gradients.get()):
                self.gradient_control.update()

            if final_update or (1 == self.show_instant_feedback.get()):
                self.gradient_table.store_to_vtk_lookup_table(
                    self.vtk_color_table)
                on_change_color_table()

        self.on_gradient_table_changed = on_gradient_table_changed

        self.function_control_rgb = FunctionControl(self, self.gradient_table,
                                                    "rgb",
                                                    channel_function_width,
                                                    channel_function_height,
                                                    on_gradient_table_changed)
        label = tk.Label(self, text="rgb")
        label.grid(row=1, column=0)
        self.function_control_rgb.grid(row=1, column=1, sticky="we")
        self.function_controls_hsv = []
        for it in [("hue", 2), ("sat", 3), ("val", 4), ("alp", 5)]:
            control = FunctionControl(self, self.gradient_table, it[0][0],
                                      channel_function_width,
                                      channel_function_height,
                                      on_gradient_table_changed)
            control.grid(row=it[1], column=1, sticky="we")
            self.function_controls_hsv.append(control)

            label = tk.Label(self, text=it[0])
            label.grid(row=it[1], column=0)

        # buttons and the instruction label get into an own subframe for
        # easier control.
        button_frame = tk.Frame(self)
        button_frame.grid(row=6, column=0, columnspan=2)

        ok_button = tk.Button(button_frame, text="ok", command=self.ok)
        ok_button.grid(row=0, column=1)
        #CancelButton = tk.Button(ButtonFrame, text="cancel", command=self.Cancel)
        #CancelButton.grid(row=0,column=2)
        spacer = tk.Frame(button_frame, width=10)
        spacer.grid(row=0, column=3)
        save_button = tk.Button(button_frame,
                                text="save",
                                command=self.save_gradient)
        save_button.grid(row=0, column=4)
        load_button = tk.Button(button_frame,
                                text="load",
                                command=self.load_gradient)
        load_button.grid(row=0, column=5)
        spacer = tk.Frame(button_frame, width=10)
        spacer.grid(row=0, column=6)
        label = tk.Label(button_frame, text="instant:")
        label.grid(row=0, column=7)

        # these two buttons control whether gradient and render target
        # updates are executed during movement of control points or
        # only at the end of such changes.
        self.show_instant_gradients = tk.IntVar()
        self.show_instant_gradients.set(
            1)  # enable instant gradients by default
        self.show_instant_feedback = tk.IntVar()
        self.show_instant_feedback.set(
            0)  # disable instant feedback by default
        instant_gradient_button = tk.Checkbutton(button_frame, text="grad")
        instant_gradient_button.grid(row=0, column=8)
        instant_gradient_button.configure(variable=self.show_instant_gradients)
        instant_feedback_button = tk.Checkbutton(button_frame, text="feed")
        instant_feedback_button.grid(row=0, column=9)
        instant_feedback_button.configure(variable=self.show_instant_feedback)

        instruction_label = tk.Label(
            button_frame,
            text="left button: move point; right click: toggle point")
        instruction_label.grid(column=0, columnspan=9, row=1)

        # insert a ratio button which decides whether the controls for nonlinear
        # scaling of the gradient are shown and activated.
        self.nonlinear_scaling_enabled = tk.IntVar()
        self.nonlinear_scaling_enabled.set(0)
        nonlinear_enabled_button = tk.Checkbutton(button_frame, text="nonlin")
        nonlinear_enabled_button.grid(column=9, row=1)
        nonlinear_enabled_button.configure(
            variable=self.nonlinear_scaling_enabled,
            command=self.nonlinear_scaling_option_changed)

        # the controls for the nonlinear scaling also get into an own frame.
        # this one can be shown or hidden when the "nonlin"-button is pressed
        nonlin_frame = tk.Frame(self)
        self.nonlin_frame = nonlin_frame

        label = tk.Label(nonlin_frame, text="f(x) =")
        label.grid(row=0, column=0)
        self.nonlinear_function_string = tk.StringVar()
        self.nonlinear_function_string.set("x**(4*a)")
        function_edit = tk.Entry(nonlin_frame,
                                 width=35,
                                 textvariable=self.nonlinear_function_string)
        function_edit.bind("<Return>", self.nonlinear_function_string_changed)
        function_edit.grid(row=0, column=1)

        label = tk.Label(nonlin_frame, text="param a:")
        label.grid(row=1, column=0)
        self.parameter_scale = tk.Scale(nonlin_frame,
                                        from_=0.0,
                                        to=1.0,
                                        resolution=0.001,
                                        length=250,
                                        orient="horizontal")
        self.parameter_scale.bind(
            "<ButtonRelease>", lambda event: self.
            nonlinear_parameter_scale_changed(final_update=True))
        self.parameter_scale.bind(
            "<Motion>", lambda event: self.nonlinear_parameter_scale_changed(
                final_update=False))
        self.parameter_scale.set(0.5)
        self.parameter_scale.grid(row=1, column=1)
        label = tk.Label(nonlin_frame, text= \
                "f(x) should map [0..1] to [0..1]. It rescales the gradient.")
        label.grid(column=0, columnspan=2, row=2)

        # finally, write the current gradient out into main program
        on_gradient_table_changed(final_update=True)

    def nonlinear_scaling_option_changed(self):
        """called when the 'nonlin'-button is pressed to toggle if nonlinear-
        scaling is activated and the corresponding controls are shown"""
        if (1 == self.nonlinear_scaling_enabled.get()):
            # activate the nonlinear scaling controls
            self.nonlin_frame.grid(row=7, column=0, columnspan=2)
            self.nonlinear_parameter_scale_changed(final_update=False)
            self.nonlinear_function_string_changed(None)
        else:
            # disable the nonlinear scaling controls (and the scaling)
            self.nonlin_frame.pack(side=tk.LEFT, anchor=tk.NW)
            self.nonlin_frame.pack_forget()
            self.gradient_table.set_scaling_function("")
            self.on_gradient_table_changed(final_update=True)

    def nonlinear_parameter_scale_changed(self, final_update):
        """Event Handler for the nonlinear-parameter scaling bar. FinalUpdate
        is true on ButtonRelease and False on Motion"""
        self.gradient_table.set_scaling_function_parameter(
            self.parameter_scale.get())
        self.on_gradient_table_changed(final_update=final_update)

    def nonlinear_function_string_changed(self, event):
        """Invoked when Return is pressed in the nonlinear-function edit"""
        self.gradient_table.set_scaling_function(
            self.nonlinear_function_string.get())
        self.on_gradient_table_changed(final_update=True)

    def ok(self):
        self.destroy()

    def save_gradient(self):
        filetypes = [("Gradient Files", "*.grad"), ("All Files", "*")]
        file_name = tkFileDialog.asksaveasfilename(defaultextension=".grad",
                                                   filetypes=filetypes)
        if file_name:
            # there is probably a way to find out which file type the user
            # actually selected. But since I don't know it and also don't really
            # know how to find it out, i rely on this error prone method...
            if (".lut" == file_name[len(file_name) - 4:]):
                self.gradient_table.save(file_name)
            self.gradient_table.save(file_name)

    def load_gradient(self):
        filetypes = [("Gradient Files", "*.grad"), ("All Files", "*")]
        file_name = tkFileDialog.askopenfilename(defaultextension=".grad",
                                                 filetypes=filetypes)
        if file_name:
            self.gradient_table.load(file_name)
            self.on_gradient_table_changed(final_update=True)
            if self.gradient_table.scaling_function:
                self.parameter_scale.set(
                    self.gradient_table.scaling_function_parameter)
                self.nonlinear_function_string.set(
                    self.gradient_table.scaling_function_string)
                self.nonlinear_scaling_enabled.set(1)
                self.nonlinear_scaling_option_changed()
            else:
                self.nonlinear_scaling_enabled.set(0)
                self.nonlinear_scaling_option_changed()
Ejemplo n.º 2
0
class GradientEditor(tk.Toplevel):
    """The gradient editor window, i.e. the thing that contains the gradient
    display, the function controls and the buttons."""
    def __init__(self, master, vtk_table, on_change_color_table = None):
        """Initialize the gradient editor window.
        
        Parameters
        ----------
        master
            Owning widget, for example a tk root object.
        VtkTable 
            Instance of vtkLookupTable, designating the table which is
            to be edited.
        OnChangeColorTable
            Callback function taking no arguments. Called
            when the color table was changed and rendering is requested."""
        
        # Inner dimensions of the color control gui-elements in pixels.
        gradient_preview_width = 300
        gradient_preview_height = 50
        channel_function_width = gradient_preview_width
        channel_function_height = 80
    
        tk.Toplevel.__init__(self, master) 
        self.title("Color Gradient Editor")
        self.minsize( gradient_preview_width+4, gradient_preview_height + 5 * \
                      channel_function_height + 50 )

        self.gradient_table = GradientTable(gradient_preview_width)
        self.vtk_color_table = vtk_table

        # create controls.
        self.gradient_control = GradientControl(self, self.gradient_table,
                                                gradient_preview_width,
                                                gradient_preview_height )
        self.gradient_control.grid(row=0,column=1,sticky="we")

        def on_gradient_table_changed( final_update ):
            # update all function controls.
            self.function_control_rgb.update()
            for control in self.function_controls_hsv:
                control.update()
            # repaint the gradient display or the external windows only
            # when the instant*** options are set or when the update was final.
            if final_update or ( 1 == self.show_instant_gradients.get() ):
                self.gradient_control.update()

            if final_update or ( 1 == self.show_instant_feedback.get() ):
                self.gradient_table.store_to_vtk_lookup_table( self.vtk_color_table )
                on_change_color_table()
        self.on_gradient_table_changed = on_gradient_table_changed
            
        self.function_control_rgb = FunctionControl(self, self.gradient_table,
                "rgb", channel_function_width, channel_function_height,
                on_gradient_table_changed)
        label = tk.Label( self, text = "rgb" )
        label.grid(row=1, column=0)
        self.function_control_rgb.grid(row=1,column=1,sticky="we")
        self.function_controls_hsv = []
        for it in [("hue",2), ("sat",3), ("val",4), ("alp", 5) ]:
            control = FunctionControl(self, self.gradient_table,
                it[0][0], channel_function_width, channel_function_height,
                on_gradient_table_changed )
            control.grid(row=it[1],column=1,sticky="we")
            self.function_controls_hsv.append(control)

            label = tk.Label( self, text = it[0] )
            label.grid(row=it[1], column=0)

        # buttons and the instruction label get into an own subframe for
        # easier control.
        button_frame = tk.Frame(self)
        button_frame.grid(row=6,column=0,columnspan=2)

        ok_button = tk.Button(button_frame, text="ok", command=self.ok)
        ok_button.grid(row=0,column=1)
        #CancelButton = tk.Button(ButtonFrame, text="cancel", command=self.Cancel)
        #CancelButton.grid(row=0,column=2)
        spacer = tk.Frame(button_frame, width=10 )
        spacer.grid(row=0,column=3)
        save_button = tk.Button(button_frame, text="save", command=self.save_gradient)
        save_button.grid(row=0,column=4)
        load_button = tk.Button(button_frame, text="load", command=self.load_gradient)
        load_button.grid(row=0,column=5)
        spacer = tk.Frame(button_frame, width=10 )
        spacer.grid(row=0,column=6)
        label = tk.Label(button_frame,text="instant:")
        label.grid(row=0,column=7)

        # these two buttons control whether gradient and render target
        # updates are executed during movement of control points or
        # only at the end of such changes.
        self.show_instant_gradients = tk.IntVar()
        self.show_instant_gradients.set(1) # enable instant gradients by default
        self.show_instant_feedback = tk.IntVar()
        self.show_instant_feedback.set(0) # disable instant feedback by default
        instant_gradient_button = tk.Checkbutton(button_frame, text="grad")
        instant_gradient_button.grid(row=0,column=8)
        instant_gradient_button.configure(variable=self.show_instant_gradients)
        instant_feedback_button = tk.Checkbutton(button_frame, text="feed")
        instant_feedback_button.grid(row=0,column=9)
        instant_feedback_button.configure(variable=self.show_instant_feedback)

        instruction_label = tk.Label(button_frame,
                text="left button: move point; right click: toggle point")
        instruction_label.grid(column=0,columnspan=9,row=1)

        # insert a ratio button which decides whether the controls for nonlinear
        # scaling of the gradient are shown and activated.
        self.nonlinear_scaling_enabled = tk.IntVar()
        self.nonlinear_scaling_enabled.set(0)
        nonlinear_enabled_button = tk.Checkbutton(button_frame, text="nonlin")
        nonlinear_enabled_button.grid(column=9,row=1)
        nonlinear_enabled_button.configure(variable=self.nonlinear_scaling_enabled,
            command=self.nonlinear_scaling_option_changed)
        
        # the controls for the nonlinear scaling also get into an own frame.
        # this one can be shown or hidden when the "nonlin"-button is pressed
        nonlin_frame = tk.Frame(self)
        self.nonlin_frame = nonlin_frame

        label = tk.Label(nonlin_frame, text="f(x) =")
        label.grid(row=0, column=0)
        self.nonlinear_function_string = tk.StringVar()
        self.nonlinear_function_string.set( "x**(4*a)" )
        function_edit = tk.Entry(nonlin_frame, width=35, 
            textvariable=self.nonlinear_function_string)
        function_edit.bind("<Return>", self.nonlinear_function_string_changed )
        function_edit.grid(row=0, column=1)

        label = tk.Label(nonlin_frame, text="param a:")
        label.grid(row=1, column=0)
        self.parameter_scale = tk.Scale(nonlin_frame, from_=0.0, to=1.0, 
            resolution=0.001, length=250, orient="horizontal")
        self.parameter_scale.bind("<ButtonRelease>",
            lambda event: self.nonlinear_parameter_scale_changed(final_update=True))
        self.parameter_scale.bind("<Motion>",
            lambda event:self.nonlinear_parameter_scale_changed(final_update=False))
        self.parameter_scale.set(0.5)
        self.parameter_scale.grid(row=1, column=1)
        label = tk.Label(nonlin_frame, text= \
                "f(x) should map [0..1] to [0..1]. It rescales the gradient.")
        label.grid(column=0,columnspan=2,row=2)

        # finally, write the current gradient out into main program
        on_gradient_table_changed(final_update = True)

    def nonlinear_scaling_option_changed(self):
        """called when the 'nonlin'-button is pressed to toggle if nonlinear-
        scaling is activated and the corresponding controls are shown"""
        if ( 1 == self.nonlinear_scaling_enabled.get() ):
            # activate the nonlinear scaling controls
            self.nonlin_frame.grid(row=7,column=0,columnspan=2)
            self.nonlinear_parameter_scale_changed(final_update=False)
            self.nonlinear_function_string_changed(None)
        else:
            # disable the nonlinear scaling controls (and the scaling)
            self.nonlin_frame.pack(side=tk.LEFT, anchor=tk.NW)
            self.nonlin_frame.pack_forget()
            self.gradient_table.set_scaling_function("")
            self.on_gradient_table_changed(final_update=True)

    def nonlinear_parameter_scale_changed(self,final_update):
        """Event Handler for the nonlinear-parameter scaling bar. FinalUpdate
        is true on ButtonRelease and False on Motion"""
        self.gradient_table.set_scaling_function_parameter(self.parameter_scale.get())
        self.on_gradient_table_changed(final_update = final_update)

    def nonlinear_function_string_changed(self,event):
        """Invoked when Return is pressed in the nonlinear-function edit"""
        self.gradient_table.set_scaling_function(self.nonlinear_function_string.get())
        self.on_gradient_table_changed(final_update = True)
        
    def ok(self):
        self.destroy()
        
    def save_gradient(self):
        filetypes = [("Gradient Files","*.grad"),("All Files","*")]
        file_name = tkFileDialog.asksaveasfilename(defaultextension=".grad",
                                                   filetypes=filetypes)
        if file_name:
            # there is probably a way to find out which file type the user
            # actually selected. But since I don't know it and also don't really
            # know how to find it out, i rely on this error prone method...
            if ( ".lut" == file_name[len(file_name)-4:] ):
                self.gradient_table.save(file_name)
            self.gradient_table.save(file_name)
        
    def load_gradient(self):
        filetypes = [("Gradient Files","*.grad"), ("All Files","*")]
        file_name = tkFileDialog.askopenfilename(defaultextension=".grad",
                                                 filetypes=filetypes)
        if file_name:
            self.gradient_table.load(file_name)
            self.on_gradient_table_changed(final_update = True)
            if self.gradient_table.scaling_function:
                self.parameter_scale.set(self.gradient_table.scaling_function_parameter)
                self.nonlinear_function_string.set(self.gradient_table.scaling_function_string)
                self.nonlinear_scaling_enabled.set(1)
                self.nonlinear_scaling_option_changed()
            else:
                self.nonlinear_scaling_enabled.set(0)
                self.nonlinear_scaling_option_changed()