Ejemplo n.º 1
0
    def set_stage2(self):
        # Update image title
        self.ref_object_var.set("Selected Reference Object")

        # Update instructions
        self.instructions_var.set(
            "Now please tell us how long is the dimension given by the pink line in the image.\n"
            "Provide the most decimals for more accurate results!")

        # Hide stage 1
        for (widget, grid_command) in self.stage1_widgets:
            widget.grid_forget()

        # Show stage 2
        for (widget, grid_command) in self.stage2_widgets:
            grid_command()

        # Update responsive image
        if self.responsive_image is not None:
            self.responsive_image.grid(row=1,
                                       column=0,
                                       rowspan=8,
                                       columnspan=3,
                                       sticky=NSEW,
                                       pady=20)

        # Update responsive
        reset_both_responsive(self)
        make_columns_responsive(self, ignored=[0, 1])
        make_rows_responsive(self, ignored=[0, 2, 3, 4, 5, 6, 7])
Ejemplo n.º 2
0
    def set_stage1(self):
        current = self.current_contour_var.get()
        # Update image title
        self.ref_object_var.set("Object #" + str(current + 1))

        # Update instructions
        self.instructions_var.set(
            "Choose an object for which you know its width or height.\n"
            "This will allow to calculate the real dimensions of the bamboo slice."
        )

        # Hide stage 2
        for (widget, grid_command) in self.stage2_widgets:
            widget.grid_forget()

        # Hide invalid message
        self.invalid_dimension.grid_forget()

        # Show stage 1
        for (widget, grid_command) in self.stage1_widgets:
            grid_command()

        # Update responsive image
        if self.responsive_image is not None:
            self.responsive_image.grid(row=1,
                                       column=0,
                                       rowspan=2,
                                       columnspan=3,
                                       sticky=NSEW,
                                       pady=20)

        # Update responsive
        reset_both_responsive(self)
        make_columns_responsive(self, ignored=[1, 2])
        make_rows_responsive(self, ignored=[0])
Ejemplo n.º 3
0
    def initialize_widgets(self):
        # Watchers
        # Update captured count
        self.count_str = StringVar()
        self.count_number = IntVar()
        self.count_number.trace("w", self.update_count_label)

        # Status message
        self.status_var = StringVar()
        self.status_message = Label(self, textvariable=self.status_var, font=self.controller.header_font, relief=GROOVE,
                                    padx=10, pady=10)
        self.status_message.grid(row=0, column=0, sticky=EW, padx=40, pady=20)

        # Table headers
        top_headers = ["Sensor #", "Current\nreading", "Last\ncaptured\nvalue", "Last\ndeviation"]
        self.table_headers = VerticalTable(self, rows=1, columns=len(top_headers))
        self.table_headers.update_cells(top_headers)
        self.table_headers.grid(row=1, column=0, sticky=S)

        # column indexes
        self.live_column = 0
        self.captured_column = 1
        self.deviation_column = 2

        # Sensor Data: current readings, last captured, and deviation info
        sensor_headers = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "Sensor Z"]
        self.table = HorizontalTable(self, rows=len(sensor_headers), columns=3, header_values=sensor_headers)
        self.table.grid(row=2, column=0, rowspan=2, sticky=N)

        # calibrate button
        self.calibrate_button = GreenButton(self, text="Calibrate Sensors", command=self.calibrate)
        self.calibrate_button.grid(row=0, column=1, pady=20)

        # captured count
        self.captured_count = Label(self, textvariable=self.count_str, font=self.controller.bold_font)
        self.captured_count.grid(row=2, column=1, sticky=S, pady=10)

        # capture button
        self.capture_button = YellowButton(self, text="Capture Measurements", command=self.capture)
        self.capture_button.grid(row=3, column=1, sticky=N)

        # view results button
        self.results_button = GreenButton(self, text="View Results", command=self.view_results,
                                          image=self.controller.arrow_right, compound=RIGHT)
        self.results_button.grid(row=4, column=1, sticky=SE, padx=20, pady=20)

        make_rows_responsive(self)
        make_columns_responsive(self)
