Adding a User-Controlled Camera Feature To Our Python GUI Clock

Scofield Idehen - Jun 30 - - Dev Community

In our previous tutorial, we built a customizable GUI clock using Python and Tkinter. Let's take it a step further by adding a camera feature that allows users to capture and save images on demand. This project will introduce you to working with camera input in Python, enhancing your skills in GUI development and file handling.

Setting Up the Environment

Before we begin, ensure you have the necessary libraries installed. We'll be using OpenCV for camera handling. Install it using pip:

pip install opencv-python
Enter fullscreen mode Exit fullscreen mode

Next, we are going to Install Pillow using pip.

pip install Pillow
Enter fullscreen mode Exit fullscreen mode

Now that we have installed all the dependencies, we can add the camera. We are going to create two kinds of cameras: a regular camera and a camera hidden behind clicks.

Stay with me.

    import cv2
    from PIL import Image, ImageTk
    import os
    from datetime import datetime
Enter fullscreen mode Exit fullscreen mode

Creating the Camera Function

Let's add a function to handle camera capture:

    def capture_image():
        # Initialize the camera
        cap = cv2.VideoCapture(0)

        if not cap.isOpened():
            print("Error: Could not open camera.")
            return

        # Capture a frame
        ret, frame = cap.read()

        if not ret:
            print("Error: Could not capture image.")
            cap.release()
            return

        # Create a directory to store images if it doesn't exist
        if not os.path.exists("captured_images"):
            os.makedirs("captured_images")

        # Generate a unique filename using timestamp
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"captured_images/image_{timestamp}.png"

        # Save the image
        cv2.imwrite(filename, frame)
        print(f"Image saved as {filename}")

        # Release the camera
        cap.release()

        # Display the captured image
        display_image(filename)
Enter fullscreen mode Exit fullscreen mode

Let's break down the capture_image() function in a way that's easy for beginners to understand. We'll go through each part step-by-step, explaining what's happening and why.

`def capture_image()`
Enter fullscreen mode Exit fullscreen mode

This line creates a new function called capture_image(). Think of a function as a set of instructions we can use whenever we want to take a picture.

`cap = cv2.VideoCapture(0)`
Enter fullscreen mode Exit fullscreen mode

Here, we're setting up our camera. Imagine you're turning on a digital camera:

  • cv2 is a tool (library) that helps us work with cameras and images in Python.
  • VideoCapture(0) is like pressing the camera's power button. The 0 means "use the first camera you find" (usually the built-in webcam on a laptop).
  • We're calling this camera setup cap (short for capture), so we can refer to it later.
    if not cap.isOpened():
        print("Error: Could not open camera.")
        return
Enter fullscreen mode Exit fullscreen mode

This part checks if the camera turned on properly:

  • if not cap.isOpened(): asks, "Did the camera fail to turn on?"
  • If it did fail, we print an error message.
  • return means "stop here and exit the function" if there's a problem.

    ret, frame = cap.read()

Now we're taking the actual picture:

cap.read() is like pressing the shutter button on a camera.

It gives us two things:

  • ret: A yes/no answer to "Did the picture take successfully?"
  • frame: The actual picture, if it was taken.
    if not ret:
        print("Error: Could not capture image.")
        cap.release()
        return
Enter fullscreen mode Exit fullscreen mode

This checks if the picture was taken successfully:

  • If ret is "no" (which means the picture failed), we:
  • Print an error message.

  • cap.release() turns off the camera.

  • return exits the function.


    if not os.path.exists("captured_images"):
        os.makedirs("captured_images")
Enter fullscreen mode Exit fullscreen mode

This part creates a special folder to store our pictures:

