-1

I am pretty familiar with python, however right now I am creating my first program with user interface using tkinter. I have a grid layout in my scenario in column 3, row 1 (it is the second row and fourth column I am including row/column 0 here) is a list with items. Initially column 4, row 1 is empty. At later stages in the program it is filled with data (in my scenario coordinates that correspond with the labels from column 3)

below both grid items mentioned above I have a button with a columnspan of 2. however the button breaks when the data is inserted to column 4, row 1.

I thought I could resolve the issue by displaying some empty text (" ") in column 4 in the beginning, however the problem persists. I also tried to limit row 1 growth with using rowconfigure, this approach also failed.

Any ideas how I can avoid the row from growing? I tried breaking down the problem and added the code causing the problem below:

import os
import tkinter as tk
from tkinter import font,messagebox
from PIL import Image, ImageTk, ExifTags
import math


class GUIApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Winkelmessung")

        # Define the custom font
        self.helvetica_font = font.Font(family="Helvetica", size=16, weight="bold")

        # Create a dictionary to store body parts and their coordinates
        self.selected_body_parts = {}

        # Create a list of body parts
        self.body_parts = ["Fußgelenk", "Knie", "Hüfte", "Schulter", "Ohr", "Ellenbogen", "Handgelenk"]
        self.current_body_part_index = 0  # To keep track of the current body part index

        # Create a list of joint angles
        self.joint_angles = ["Knie", "Hüfte", "Schulter", "Nacken", "Ellenbogen"]


        # Add the coordinates selected instance variable
        self.coordinates_selected = 0
        # Initialize the list to store marked coordinates for undo functionality
        self.marked_coordinates = []

        # Load and display the oldest image
        self.display_oldest_image()
        self.create_grid()
        self.create_buttons()

    def create_grid(self):
        # Create custom font
        helvetica_font = font.Font(family="Helvetica", size=16, weight="bold")

        screen_height=self.root.winfo_screenheight()
        new_height = int(screen_height -210)
        image_width = int(new_height*0.666)
        column1u2width= image_width/2
        row3_height= (new_height-275)

        # Create labels with different background colors
        label1 = tk.Label(self.root, text="Manuelle Bildbearbeitung", font=helvetica_font)
        label2 = tk.Label(self.root, text="KI-basierte Bearbeitung", font=helvetica_font)

        # Set column weights
        self.root.columnconfigure(1, minsize=column1u2width)
        self.root.columnconfigure(2, minsize=column1u2width)
        self.root.columnconfigure(3, minsize=125)
        self.root.columnconfigure(4, minsize=125)

        # Set row weights
        self.root.rowconfigure(1, minsize=150)
        self.root.rowconfigure(2, minsize=125)
        self.root.rowconfigure(3, minsize=row3_height)

        # Create a label to display the list of body parts
        body_parts_label = tk.Label(self.root, text="\n".join(self.body_parts), anchor="nw", justify="left")
        body_parts_label.grid(row=1, column=3, rowspan=len(self.body_parts), sticky="nsew", padx=5, pady=10)

        # Create a label to display the list of joint angles in row 2, column 3
        joint_angles_label = tk.Label(self.root, text="\n".join(self.joint_angles), anchor="nw", justify="left")
        joint_angles_label.grid(row=2, column=3, rowspan=len(self.joint_angles), sticky="nsew", padx=5, pady=10)

    def display_oldest_image(self):
        # Define the path to the "Unbearbeitet" folder
        folder_path = "Unbearbeitet"

        # Get a list of image files in the folder
        image_files = [f for f in os.listdir(folder_path) if f.lower().endswith((".jpg", ".png"))]

        # Load the oldest image using Pillow
        oldest_image_path = os.path.join(folder_path, image_files[0])
        oldest_image = Image.open(oldest_image_path)

        # Rotate the image based on Exif orientation
        for orientation in ExifTags.TAGS.keys():
            if ExifTags.TAGS[orientation] == 'Orientation':
                break
        exif = oldest_image._getexif()
        if exif is not None:
            exif = dict(exif.items())
            if orientation in exif:
                if exif[orientation] == 3:
                    oldest_image = oldest_image.rotate(180, expand=True)
                elif exif[orientation] == 6:
                    oldest_image = oldest_image.rotate(270, expand=True)
                elif exif[orientation] == 8:
                    oldest_image = oldest_image.rotate(90, expand=True)

        # Calculate the dimensions for resizing while maintaining aspect ratio
        screen_height = self.root.winfo_screenheight()
        new_height = int(screen_height -210)
        aspect_ratio = oldest_image.width / oldest_image.height
        new_width = int(new_height * aspect_ratio)

        # Resize the image to fit the calculated dimensions
        resized_image = oldest_image.resize((new_width, new_height), Image.LANCZOS)

        # Create a Canvas widget to display the image
        self.canvas = tk.Canvas(self.root)
        self.canvas.grid(row=1, column=1, rowspan=4, columnspan=2, sticky="nsew")  # Span 4 rows

        # Display the image on the Canvas
        self.photo = ImageTk.PhotoImage(resized_image)
        self.canvas.create_image(0, 0, anchor="nw", image=self.photo)

        # Bind click event to the Canvas
        self.canvas.bind("<Button-1>", self.image_click_handler)


    def image_click_handler(self, event):
        # Check if all 7 coordinates have been selected
        if self.coordinates_selected >= len(self.body_parts):
            return

        # Get the clicked coordinates
        x, y = event.x, event.y

        # Get the current body part from the list
        current_body_part = self.body_parts[self.current_body_part_index]

        # Store the body part and its coordinates in the dictionary
        self.selected_body_parts[(x, y)] = current_body_part

        # Mark the selected body part with a dot and label on the canvas
        dot_radius = 5
        dot_color = "red"
        dot_id = self.canvas.create_oval(x - dot_radius, y - dot_radius, x + dot_radius, y + dot_radius, fill=dot_color)
        label_id = self.canvas.create_text(x, y + dot_radius + 10, text=current_body_part, fill=dot_color)

        # Update the current body part index
        self.current_body_part_index += 1
        if self.current_body_part_index >= len(self.body_parts):
            self.current_body_part_index = 0

        # Update the list of body parts in column 4, row 1 (coordinates)
        coordinates_list = [f"({coord[0]}, {coord[1]})" for coord, part in self.selected_body_parts.items()]
        coordinates_label = tk.Label(self.root, text="\n".join(coordinates_list), anchor="nw", justify="left")
        coordinates_label.grid(row=1, column=4, rowspan=len(coordinates_list), sticky="nsew", padx=5, pady=10)

        # Append the marked coordinates to the list for undo functionality
        self.marked_coordinates.append((dot_id, label_id, (x, y), current_body_part))

        # Update the number of coordinates selected
        self.coordinates_selected += 1

        # Check if all coordinates have been selected
        if self.coordinates_selected >= len(self.body_parts):
            # Calculate joint angles
            self.calculate_joint_angles()
            # Disable further clicks
            self.canvas.unbind("<Button-1>")

    def calculate_joint_angles(self):
        # Check if all required body parts are marked
        required_parts = ["Knie", "Hüfte", "Schulter", "Ohr", "Ellenbogen", "Handgelenk"]
        missing_parts = [part for part in required_parts if part not in self.selected_body_parts.values()]

        if missing_parts:
            # Display a message indicating missing body parts
            joint_angles_label = tk.Label(self.root, text=f"Mark the missing body parts: {', '.join(missing_parts)}", anchor="nw", justify="left")
            joint_angles_label.grid(row=2, column=4, sticky="nsew", padx=5, pady=10)
        else:
            # Retrieve the coordinates of the body parts for calculation
            foot_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Fußgelenk")
            knee_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Knie")
            hip_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Hüfte")
            shoulder_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Schulter")
            ear_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Ohr")
            elbow_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Ellenbogen")
            wrist_coords = next(coord for coord, part in self.selected_body_parts.items() if part == "Handgelenk")

            # Calculate the joint angles based on the coordinates
            angles = {
                "Knie": self.calculate_angle(foot_coords, knee_coords, hip_coords),
                "Hüfte": self.calculate_angle(knee_coords, hip_coords, shoulder_coords),
                "Schulter": self.calculate_angle(hip_coords, shoulder_coords, elbow_coords),
                "Nacken": self.calculate_angle(hip_coords, shoulder_coords, ear_coords),
                "Ellenbogen": self.calculate_angle(shoulder_coords, elbow_coords, wrist_coords),
            }

            # Update the joint angles list
            self.joint_angles = [f"{angle:.2f}°" for part, angle in angles.items()]

            # Update the label in row 2, column 4
            joint_angles_label = tk.Label(self.root, text="\n".join(self.joint_angles), anchor="nw", justify="left")
            joint_angles_label.grid(row=2, column=4, rowspan=len(self.joint_angles), sticky="nsew", padx=5, pady=10)

    def calculate_angle(self, start_coord, middle_coord, end_coord):
        # Calculate the vectors representing the lines between the points
        vector1 = (middle_coord[0] - start_coord[0], middle_coord[1] - start_coord[1])
        vector2 = (middle_coord[0] - end_coord[0], middle_coord[1] - end_coord[1])

        # Calculate the dot product of the vectors
        dot_product = vector1[0] * vector2[0] + vector1[1] * vector2[1]

        # Calculate the magnitudes of the vectors
        magnitude1 = math.sqrt(vector1[0] ** 2 + vector1[1] ** 2)
        magnitude2 = math.sqrt(vector2[0] ** 2 + vector2[1] ** 2)

        # Calculate the angle in radians using the arccosine of the dot product divided by the product of magnitudes
        angle_rad = math.acos(dot_product / (magnitude1 * magnitude2))

        # Convert the angle from radians to degrees
        angle_deg = math.degrees(angle_rad)

        return angle_deg

    def create_buttons(self):
        # Button to reset
        reset_button = tk.Button(self.root, text="Zurücksetzen", command=self.reset_function, cursor="hand")
        reset_button.grid(row=5, column=1, sticky="ew", padx=5, pady=10)

        # Button to undo
        undo_button = tk.Button(self.root, text="Rückgängig", command=self.undo_function, cursor="hand")
        undo_button.grid(row=5, column=2, sticky="ew", padx=5, pady=10) 
        
        # Button for "Speichern und weiter"
        save_and_continue_button = tk.Button(self.root, text="Speichern und weiter", command=self.save_and_continue, cursor="hand")
        save_and_continue_button.grid(row=3, column=3, columnspan=2, sticky="new", padx=5, pady=0, ipady=10)

    def ki_analysis_function(self):
        # Implement the behavior of the KI-basierte Bildanalyse button here
        pass

    def live_analysis_function(self):
        # Implement the behavior of the Live-Analyse (KI-basiert) button here
        pass
    
    def reset_function(self):
        # Clear the marked coordinates and selected body parts
        self.selected_body_parts.clear()
        self.coordinates_selected = 0

        # Remove all dots and labels added to the canvas
        for dot_id, label_id, _, _ in self.marked_coordinates:
            self.canvas.delete(dot_id)
            self.canvas.delete(label_id)

        # Clear the coordinates list label in column 4, row 1 (coordinates)
        coordinates_label = tk.Label(self.root, text="", anchor="nw", justify="left")
        coordinates_label.grid(row=1, column=4, rowspan=1, sticky="nsew", padx=5, pady=10)

        # Clear the joint angles label in column 4, row 2 (remove this part)
        joint_angles_label = tk.Label(self.root, text="", anchor="nw", justify="left")
        joint_angles_label.grid(row=2, column=4, rowspan=1, sticky="nsew", padx=5, pady=10)

        # Re-enable clicking on the canvas
        self.canvas.bind("<Button-1>", self.image_click_handler)

        # Clear the marked coordinates list for undo functionality
        self.marked_coordinates = []

        # Reset the current body part index to 0
        self.current_body_part_index = 0

    def undo_function(self):
        if self.marked_coordinates:
            # Get the last marked coordinates and canvas element IDs
            dot_id, label_id, (x, y), body_part = self.marked_coordinates.pop()

            # Remove the marked dot and label from the canvas
            self.canvas.delete(dot_id)
            self.canvas.delete(label_id)

            # Remove the coordinates from the selected body parts dictionary
            for coord in self.selected_body_parts.copy():
                if coord == (x, y):
                    del self.selected_body_parts[coord]

            # Update the list of coordinates in column 4, row 1 (coordinates)
            coordinates_list = [f"({coord[0]}, {coord[1]})" for coord, part in self.selected_body_parts.items()]
            
            # Adjust rowspan to be at least 1 to avoid Tkinter error
            coordinates_label = tk.Label(self.root, text="\n".join(coordinates_list), anchor="nw", justify="left")
            coordinates_label.grid(row=1, column=4, rowspan=max(1, len(coordinates_list)), sticky="nsew", padx=5, pady=10)

            # Decrement the number of coordinates selected
            self.coordinates_selected -= 1

            # Set the current body part index to the index of the undone coordinate
            if self.current_body_part_index == 0:
                self.current_body_part_index = len(self.body_parts) - 1
            else:
                self.current_body_part_index -= 1

            # Re-enable clicking on the canvas
            self.canvas.bind("<Button-1>", self.image_click_handler)

    # Add this method to handle the "Speichern und weiter" button click
    def save_and_continue(self):
        if self.coordinates_selected < len(self.body_parts):
            # If less than 7 points have been marked, show a popup
            messagebox.showwarning("Information", "Bitte markieren Sie zuerst 7 Punkte.")
        else:
            # If 7 points have been marked, proceed with the specified functions
            self.save_excel()
            self.move_image()
            self.next_image()

    def save_excel(self):
        # Implement the save_excel function here
        pass

    def move_image(self):
        # Implement the move_image function here
        pass

    def next_image(self):
        # Implement the next_image function here
        pass

if __name__ == "__main__":
    root = tk.Tk()
    app = GUIApp(root)
    root.mainloop()
BB23
  • 173
  • 9

0 Answers0