Ejemplo n.º 4
0
    def initialize_widgets(self):
        # Watchers

        # on image path change
        self.image_path = StringVar()
        self.image_path.trace("w", self.on_image_path_change)

        # responsive image container
        self.placeholder_image = Image.open("assets/placeholder_image.png")
        self.responsive_image = ResponsiveImage(self, self.placeholder_image)
        self.responsive_image.grid(row=0, column=0, rowspan=4)

        # choose image button
        self.choose_button = GreenButton(self,
                                         text="Choose an image",
                                         command=self.load_image)
        self.choose_button.grid(row=0, column=1, sticky=S)

        # selected image path
        self.path_entry = Entry(self,
                                textvariable=self.image_path,
                                state="readonly")
        self.path_entry.grid(row=1, column=1, sticky=EW, padx=20)

        # status message in row 2
        self.message_var = StringVar()
        self.message = Label(self,
                             textvariable=self.message_var,
                             font=self.controller.header_font)

        # begin button
        self.begin_button = YellowButton(self,
                                         text="BEGIN",
                                         command=self.begin,
                                         image=self.controller.arrow_right,
                                         compound=RIGHT)
        self.begin_button.grid(row=3, column=1, sticky=SE, padx=20, pady=20)

        # Update widgets
        self.on_image_path_change()

        # visited flag
        self.visit_counter = 0

        make_rows_responsive(self)
        make_columns_responsive(self)
Ejemplo n.º 5
0
    def initialize_widgets(self):

        # Result image row=0, col=0, columnspan=2

        # Save button
        self.save_button = YellowButton(self, text="Save coordinates", command=self.save, image=self.controller.save_icon,
                                        compound=LEFT)
        self.save_button.grid(row=2, column=0, sticky=E, padx=10, pady=20)

        # Discard button
        self.discard_button = RedButton(self, text="DISCARD", command=self.discard)
        self.discard_button.grid(row=2, column=1, sticky=W, padx=10, pady=20)

        # min size of buttons row
        self.grid_rowconfigure(2, minsize=80)

        make_rows_responsive(self, ignored=[0])
        make_columns_responsive(self)
Ejemplo n.º 6
0
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller
        self.bind("<<ShowFrame>>", self.on_show_frame)

        # Bamboo Scanner logo
        logo_image = ImageTk.PhotoImage(Image.open("assets/logo.png"))
        self.logo = Label(self, image=logo_image)
        self.logo.image = logo_image
        self.logo.grid(row=0, columnspan=2, sticky=S)

        # Buttons font
        buttons_font = font.Font(family="Segoe UI Emoji", size=26)

        # BPC
        self.bpc_button = GreenButton(self,
                                      text="Pole",
                                      command=self.open_bpc,
                                      font=buttons_font)
        self.bpc_button.grid(row=1, column=0, sticky=S)

        self.bpc_description = Label(
            self,
            fg="#333333",
            text="Measure the external surface\n of a bamboo pole")
        self.bpc_description.grid(row=2, column=0, rowspan=2, sticky=N)

        # BSC
        self.bsc_button = GreenButton(self,
                                      text="Slice",
                                      command=self.open_bsc,
                                      font=buttons_font)
        self.bsc_button.grid(row=1, column=1, sticky=S)

        self.bsc_description = Label(
            self,
            fg="#333333",
            text=
            "Measure the inner and outer\n circumferences of a\n bamboo cross section"
        )
        self.bsc_description.grid(row=2, column=1, rowspan=3, sticky=N)

        make_rows_responsive(self)
        make_columns_responsive(self)
