Page 1 of 1

My First Pi Project!

Posted: Wed Feb 26, 2014 12:10 pm
by Maddog64
Hi

New to the Pi and Python (in fact a long time since I did any programming!) so set myself a small project to write a GUI to control the Pi Camera i.e. an interface to make various settings and then press a button to take the photo(s). It's only for still shots at the moment (i.e. no video functions). A lot was done by trial and error, loads of googling and reading posts on this forum. I've copied the py code below and would welcome constructive feedback on how I could have done this better or more efficiently. Happy for suggestions on improvements or additions to the program.

You need to install the picamera library - details at http://picamera.readthedocs.org/en/rele ... tall3.html.

Now to think of the next project!

Rob

Code: Select all

from tkinter import *
import time
import tkinter.messagebox as mb
from picamera import *


class PiCameraControl:

    def __init__ (self, master):
        frame = Frame (master)
        frame.pack()
       
        # Radio Buttons for still or continuous and number of continuous shots entry if allowed
        mode_Label = Label (frame, text = 'Select mode', width = 13, anchor = W)
        mode_Label.grid(row = 0, column = 0)
        mode_frame = Frame (frame)
        self.mode_select = StringVar()
        self.no_of_shots = IntVar()
        self.interval_var = IntVar()
        self.b1 = Radiobutton (mode_frame, text = 'Single', variable = self.mode_select, value = 'S', command = self.single_sel)
        self.b1.pack(side=LEFT)
        self.b2 = Radiobutton (mode_frame, text = 'Continuous     ', variable = self.mode_select, value = 'C', command = self.change_enter_state)
        self.b2.pack(side=LEFT)
        mode_frame.grid(row=0, column=1, sticky = W)
        
        self.shots_Label = Label(frame, text='No. of shots', width = 18, anchor = W)
        self.shots_Label.grid(row = 0, column = 2)
        self.shots_Label.config(foreground = 'grey')
        self.Enter_Shots = Entry(frame, textvariable=self.no_of_shots, state=DISABLED, width = 5)
        self.Enter_Shots.grid(row=0, column=3, sticky = W)
        self.interval_Label = Label(frame, text = 'Interval (secs)', width = 14, anchor = W)
        self.interval_Label.grid(row = 0, column = 4, padx = 10)
        self.interval_Scale = Scale(frame, from_=1, to=60, orient = HORIZONTAL, variable = self.interval_var, length = 200)
        self.interval_Scale.grid(row = 0, column = 5)
        
        #User sets filename - if continuous mode this will be used with {counter}
        self.FileName = StringVar()
        name_Label = Label(frame, text = 'Choose Filename', width = 14, anchor = W).grid (row = 1, column = 0)
        self.Enter_Filename = Entry(frame, textvariable=self.FileName, width = 15)
        self.Enter_Filename.grid(row = 1 , column = 1, sticky = W)

        #File format selection
        self.FileFormat = StringVar ()
        format_Label = Label(frame, text = 'File Format', width = 18, anchor = W).grid(row = 1, column = 2)
        self.fileext = Spinbox(frame, values = ('jpeg', 'png', 'gif', 'bmp', 'yuv', 'rgb', 'rgba', 'raw'), wrap=True, textvariable = self.FileFormat, \
                                      width = 10, command = self.file_ext).grid(row = 1, column = 3, sticky = W)


        # For jpeg format, option to select encoder quality
        self.jpeg_quality_var = IntVar()
        self.quality_Label = Label(frame, text = 'JPEG Quality', width = 14, anchor = W)
        self.quality_Label.grid(row = 1, column = 4)
        self.jpeg_quality_Scale = Scale(frame, from_=1, to=100, orient=HORIZONTAL, variable = self.jpeg_quality_var, length = 200)
        self.jpeg_quality_Scale.grid(row = 1, column = 5)
        

        # ISO selection
        self.ISO_Mode = IntVar()
        iso_Label = Label (frame, text  = 'ISO (0=Auto)', width = 14, anchor = W).grid(row = 2, column = 0)
        self.iso_select = Spinbox(frame, values = (0, 100, 200, 320, 400, 500, 640, 800), wrap=True, textvariable = self.ISO_Mode, width = 5, command = self.ISO_select)
        self.iso_select.grid(row = 2, column = 1, sticky = W)

        #Exposure Mode selection
        self.Exposure_Mode = StringVar()
        self.exposure_Label = Label (frame, text = 'Exposure Mode', width = 18, anchor = W)
        self.exposure_Label.grid(row = 2, column = 2)
        self.exposure_mode_Spinbox = Spinbox(frame, values = ('auto', 'off', 'night', 'nightpreview', 'backlight', 'spotlight', 'sports', 'snow', 'beach', 'verylong', \
                                                      'fixedfps', 'antishake', 'fireworks'), wrap = True, textvariable = self.Exposure_Mode, width = 10)
        self.exposure_mode_Spinbox.grid(row = 2, column = 3, sticky = W)

        #Set White Balance Mode
        self.AWB_Mode = StringVar()
        self.awb_Label = Label(frame, text = 'AWB Mode', width = 14, anchor = W)
        self.awb_Label.grid(row = 3, column = 0)
        self.awb_Spinbox = Spinbox(frame, values = ('auto', 'off', 'sunlight', 'cloudy', 'shade', 'tungsten', 'fluorescent', 'incandescent', 'flash', 'horizon'), wrap = True, \
                                   textvariable = self.AWB_Mode, width = 14)
        self.awb_Spinbox.grid(row = 3, column = 1, sticky = W, pady = 10)

        #Set metering mode

        self.meter_Mode = StringVar()
        self.meter_mode_Label = Label(frame, text = 'Metering Mode', width = 18, anchor = W)
        self.meter_mode_Label.grid(row = 3, column = 2)
        self.meter_mode_Spinbox = Spinbox(frame, values = ('average', 'spot', 'backlit', 'matrix'), wrap = True, textvariable = self.meter_Mode, width = 10)
        self.meter_mode_Spinbox.grid(row = 3, column = 3, sticky = W)


        #Set Image Effects

        self.image_Effect = StringVar()
        self.image_effect_Label = Label(frame, text = 'Image Effect', width =14, anchor = W)
        self.image_effect_Label.grid(row = 4, column = 0, pady = 10)
        self.image_effect_Spinbox = Spinbox(frame, values = ('none', 'negative', 'solarize', 'posterise', 'whiteboard', ' blackboard', 'sketch', 'denoise', 'emboss', 'oilpaint', 'hatch', \
                                                             'gpen', 'pastel', 'watercolor', 'film', 'blur', 'saturation', 'colorswap', 'washedout', 'colorpoint', 'colorbalance', 'cartoon'), \
                                            wrap = True, textvariable = self.image_Effect, width = 14)
        self.image_effect_Spinbox.grid(row = 4, column = 1, sticky = W)


        #Set Brightness - range 0-100, default 50

        self.Brightness_Level = IntVar()
        self.brigthness_Label = Label(frame, text = 'Brightness', width = 14, anchor = W)
        self.brigthness_Label.grid(row = 3, column = 4)
        self.brightness_Scale = Scale(frame, from_ =0, to = 100, orient = HORIZONTAL, variable = self.Brightness_Level, length = 200)
        self.brightness_Scale.grid(row = 3, column = 5, sticky = W)

        #Set Contrast - range -100 to 100, default 0

        self.Contrast_Level = IntVar()
        self.contrast_Label = Label(frame, text = 'Contrast', width = 14, anchor = W)
        self.contrast_Label.grid(row = 5, column = 0)
        self.contrast_Scale = Scale(frame, from_ = -100, to =100, orient = HORIZONTAL, variable = self.Contrast_Level, length = 430)
        self.contrast_Scale.grid(row = 5, column = 1, columnspan = 3, sticky = W)


        #Set Exposure Compensation - range -25 to 25 default 0
        
        self.Exposure_Comp = IntVar()
        self.exposure_comp_Label = Label(frame, text = 'Exposure Comp', width = 14, anchor = W)
        self.exposure_comp_Label.grid(row = 2, column = 4)
        self.exposure_comp_Scale = Scale(frame, from_=-25, to= 25, orient = HORIZONTAL, variable = self.Exposure_Comp, length = 200)
        self.exposure_comp_Scale.grid(row = 2, column = 5, sticky = W)

        #Set Saturation - range -100 to 100, default 0
        
        self.Saturation_Level = IntVar()
        self.saturation_Label = Label(frame, text = 'Saturation', width = 14, anchor = W)
        self.saturation_Label.grid(row = 6, column = 0)
        self.saturation_Scale = Scale(frame, from_ = -100, to =100, orient = HORIZONTAL, variable = self.Saturation_Level, length = 430)
        self.saturation_Scale.grid(row = 6, column = 1, columnspan = 3, sticky = W)

        #Set Sharpness - range -100 to 100, default 0

        self.Sharpness_Level = IntVar()
        self.sharpness_Label = Label(frame, text = 'Sharpness', width = 14, anchor = W)
        self.sharpness_Label.grid(row = 7, column = 0)
        self.sharpness_Scale = Scale(frame, from_ = -100, to =100, orient = HORIZONTAL, variable = self.Sharpness_Level, length = 430)
        self.sharpness_Scale.grid(row = 7, column = 1, columnspan = 3, sticky = W)

        #Set Shutter Speed - in milliseconds, default 0, Auto based on exposure

        self.Shutter_Speed = StringVar()
        self.shutter_speed_Label = Label(frame, text = 'Shutter Speed (ms)', width = 18, anchor = W)
        self.shutter_speed_Label.grid(row = 4, column = 2)
        self.shutter_speed_Entry = Entry(frame, textvariable = self.Shutter_Speed, width = 11)
        self.shutter_speed_Entry.grid(row = 4, column = 3, sticky = W)
                                                      
        #Take picture button
        Button (frame, text = 'Take Photo(s)', command = self.take, width = 22).grid(row = 9, column = 5, sticky = E)

        #Reset defaults button
        Button(frame, text = 'Reset Defaults', command = self.set_defaults, width = 22).grid(row = 8, column = 5, sticky = E, pady = 10)

        #Set default values
        self.set_defaults()

        
  
    def single_sel(self):
        self.shots_Label.config(foreground = 'grey')
        self.Enter_Shots.config(state=DISABLED)
        self.no_of_shots.set(1)
        self.interval_var.set(1)
        self.interval_Label.config(foreground = 'grey')
        self.interval_Scale.config(state=DISABLED)

    def change_enter_state(self):
        self.shots_Label.config(foreground = 'black')
        self.Enter_Shots.config(state=NORMAL)
        self.interval_Label.config(foreground = 'black')
        self.interval_Scale.config(state=NORMAL)

    def file_ext(self):
        if self.FileFormat.get()!='jpeg':
            self.quality_Label.config(foreground = 'grey')
            self.jpeg_quality_Scale.config(state=DISABLED)
        else:
            self.quality_Label.config(foreground = 'black')
            self.jpeg_quality_Scale.config(state=NORMAL)
        
    def ISO_select(self):
        if self.ISO_Mode.get() != 0:
            self.exposure_Label.config(foreground = 'grey')
            self.Exposure_Mode.set('auto')
            self.exposure_mode_Spinbox.config(state = DISABLED)
            mb.showinfo('Information', 'Exposure options disabled when ISO is not set to 0 Auto')
        else:
            self.exposure_Label.config(foreground = 'black')
            self.exposure_mode_Spinbox.config(state = NORMAL)


    def take(self):

        self.camera = PiCamera()
        self.camera.ISO = self.ISO_Mode.get()
        self.camera.awb_mode = self.AWB_Mode.get()
        self.camera.exposure_mode = self.Exposure_Mode.get()
        self.camera.exposure_compensation = self.Exposure_Comp.get()
        self.camera.meter_mode = self.meter_Mode.get()
        self.camera.image_effect = self.image_Effect.get()
        self.camera.brightness = self.Brightness_Level.get()
        self.camera.contrast = self.Contrast_Level.get()
        self.camera.saturation = self.Saturation_Level.get()
        self.camera.sharpness = self.Sharpness_Level.get()

        if self.Shutter_Speed.get() == 'auto':
            self.camera.shutter_speed = 0

        else:

            try:
                self.camera.shutter_speed = int(self.Shutter_Speed.get())

            except ValueError:
                mb.showwarning('Warning', "You've entered an invalid quantity, please retry")
                self.Shutter_Speed.set('auto')

        if self.FileName.get()=="":
            self.FileName.set('Image')

        try:
            
            if self.no_of_shots.get()==0:
                self.no_of_shots.set(1)
                
            if self.no_of_shots.get() == 1:

                if self.FileFormat.get()=='jpeg':
                    
                    self.camera.capture(self.FileName.get(), 'jpeg', quality = self.jpeg_quality_var.get())
                else:
                    self.camera.capture(self.FileName.get(), self.FileFormat.get())
                
            else:

                if self.FileFormat.get()=='jpeg':

                    for i, filename in enumerate(self.camera.capture_continuous(self.FileName.get() + '{counter}.jpg', quality = self.jpeg_quality_var.get())):
                        time.sleep(self.interval_var.get())
                        if i == self.no_of_shots.get() - 1:
                            break
                else:

                    for i, filename in enumerate(self.camera.capture_continuous(self.FileName.get() + '{counter}', self.FileFormat.get())):
                        time.sleep(self.interval_var.get())
                        if i == self.no_of_shots.get() - 1:
                            break
 
        except ValueError:
                mb.showwarning('Warning', "You've entered an invalid quantity, please retry")
                self.no_of_shots.set(1)

        finally:

            self.camera.close()

    def set_defaults(self):
        
        self.b1.select()
        self.single_sel()
        self.FileName.set('')
        self.FileFormat.set('jpeg')
        self.file_ext()
        self.jpeg_quality_Scale.set(85)
        self.ISO_Mode.set(0)
        self.ISO_select()
        self.Exposure_Mode.set('auto')
        self.Exposure_Comp.set(0)
        self.AWB_Mode.set('auto')
        self.meter_Mode.set('average')
        self.image_Effect.set('none')
        self.Brightness_Level.set(50)
        self.Contrast_Level.set(0)
        self.Saturation_Level.set(0)
        self.Sharpness_Level.set(0)
        self.Shutter_Speed.set('auto')
            

root = Tk()
root.resizable(0,0)
root.wm_title('Pi Camera Controller')
control = PiCameraControl (root)
root.mainloop()