yiking/main.py
2024-12-15 18:09:32 +01:00

200 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Heres a detailed prompt you can use to generate the same GUI code anew using a language model:
---
**Prompt:**
"I need a Python program to create a Tkinter-based GUI named 'Yiking'. The GUI will control parameters for an OpenCV image processing program. The program must be split into two files:
1. **`process.py`**: Contains a `process_frame` function that takes parameters as input, performs OpenCV operations (not implemented in this request), and returns an image as a NumPy array and a result string.
2. **`gui.py`**: Implements the GUI and integrates with `process.py`. Here are the detailed requirements:
### GUI Details:
- **Window Title**: 'Yiking'
- **Layout**:
- **Row 1**: Two columns
- Left column: Group of sliders for controlling parameters.
- Right column: A canvas for displaying a processed image (1024x768 pixels).
- **Row 2**: Two columns
- Left column: A "Run" button.
- Right column: A text box for displaying results or error messages.
- **Widgets**:
- Sliders with corresponding min, max, and default values:
- `minDist` = (0, 500, 100)
- `param1` = (0, 500, 30)
- `param2` = (0, 400, 25)
- `minRadius` = (0, 100, 5)
- `maxRadius` = (0, 1000, 1000)
- `color1_R_min` = (0, 64, 5)
- `color1_V_min` = (0, 64, 5)
- `color1_B_min` = (0, 64, 5)
- `color1_R_min` = (0, 64, 5)
- `color1_V_min` = (0, 64, 5)
- `color1_B_min` = (0, 64, 5)
- Sliders must snap to increments of 5 and synchronize with a text entry box.
- The image canvas must resize input images to fit within 1024x768 while maintaining aspect ratio.
- A "Run" button executes the `process_frame` function with the current slider values.
- If an exception occurs in `process_frame`, it should display the error in the result text box and clear the canvas.
### Key Features:
- Use the `Pillow` library (`PIL`) to handle image conversion and resizing.
- Catch exceptions in `run_process` to display error messages.
- Keep the GUI responsive and visually clean.
### Output:
Please provide a fully functional `gui.py` implementation meeting these requirements. Include imports, class structure, and the `__main__` block. Do not implement the OpenCV functionality within `process.py`—assume it exists for integration purposes."
---
This prompt is designed to give the LLM everything it needs to generate the desired `gui.py` code. Let me know if you'd like to adjust it further!
"""
import tkinter as tk
from tkinter import ttk
from process import process_frame
from PIL import Image, ImageTk # Required for displaying images
import cv2 # OpenCV for numpy array to image conversion
class OpenCVInterface:
def __init__(self, root):
self.root = root
self.root.title("Yiking")
# Variables for sliders with min, max, and default values
self.variables_config = {
"minDist": (0, 500, 100),
"param1": (0, 500, 30),
"param2": (0, 400, 25),
"minRadius": (0, 100, 5),
"maxRadius": (0, 1000, 1000),
"color1_R_min": (0, 64, 5),
"color1_R_max": (0, 64, 5),
"color1_V_min": (0, 64, 5),
"color1_V_max": (0, 64, 5),
"color1_B_min": (0, 64, 5),
"color1_B_max": (0, 64, 5),
}
self.variables = {
name: tk.IntVar(value=config[2]) for name, config in self.variables_config.items()
}
# GUI Layout
self.setup_gui()
def setup_gui(self):
# Root grid layout
self.root.rowconfigure(1, weight=1)
self.root.columnconfigure(0, weight=1)
self.root.columnconfigure(1, weight=1)
# Dropdown for camera selection
camera_frame = ttk.Frame(self.root)
camera_frame.grid(row=0, column=0, sticky="nsew")
self.camera_selection = tk.StringVar()
self.camera_dropdown = ttk.Combobox(camera_frame, textvariable=self.camera_selection)
self.camera_dropdown.grid(row=0, column=0, padx=5, pady=5)
self.populate_camera_dropdown()
# Left Column: Sliders
left_frame = ttk.Frame(self.root)
left_frame.grid(row=1, column=0, sticky="nswe")
for var_name, var in self.variables.items():
min_val, max_val, _ = self.variables_config[var_name]
self.create_slider(left_frame, var_name, var, min_val, max_val)
# Right Column: Image Placeholder
self.image_canvas = tk.Canvas(self.root, bg="gray", width=1024, height=768)
self.image_canvas.grid(row=0, column=1, rowspan=2, sticky="nswe")
# Bottom Row: Run Button and Result
run_button = ttk.Button(self.root, text="Run", command=self.run_process)
run_button.grid(row=2, column=0, sticky="we")
self.result_text = tk.Text(self.root, height=5, width=40)
self.result_text.grid(row=2, column=1, sticky="nswe")
def populate_camera_dropdown(self):
# Detect connected cameras using OpenCV
cameras = []
for i in range(5): # Check first 5 indexes for cameras
cap = cv2.VideoCapture(i)
if cap.read()[0]:
cameras.append(f"Camera {i}")
cap.release()
self.camera_dropdown["values"] = cameras
if cameras:
self.camera_dropdown.current(0)
def create_slider(self, parent, name, variable, min_val, max_val):
frame = ttk.Frame(parent)
frame.pack(fill="x", padx=5, pady=2)
# Label
label = ttk.Label(frame, text=name)
label.pack(side="left")
def on_slide(value):
# Round value to nearest multiple of 5
rounded_value = round(float(value) / 5) * 5
variable.set(int(rounded_value)) # Update the variable with the rounded value
# Slider
slider = ttk.Scale(
frame, from_=min_val, to=max_val,
variable=variable, orient="horizontal", command=on_slide
)
slider.pack(side="left", fill="x", expand=True, padx=5)
# Entry box
entry = ttk.Entry(frame, textvariable=variable, width=5)
entry.pack(side="left")
def run_process(self):
# Collect slider values
parameters = {key: var.get() for key, var in self.variables.items()}
try:
# Get selected camera ID
cam_id = int(self.camera_selection.get().split()[-1])
# Call process function
image, result_text = process_frame(parameters, cam_id=cam_id)
# Convert OpenCV image (numpy array) to PIL Image for Tkinter display
if image is not None:
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # Convert BGR to RGB
pil_image = Image.fromarray(image) # Convert numpy array to PIL Image
# Rescale image to fit within 1024x768 while preserving aspect ratio
max_width, max_height = 1024, 768
original_width, original_height = pil_image.size
aspect_ratio = min(max_width / original_width, max_height / original_height)
new_width = int(original_width * aspect_ratio)
new_height = int(original_height * aspect_ratio)
pil_image = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
tk_image = ImageTk.PhotoImage(pil_image) # Convert PIL Image to Tkinter Image
# Clear canvas and display the image
self.image_canvas.delete("all")
self.image_canvas.create_image(512, 384, image=tk_image, anchor="center")
self.image_canvas.image = tk_image # Keep a reference to prevent garbage collection
# Update result text
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, result_text)
except Exception as exc:
# Handle and display exceptions
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, str(exc))
self.image_canvas.delete("all")
if __name__ == "__main__":
root = tk.Tk()
app = OpenCVInterface(root)
root.mainloop()