Ejemplo n.º 7
0
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller = controller
        self.bind("<<ShowFrame>>", self.on_show_frame)

        # Bamboo Scanner logo
        logo_image = ImageTk.PhotoImage(Image.open("assets/logo.png"))
        self.logo = Label(self, image=logo_image)
        self.logo.image = logo_image
        self.logo.grid(row=0, columnspan=2, sticky=S)

        # select a tool label
        self.select_label = Label(self, text="Select a tool:", font=self.controller.important_font, fg="grey")
        self.select_label.grid(row=1, column=0, columnspan=2, sticky=S)

        # Buttons font
        buttons_font = font.Font(family="Segoe UI Emoji", size=26)

        # BPC
        self.bpc_button = GreenButton(self, text="Pole", command=self.open_bpc, font=buttons_font)
        self.bpc_button.grid(row=2, column=0, sticky=S)

        self.bpc_description = Label(self, fg="#333333", text="Measure the external surface\n of a bamboo pole")
        self.bpc_description.grid(row=3, column=0, rowspan=2, sticky=N)

        # BSC
        self.bsc_button = GreenButton(self, text="Slice", command=self.open_bsc, font=buttons_font)
        self.bsc_button.grid(row=2, column=1, sticky=S)

        self.bsc_description = Label(self, fg="#333333",
                                     text="Measure the inner and outer\n circumferences of a\n bamboo cross section")
        self.bsc_description.grid(row=3, column=1, rowspan=3, sticky=N)

        # guide link
        self.guide_button = Button(self, text="First time? Open tutorial", relief=FLAT, command=self.open_guide,
                                   fg="blue", font=self.controller.underlined_font)
        self.guide_button.grid(row=4, columnspan=2)

        make_rows_responsive(self)
        make_columns_responsive(self)
Ejemplo n.º 8
0
    def initialize_widgets(self):
        # Empty message
        self.empty_message = Label(
            self,
            text="Nothing to see here. Go capture some measurements!",
            font=self.controller.header_font)

        # Save button
        self.save_button = YellowButton(self,
                                        text="Save coordinates",
                                        command=self.save,
                                        image=self.controller.save_icon,
                                        compound=LEFT)
        self.save_button.grid(row=1, column=0, sticky=SE, padx=10, pady=20)

        # Discard button
        self.discard_button = RedButton(self,
                                        text="DISCARD",
                                        command=self.discard)
        self.discard_button.grid(row=1, column=1, sticky=SW, padx=10, pady=20)

        make_rows_responsive(self)
        make_columns_responsive(self)
Ejemplo n.º 9
0
    def initialize_widgets(self):
        # a canvas with scrollbars; the results table goes in it
        h_scrollbar = AutoScrollbar(self, orient=HORIZONTAL)
        h_scrollbar.grid(row=1, column=0, columnspan=2, sticky=EW)
        v_scrollbar = AutoScrollbar(self)
        v_scrollbar.grid(row=0, column=2, sticky=NS)

        self.canvas = Canvas(self,
                             xscrollcommand=h_scrollbar.set,
                             yscrollcommand=v_scrollbar.set)
        h_scrollbar.config(command=self.canvas.xview)
        v_scrollbar.config(command=self.canvas.yview)

        # Empty message
        self.empty_message = Label(
            self,
            text="Nothing to see here. Go capture some measurements!",
            font=self.controller.header_font)

        # Save button
        self.save_button = YellowButton(self,
                                        text="Save coordinates",
                                        command=self.save,
                                        image=self.controller.save_icon,
                                        compound=LEFT)
        self.save_button.grid(row=2, column=0, sticky=S, pady=20)

        # Discard button
        self.discard_button = RedButton(self,
                                        text="DISCARD",
                                        command=self.discard)
        self.discard_button.grid(row=2, column=1, sticky=S, pady=20)

        # responsive except the scrollbars and the buttons
        make_rows_responsive(self, ignored=[1, 2])
        make_columns_responsive(self, ignored=[2])