if not os.path.exists("captured_images"):` checks if a folder named "captured_images" already exists.
Enter fullscreen mode Exit fullscreen mode
  • If it doesn't exist, os.makedirs("captured_images") creates this folder.
  • It's like creating a new album to store your photos.
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"captured_images/image_{timestamp}.png"
Enter fullscreen mode Exit fullscreen mode

Here, we're creating a unique name for our picture:

datetime.now() gets the current date and time.
.strftime("%Y%m%d_%H%M%S") formats this time into a string like "20240628_152059" (Year-Month-Day_HourMinuteSecond).

  • We use this to create a filename like "captured_images/image_20240628_152059.png".
  • This ensures each picture has a unique name based on when it was taken.
    cv2.imwrite(filename, frame)
    print(f"Image saved as {filename}")
Enter fullscreen mode Exit fullscreen mode

Now we're saving the picture.

v2.imwrite(filename, frame) saves our picture (frame) with the filename we created.
We then print a message saying where the image was saved.

`cap.release()`
Enter fullscreen mode Exit fullscreen mode

This line turns off the camera, like pressing the power button again when you're done.

`display_image(filename)`
Enter fullscreen mode Exit fullscreen mode

Finally, we call another function to show the picture we just took on the screen.
In summary, this function does the following.

  • Turns on the camera
  • Check if the camera is working
  • Takes a picture
  • Makes sure the picture was taken successfully
  • Creates a folder to store pictures if it doesn't exist
  • Gives the picture a unique name based on the current time
  • Saves the picture in the folder
  • Turns off the camera

Each step has checks to ensure things are working correctly, and if there's a problem at any point, the function will stop and let us know what went wrong.

Displaying the Captured Image

Add a function to display the captured image:

    def display_image(filename):
        # Open the image file
        img = Image.open(filename)

        # Resize the image to fit in the window
        img = img.resize((300, 200), Image.LANCZOS)

        # Convert the image for Tkinter
        photo = ImageTk.PhotoImage(img)

        # Update the image label
        image_label.config(image=photo)
        image_label.image = photo
Enter fullscreen mode Exit fullscreen mode

Let's start with a beginner-friendly explanation of file operations and then dive into the code.

File Operations for Beginners:

  1. Read:

    • This is like opening a book and looking at its contents.
    • In programming, reading a file means accessing its content without changing it.
    • Example: Opening an image to view it.
  2. Write:

    • This is like writing in a notebook.
    • In programming, writing means adding new content to a file or changing existing content.
    • Example: Saving a new image or modifying an existing one.
  3. Execute:

    • This is like following a set of instructions.
    • In programming, executing usually refers to running a program or script.
    • For images, we don't typically "execute" them, but we can process or display them.

Now, let's focus on the display_image(filename) function:

`def display_image(filename)`
Enter fullscreen mode Exit fullscreen mode

This line defines a function named display_image that takes a filename as input. This filename is the path to the image we want to display.

`img = Image.open(filename)`
Enter fullscreen mode Exit fullscreen mode

Here's where we use the "read" operation:

  • Image.open() is a function from the PIL (Python Imaging Library) that opens an image file.
  • It's like telling the computer, "Please look at this picture for me."
  • The opened image is stored in the variable img.
  • This operation doesn't change the original file; it allows us to work with its contents.

    img = img.resize((300, 200), Image.LANCZOS)

This line resizes the image:

  • img.resize() changes the size of the image.
  • (300, 200) sets the new width to 300 pixels and height to 200 pixels.
  • Image.LANCZOS is a high-quality resizing method that helps maintain image quality.

    photo = ImageTk.PhotoImage(img)

This line converts the image for use with Tkinter (the GUI library we're using):

  • ImageTk.PhotoImage() takes our resized image and converts it into a format that Tkinter can display.
  • This converted image is stored in the photo variable.
    image_label.config(image=photo)
    image_label.image = photo
Enter fullscreen mode Exit fullscreen mode

These lines update the GUI to show the image:

  • image_label is a Tkinter widget (like a container) that can display images.
  • config(image=photo) tells this label to display our processed image.
  • image_label.image = photo is a special line that prevents the image from being deleted by Python's garbage collector.

In summary, this function does the following:

  1. Opens an image file (read operation).
  2. Resize the image to fit nicely in our GUI window.
  3. Converts the image to a format our GUI system (Tkinter) can understand.
  4. Updates a label in our GUI to display this image.

This process doesn't involve writing to the file or executing it. We're simply reading the image, processing it in memory, and displaying it in our application.

Adding GUI Elements

Update your existing GUI to include a button for image capture and a label to display the image:

# Add this after your existing GUI elements
capture_button = tk.Button(window, text="Capture Image", command=capture_image)
capture_button.pack(anchor='center', pady=5)

image_label = tk.Label(window)
image_label.pack(anchor='center', pady=10)
Enter fullscreen mode Exit fullscreen mode
  • Adjusting the Window Size:

You may need to adjust the window size to accommodate the new elements:

window.geometry("350x400")  # Increase the height
Enter fullscreen mode Exit fullscreen mode
  • Complete Code:

Here's the complete code incorporating the new camera feature:

    import tkinter as tk
    from time import strftime
    import cv2
    from PIL import Image, ImageTk
    import os
    from datetime import datetime

    window = tk.Tk()
    window.title("Python GUI Clock with Camera")
    window.geometry("350x400")

    is_24_hour = True

    def update_time():
        global is_24_hour
        time_format = '%H:%M:%S' if is_24_hour else '%I:%M:%S %p'
        time_string = strftime(time_format)
        date_string = strftime('%B %d, %Y')
        time_label.config(text=time_string)
        date_label.config(text=date_string)
        time_label.after(1000, update_time)

    def change_color():
        colors = ['black', 'red', 'green', 'blue', 'yellow', 'purple', 'orange']
        current_bg = time_label.cget("background")
        next_color = colors[(colors.index(current_bg) + 1) % len(colors)]
        time_label.config(background=next_color)
        date_label.config(background=next_color)

    def toggle_format():
        global is_24_hour
        is_24_hour = not is_24_hour

    def capture_image():
        cap = cv2.VideoCapture(0)

        if not cap.isOpened():
            print("Error: Could not open camera.")
            return

        ret, frame = cap.read()

        if not ret:
            print("Error: Could not capture image.")
            cap.release()
            return

        if not os.path.exists("captured_images"):
            os.makedirs("captured_images")

        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"captured_images/image_{timestamp}.png"

        cv2.imwrite(filename, frame)
        print(f"Image saved as {filename}")

        cap.release()

        display_image(filename)

    def display_image(filename):
        img = Image.open(filename)
        img = img.resize((300, 200), Image.LANCZOS)
        photo = ImageTk.PhotoImage(img)
        image_label.config(image=photo)
        image_label.image = photo

    time_label = tk.Label(window, font=('calibri', 40, 'bold'), background='black', foreground='white')
    time_label.pack(anchor='center')

    date_label = tk.Label(window, font=('calibri', 24), background='black', foreground='white')
    date_label.pack(anchor='center')

    color_button = tk.Button(window, text="Change Color", command=change_color)
    color_button.pack(anchor='center', pady=5)

    format_button = tk.Button(window, text="Toggle 12/24 Hour", command=toggle_format)
    format_button.pack(anchor='center', pady=5)

    capture_button = tk.Button(window, text="Capture Image", command=capture_image)
    capture_button.pack(anchor='center', pady=5)

    image_label = tk.Label(window)
    image_label.pack(anchor='center', pady=10)

    update_time()
    window.mainloop()
Enter fullscreen mode Exit fullscreen mode

Conclusion

You've now enhanced your GUI clock with a user-controlled camera feature. This addition demonstrates how to integrate hardware interactions into a Python GUI application, handle file operations, and dynamically update the interface.

Always respect user privacy and obtain the necessary permissions when working with camera features in your applications.

Resource

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .