Sam
Sam

Reputation: 1062

I am having trouble with Tkinter GUI control

I have a programme with various controls, a live webcam feed and a matplotlib figure based on the webcam data. I would like all the controls to be located in a left column, and the webcam and matplotlib figure to be located in a right column.

Attempts to use .grid() methods, canvases and frames etc don't work - the python script just fails to execute and hangs indefinitely.

How can this be achieved?

Minimum working example:

import Tkinter as tk

import cv2
from PIL import Image, ImageTk
import numpy as np

import matplotlib
matplotlib.use('TkAgg')

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.pyplot as plt
from matplotlib.figure import Figure

root = tk.Tk()
root.bind('<Escape>', lambda e: root.quit())
lmain = tk.Label(root)
lmain.pack()

class Controller(tk.Frame):
    def __init__(self, parent=root, camera_index=0):
        self.camera_index = 0

        frame = tk.Frame.__init__(self, parent,relief=tk.GROOVE,width=100,height=100,bd=1)
        self.pack()
        self.parent = parent
        self.var = tk.IntVar()

        self.parent.title('Laser Beam Profiler')

        labelframe = tk.LabelFrame(parent, text="This is a LabelFrame")
        labelframe.pack(fill="both", expand="yes") #.grid(row=0, column=0) 

        self.plot = tk.Button(labelframe, text = "Plot", command = self.refresh_plot)
        self.plot.pack()
        self.exit = tk.Button(labelframe, text = "Exit", command = self.close_window, compound = tk.BOTTOM)
        self.exit.pack()

        self.init_camera()
        self.show_frame() #initialise camera
        self.make_fig()

    def make_fig(self):
        self.fig = Figure(figsize=(4,4), dpi=100) 
        self.ax = self.fig.add_subplot(111) 

        canvas = FigureCanvasTkAgg(self.fig, self) 
        canvas.show() 
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True) 

        toolbar = NavigationToolbar2TkAgg(canvas, self) 
        toolbar.update() 
        canvas._tkcanvas.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

    def refresh_plot(self):
        self.ax.plot(self.img[0])
        self.fig.canvas.draw() 
        self.ax.clear()
        print 'updated plot'

    def init_camera(self):
        width, height = 400, 300
        self.cap = cv2.VideoCapture(self.camera_index)
        if not self.cap:
            raise Exception("Camera not accessible")
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)            

    def show_frame(self):
        _, frame = self.cap.read()
        cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)

        cv2.putText(cv2image,"Laser Beam profiler", (50,50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255))
        dim = np.shape(cv2image)

        img = Image.fromarray(cv2image)  
        imgtk = ImageTk.PhotoImage(image=img)

        lmain.imgtk = imgtk
        lmain.configure(image=imgtk)
        lmain.after(10, self.show_frame)

        self.img = frame

    def close_window(self): 
        self.parent.quit()
        self.parent.destroy()

Controller().mainloop()

For the minimum working example I am trying to get the 'plot' and 'exit' buttons on the left hand side, and the webcam view with the matplotlib figure on the right. Currently it just places the widgets in a long column that spills off the screen.

Upvotes: 1

Views: 176

Answers (1)

J.J. Hakala
J.J. Hakala

Reputation: 6214

The line

labelframe = tk.LabelFrame(parent, text="This is a LabelFrame")

probably should have self instead of parent. After this

labelframe.pack(fill="both", expand="yes", tk.LEFT)

should work. Since the Controller and the labelframe had the same parent and pack method for the Controller was called first, it placed restrictions where the labelframe could appear.

In Controller.__init__() there is a call to self.pack() in a little bit unusual place, it is usually left to be called after the widget has been instantiated i.e.

c = Controller(root)
c.pack()
root.mainloop()

Upvotes: 1

Related Questions