Ejemplo n.º 10
0
    def initialize_widgets(self):
        # Watchers

        # Update state of navigation buttons
        self.current_circumference_var = IntVar()
        self.current_circumference_var.trace("w", self.update_navigation)

        # Update count label
        self.selected_count_var = IntVar()
        self.selected_count_var.trace("w", self.update_count_text)

        # Update buttons
        self.selected_count_text = StringVar()
        self.selected_count_text.trace("w", self.update_buttons)

        # title of circumference image
        self.circumference_title_var = StringVar()
        self.circumference_title = Label(
            self,
            textvariable=self.circumference_title_var,
            font=self.controller.header_font)
        self.circumference_title.grid(row=0,
                                      column=0,
                                      sticky=W,
                                      padx=20,
                                      pady=20)

        # Controls to navigate contours
        self.prev_button = GreenButton(
            self,
            text="Previous",
            image=self.controller.arrow_left,
            compound=LEFT,
            command=lambda: self.show_circumference(
                self.current_circumference_var.get() - 1))
        self.prev_button.grid(row=0, column=1, sticky=SE, padx=5, pady=20)

        self.next_button = GreenButton(
            self,
            text="Next",
            image=self.controller.arrow_right,
            compound=RIGHT,
            command=lambda: self.show_circumference(
                self.current_circumference_var.get() + 1))
        self.next_button.grid(row=0, column=2, sticky=SW, pady=20)

        # image in row=1, col=0, colspan=3, rowspan=4

        # instructions
        instructions_text = "More than 2 circumferences were found.\n"
        instructions_text += "Select the inner and outer circumference of the bamboo slice."
        self.instructions = Label(self,
                                  text=instructions_text,
                                  relief=GROOVE,
                                  padx=10,
                                  pady=10)
        self.instructions.grid(row=1, column=3, padx=40)

        # remove button
        self.remove_button = RedButton(self,
                                       text="Deselect circumference",
                                       command=self.deselect)
        self.remove_button.grid(row=2, column=3)

        # select button
        self.select_button = YellowButton(self,
                                          text="Use this circumference",
                                          command=self.select)
        self.select_button.grid(row=2, column=3)

        # Selection count
        self.selected_count = Label(self,
                                    textvariable=self.selected_count_text,
                                    font=self.controller.important_font)
        self.selected_count.grid(row=3, column=3, sticky=N)

        # confirm button
        self.confirm_button = YellowButton(self,
                                           text="CONFIRM",
                                           command=self.confirm,
                                           state=DISABLED,
                                           cursor="arrow")
        self.confirm_button.grid(row=4, column=3, sticky=SE, padx=20, pady=20)

        make_columns_responsive(self, ignored=[1, 2])
        make_rows_responsive(self, ignored=[0])
Ejemplo n.º 11
0
    def initialize_widgets(self):
        # CALIBRATION

        # Ring diameter entry value
        self.ring_diameter_var = StringVar()
        self.ring_diameter_var.trace("w", self.update_begin_button)

        # Calibration object value
        self.calibration_object_var = StringVar()
        self.calibration_object_var.trace("w", self.update_begin_button)

        # Distance to end of rail value
        self.distance_z_var = StringVar()
        self.distance_z_var.trace("w", self.update_begin_button)

        # %d = Type of action (1=insert, 0=delete, -1 for others)
        # %P = value of the entry if the edit is allowed
        # %S = the text string being inserted or deleted, if any
        validate_cmd = (self.register(self.validate_dimension), '%d', '%P',
                        '%S')

        # Calibration settings group
        self.calibration_settings = LabelFrame(
            self,
            text="Calibration Settings",
            fg="grey",
            padx=20,
            pady=20,
            font=self.controller.header_font)
        self.calibration_settings.grid(row=0,
                                       column=0,
                                       sticky=NS + W,
                                       padx=20,
                                       pady=20)

        # Ring diameter
        self.ring_diameter_label = Label(self.calibration_settings,
                                         text="Ring structure diameter",
                                         anchor=SW,
                                         font=self.controller.bold_font)
        self.ring_diameter_label.grid(row=0, column=0, sticky=SW, padx=20)

        self.ring_diameter_entry = EntryWithPlaceholder(
            self.calibration_settings,
            text_var=self.ring_diameter_var,
            placeholder_text="0.00",
            validatecommand=validate_cmd,
            validate="key",
            textvariable=self.ring_diameter_var)
        self.ring_diameter_entry.grid(row=1,
                                      column=0,
                                      sticky=NW,
                                      padx=20,
                                      pady=20)

        # Calibration object
        self.calibration_object_label = Label(self.calibration_settings,
                                              text="Calibration object radius",
                                              font=self.controller.bold_font,
                                              anchor=SW)
        self.calibration_object_label.grid(row=0, column=1, sticky=SW, padx=20)

        self.calibration_object_entry = EntryWithPlaceholder(
            self.calibration_settings,
            text_var=self.calibration_object_var,
            placeholder_text="0.00",
            validatecommand=validate_cmd,
            textvariable=self.calibration_object_var,
            validate="key")
        self.calibration_object_entry.grid(row=1,
                                           column=1,
                                           sticky=NW,
                                           padx=20,
                                           pady=20)

        # Distance to flat surface at end of rail
        self.distance_z_label = Label(self.calibration_settings,
                                      text="Distance to the end of the rail",
                                      anchor=SW,
                                      font=self.controller.bold_font)
        self.distance_z_label.grid(row=2, column=0, sticky=SW, padx=20)

        self.distance_z_entry = EntryWithPlaceholder(
            self.calibration_settings,
            text_var=self.distance_z_var,
            placeholder_text="0.00",
            validate="key",
            validatecommand=validate_cmd,
            textvariable=self.distance_z_var)
        self.distance_z_entry.grid(row=3,
                                   column=0,
                                   sticky=NW,
                                   padx=20,
                                   pady=20)

        # Invalid dimension message
        self.invalid_dimension = Label(
            self.calibration_settings,
            text="Dimension must be between 1 and 28 centimeters",
            fg="red",
            anchor=W)

        # make calibration section responsive
        make_columns_responsive(self.calibration_settings)
        make_rows_responsive(self.calibration_settings)

        # description label
        self.description_label = Label(
            self,
            text="Information about the sample (optional)",
            font=self.controller.bold_font)
        self.description_label.grid(row=1,
                                    column=0,
                                    sticky=SW,
                                    padx=20,
                                    pady=20)

        # Description text area
        self.text_area = ScrollableTextArea(self)
        self.text_area.grid(row=2, column=0, sticky=NW, padx=20)

        # begin button
        self.begin_button = YellowButton(self,
                                         text="BEGIN",
                                         command=self.begin,
                                         image=self.controller.arrow_right,
                                         compound=RIGHT)
        self.begin_button.grid(row=3, column=0, sticky=SE, padx=20, pady=20)

        # set placeholders
        self.ring_diameter_entry.set_placeholder()
        self.calibration_object_entry.set_placeholder()
        self.distance_z_entry.set_placeholder()

        make_rows_responsive(self)
        make_columns_responsive(self)
Ejemplo n.º 12
0
    def __init__(self):
        Tk.__init__(self)

        # START SHARED

        # Global fonts
        self.global_font_family = font.Font(family="Segoe UI Emoji")
        self.common_font = font.Font(family="Segoe UI Emoji", size=14)
        self.bold_font = font.Font(family="Segoe UI Emoji",
                                   size=13,
                                   weight="bold")
        self.header_font = font.Font(family="Segoe UI Emoji",
                                     size=16,
                                     weight="bold")
        self.title_font = font.Font(family="Segoe UI Emoji",
                                    size=28,
                                    weight="bold")
        self.important_font = font.Font(family="Segoe UI Emoji",
                                        size=28,
                                        weight="bold",
                                        underline=True)

        # update widget fonts
        self.option_add("*Font", self.global_font_family)
        self.option_add("*Button.Font", self.bold_font)
        self.option_add("*Label.Font", self.common_font)

        # Common images
        self.arrow_left = ImageTk.PhotoImage(
            Image.open("assets/arrow_left.png"))
        self.arrow_right = ImageTk.PhotoImage(
            Image.open("assets/arrow_right.png"))
        self.save_icon = ImageTk.PhotoImage(Image.open("assets/save.png"))

        # END SHARED

        # Main page layout: hosts page content and global widgets
        self.container = Frame(self)
        # Container fills the entire window
        self.container.pack(side=TOP, fill=BOTH, expand=True)

        # Navigation bar
        self.navbar = Frame(self.container, bg="#35AD35")
        self.navbar.grid(row=0, columnspan=2, sticky=NSEW)

        # Home button
        home_image = Image.open("assets/home.png")
        # resize home button image
        home_image = resize_keep_aspect(home_image, max_w=100, max_h=37)
        home_image = ImageTk.PhotoImage(home_image)
        self.home_button = Label(self.navbar,
                                 image=home_image,
                                 cursor="hand2",
                                 text="Home")
        self.home_button.image = home_image
        self.home_button.grid(row=0, column=0, sticky=NSEW)
        # bind to click event
        self.home_button.bind("<Button-1>", self.go_home)
        self.home_button.bind("<Enter>", self.on_enter_home_btn)
        self.home_button.bind("<Leave>", self.on_leave_home_btn)

        # Back button
        self.back_button = Button(self.navbar,
                                  image=self.arrow_left,
                                  cursor="hand2",
                                  text="Go back",
                                  compound=LEFT,
                                  command=self.go_back,
                                  bg="#35AD35",
                                  fg="#FFFFFF",
                                  relief=GROOVE,
                                  padx=10)
        self.back_button.grid(row=0, column=1, sticky=NSEW)

        # Page title
        self.title_var = StringVar()
        self.page_title = Label(self.navbar,
                                textvariable=self.title_var,
                                bg="#35AD35",
                                fg="#FFFFFF",
                                font=self.title_font)
        self.page_title.grid(row=0, column=2, sticky=W + E, padx=10)

        # Step indicator
        self.step = StringVar()
        self.page_step = Label(self.navbar,
                               textvariable=self.step,
                               bg="#35AD35",
                               fg="#FFFFFF")
        self.page_step.grid(row=0, column=3, sticky=E, padx=10)

        # only the page title is responsive
        make_columns_responsive(self.navbar, ignored=[0, 1, 3])

        # Bamboo decor on left side of screen
        bamboo_image = Image.open("assets/bamboo.png")
        self.bamboo = ResponsiveImage(self.container,
                                      bamboo_image,
                                      tag="bamboo",
                                      anchor=NW)
        self.bamboo.grid(row=1, column=0, sticky=NSEW)

        # The class names for both BSC and BPC
        self.bsc_pages = (ConfigBSC, PickCircumferencesBSC, RefObjectBSC,
                          ResultsBSC)
        self.bpc_pages = (ConfigBPC, MeasureBPC, ResultsBPC)

        # Initialize all pages and keep their references accessible
        self.frames = {}
        for F in (() + (Home, ) + self.bsc_pages + self.bpc_pages):
            page_name = F.__name__
            frame = F(parent=self.container, controller=self)
            self.frames[page_name] = frame

            # Add all frames to the container, on top of each other
            frame.grid(row=1, column=1, sticky=NSEW)

        # make the window responsive, except the navbar row
        make_rows_responsive(self.container, ignored=[0])
        make_columns_responsive(self.container)

        # Start on the home page
        self.active_frame = None
        self.show_frame("Home")
Ejemplo n.º 13
0
    def initialize_widgets(self):
        # CALIBRATION

        # Ring diameter entry value
        self.ring_diameter_var = StringVar()
        self.ring_diameter_var.trace("w", self.update_begin_button)

        # Calibration object value
        self.calibration_object_var = StringVar()
        self.calibration_object_var.trace("w", self.update_begin_button)

        # Distance to end of rail value
        self.distance_z_var = StringVar()
        self.distance_z_var.trace("w", self.update_begin_button)

        # %d = Type of action (1=insert, 0=delete, -1 for others)
        # %P = value of the entry if the edit is allowed
        # %S = the text string being inserted or deleted, if any
        # %W = the name of the widget
        validate_cmd = (self.register(self.validate_calibration_settings), '%d', '%P', '%S', '%W')

        # Calibration settings group
        self.calibration_settings = LabelFrame(self, text="Calibration Settings (all measures in centimeters)",
                                               fg="grey", padx=20, pady=20, font=self.controller.header_font)
        self.calibration_settings.grid(row=0, column=0, sticky=NSEW, padx=20, pady=20)

        # Ring diameter
        self.ring_diameter_label = Label(self.calibration_settings, text="Ring structure diameter", anchor=SW,
                                         font=self.controller.bold_font)
        self.ring_diameter_label.grid(row=0, column=0, sticky=SW, padx=20)

        # ring diameter range
        self.range_ring_diameter = Label(self.calibration_settings, text="[Valid range: 10 - 30]", fg="grey", anchor=SW,
                                         font=self.controller.small_font)
        self.range_ring_diameter.grid(row=1, column=0, sticky=NW, padx=20)

        self.ring_diameter_entry = EntryWithPlaceholder(self.calibration_settings, text_var=self.ring_diameter_var,
                                                        placeholder_text="0.00", validatecommand=validate_cmd,
                                                        validate="key", textvariable=self.ring_diameter_var,
                                                        name="ring")
        self.ring_diameter_entry.grid(row=2, column=0, sticky=NW, padx=20, pady=20)

        # Calibration object
        self.calibration_object_label = Label(self.calibration_settings, text="Calibration object diameter",
                                              font=self.controller.bold_font, anchor=SW)
        self.calibration_object_label.grid(row=0, column=1, sticky=SW, padx=20)

        # calibration object diameter range
        self.range_calibration_obj_diameter = Label(self.calibration_settings, text="[Valid range: 2 - 26]", fg="grey",
                                                    anchor=NW, font=self.controller.small_font)
        self.range_calibration_obj_diameter.grid(row=1, column=1, sticky=NW, padx=20)

        self.calibration_object_entry = EntryWithPlaceholder(self.calibration_settings, text_var=self.calibration_object_var,
                                                             placeholder_text="0.00", validatecommand=validate_cmd,
                                                             textvariable=self.calibration_object_var, validate="key",
                                                             name="calibration_obj")
        self.calibration_object_entry.grid(row=2, column=1, sticky=NW, padx=20, pady=20)

        # Distance to flat surface at end of rail
        self.distance_z_label = Label(self.calibration_settings, text="Distance to the end of the rail", anchor=SW,
                                      font = self.controller.bold_font)
        self.distance_z_label.grid(row=3, column=0, sticky=SW, padx=20)

        # z distance range
        self.range_z_distance = Label(self.calibration_settings, text="[Valid range: 15.24 - 645]", fg="grey", anchor=NW,
                                      font=self.controller.small_font)
        self.range_z_distance.grid(row=4, column=0, sticky=NW, padx=20)

        self.distance_z_entry = EntryWithPlaceholder(self.calibration_settings, text_var=self.distance_z_var,
                                                     placeholder_text="0.00", validate="key", name="z_distance",
                                                     validatecommand=validate_cmd, textvariable=self.distance_z_var)
        self.distance_z_entry.grid(row=5, column=0, sticky=NW, padx=20, pady=20)

        # calibration object diameter greater than ring diameter
        self.calibration_obj_greater_ring = Label(self.calibration_settings,
                                                  text="The calibration object's diameter\ncan't be greater than the ring diameter",
                                                  fg="red", anchor=NW)

        # make calibration section responsive
        make_columns_responsive(self.calibration_settings)
        make_rows_responsive(self.calibration_settings)

        # description label
        self.description_label = Label(self, text="Information about the bamboo sample (optional)",
                                       font=self.controller.bold_font)
        self.description_label.grid(row=1, column=0, sticky=SW, padx=20, pady=20)

        # Description text area
        self.text_area = ScrollableTextArea(self)
        self.text_area.grid(row=2, column=0, sticky=NW, padx=20)

        # begin button
        self.begin_button = YellowButton(self, text="BEGIN", command=self.begin,
                                         image=self.controller.arrow_right, compound=RIGHT)
        self.begin_button.grid(row=3, column=0, sticky=SE, padx=20, pady=20)

        # set placeholders
        self.ring_diameter_entry.set_placeholder()
        self.calibration_object_entry.set_placeholder()
        self.distance_z_entry.set_placeholder()

        make_rows_responsive(self)
        make_columns_responsive(self)

        # min size of buttons row
        self.grid_rowconfigure(3, minsize=80)