将用户定义的图像添加到 tkinter 框架中的 Pythonic 方法

Pythonic way to add user defined image into tkinter frame

我正在尝试让框架显示通过文件对话框打开的图像。我当前的方法在步骤 1 中使用 "image1path" 初始化框架,然后我想将其更新为用户指定的某些 "image2path"。我试图通过 Data.currentJPEG 属性来做到这一点。

  1. 有没有更好的方法不使用 "dummy" 图像进行初始化?
  2. 如果这是正确的方法,为什么我在底部注释掉的行 return: " 'str' object has no attribute '_bind' " 当 Step1 不是字符串时。我认为 Step1 是允许 self.bind 在 startPage 上工作的原因。这不是更新框架中图像的正确方法吗?
  3. 附加问题:如果我在起始页行中将 weight=100 更改为 weight=1,为什么打开 .jpg 按钮的位置会完全缩放?

这个代码结构可以找到here

可以找到以这种方式调整图像大小here

我尝试使用的方法遵循 this answer,其中当按下更新图像的按钮时会调用一个新函数。但是,我相信我 运行 遇到了问题,因为我的图像也使用了调整大小功能。

            import os
            import tkinter as tk
            from PIL import Image, ImageTk
            from tkinter import filedialog

            image1path='mainGraphic.jpg'

            cwd = os.getcwd()

            # where opened file data with be stored
            class Data():
                # this sets initial values for the class attributes 
                def __init__(self):
                    self.currentJPEG=image1path

            class program(tk.Tk):
                def __init__(self,*args,**kwargs):
                    tk.Tk.__init__(self,*args,**kwargs)

                    self.title('program name')

                    container = tk.Frame(self)
                    container.pack(side="top", fill="both", expand=True)
                    container.grid_rowconfigure(0, weight=1)
                    container.grid_columnconfigure(0, weight=1)

                    # frames are laid ontop of each other, startPage shown first
                    self.frames = {}
                    for Frame in (StartPage, Step1):
                        frame = Frame(container, self)
                        self.frames[Frame] = frame
                        frame.grid(row = 0, column = 0, sticky="nsew")
                        frame.columnconfigure(0, weight=1)
                        frame.rowconfigure(0, weight=1)

                    self.show_frame(StartPage)

                def show_frame(self,cont):
                    frame = self.frames[cont]
                    frame.tkraise()

            class StartPage(tk.Frame):
                def __init__(self, parent, controller):
                    tk.Frame.__init__(self,parent)

                    # scale rows and columns (make button scaling negligible) 
                    self.rowconfigure(0,weight=0)
                    self.rowconfigure(1,weight=100) #bonus question refers to these 2 lines
                    self.columnconfigure(0,weight=100) 

                    # button to open an image in Step 1,
                         # must pass program so app.showframe is available,
                         # must pass Step1 so the image attributes are available?
                    button = tk.Button(self, text='Open .jpg File in new frame',
                                        command=lambda: Step1.openJPEG(program))
                    button.grid(row=0, column=0, sticky='ew')

                    # add the main graphic
                    self.canvas = tk.Canvas(self,bg='black')
                    self.canvas.grid(row=1, column=0, sticky='nsew')
                    self.img_copy = Image.open(image1path)
                    self.image = None #this is overriden every time the image is redrawn so there is no need to make it yet
                    self.bind("<Configure>",self.resizeImage)

                def resizeImage(self,event):
                    origin = (0,0)
                    size = (event.width, event.height)
                    if self.bbox("bg") != origin + size:
                        self.canvas.delete("bg")
                        self.image = self.img_copy.resize(size)
                        self.background_image = ImageTk.PhotoImage(self.image)
                        self.canvas.create_image(*origin,anchor="nw",image=self.background_image,tags="bg")
                        self.canvas.tag_lower("bg","all")

            class Step1(tk.Frame):

                def __init__(self, parent, controller):
                    tk.Frame.__init__(self, parent)

                    # give even weight to each subframe
                    self.rowconfigure(0, weight=1)
                    self.columnconfigure(0, weight=100)
                    self.columnconfigure(1, weight=100)

                    # Frame1, this is on the left and empty
                    frame1 = tk.Frame(self, bg="grey")
                    frame1.grid(row=0, column=0, sticky='nsew')

                    # Frame2, this is where image preview is
                    frame2 = tk.Frame(self, bg="grey")
                    frame2.grid(row=0, column=1, sticky='nsew')    

                    # scale rows and columns with equal weight
                    frame2.rowconfigure(0,weight=1)
                    frame2.columnconfigure(0,weight=100)
                    frame2.columnconfigure(1,weight=100)

                    # initialize where image preview will be in frame2
                    self.canvas = tk.Canvas(frame2,bg='black')
                    self.canvas.grid(row=0, column=1, sticky='nsew')
                    self.img_copy = Image.open(Data.currentJPEG)
                    self.image = None 
                    self.bind("<Configure>",self.resizeImage)

                def resizeImage(self,event):
                    origin = (0,0)
                    size = (event.width, event.height) # these need to get height/width of their frame, not Step1
                    if self.bbox("bg") != origin + size:
                        self.canvas.delete("bg")
                        self.image = self.img_copy.resize(size)
                        self.background_image = ImageTk.PhotoImage(self.image)
                        self.canvas.create_image(*origin,anchor="nw",image=self.background_image,tags="bg")
                        self.canvas.tag_lower("bg","all")

                # update the image in frame2 using opened jpeg image
                def openJPEG(program):
                    filePath = filedialog.askopenfilename(initialdir=cwd,
                                                          filetypes=(("JPEG",".jpg"),("All Files","*.*")),
                                                          title="Open JPEG")
                    try:
                        with open(filePath) as image: #***need a better 'try' statement for only jpeg is opened'
                            app.show_frame(Step1)
                    except:
                        print('could not open image')
                        return

                    ## update image preview from mainGraphic to opened JPEG; these lines aren't working
                    Data.currentJPEG=filePath
                    #Step1.img_copy = Image.open(Data.currentJPEG)
                    #Step1.image = None
                    #Step1.bind("<Configure>",Step1.resizeImage)

            # initalize data class
            Data=Data()
            # run program
            app = program()

如果您只想让您的用户手动 select 一个文件,然后让您的 tkinter GUI 显示它,您只需要一个 Button 和一个 Canvas .

代码:

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.image as mpimg
from tkinter import filedialog
import tkinter as tk
import os

class program(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)

        # Create a button to load a jpg
        self.button = tk.Button(self, text="Load image", command=self.openJPEG)
        self.button.pack()

        # Create a canvas on which to show the jpg
        self.frame = tk.Frame(self)
        self.frame.pack(fill=tk.BOTH, expand=1)
        self.create_canvas()

    def openJPEG(self):
        # Display a dialog for the user to select a jpg file, 
        filePath = filedialog.askopenfilename(initialdir=os.getcwd(),
                                              filetypes=(("JPEG",".jpg"),
                                                         ("All Files","*.*")),
                                              title="Open JPEG")
        # Show the selected jpg on the canvas
        img = mpimg.imread(filePath)
        self.ax1.imshow(img)
        self.canvas1.draw()

    def create_canvas(self):
        """ Add a canvas to plot images """        
        self.fig1 = Figure(frameon=False, figsize=(6, 4.5))
        self.canvas1 = FigureCanvasTkAgg(self.fig1, master=self.frame)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=1)
        self.ax1 = self.fig1.add_axes([0, 0, 1, 1])
        self.ax1.axis('off')

# Run program
app = program()
app.mainloop()

如果您想保留 多个 类 结构,则必须更好地利用 GUI 的根部分(称为 controller在你的子框架中)在不同的框架之间进行通信。请参阅下面的代码以帮助您入门:

import os
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import filedialog

# where opened file data with be stored
class Data():
    # this sets initial values for the class attributes 
    def __init__(self):
        self.currentJPEG=""

class program(tk.Tk):
    def __init__(self, Data):
        tk.Tk.__init__(self)
        self.Data = Data
        container = tk.Frame(self)
        container.pack(fill="both", expand=True)

        # frames are laid ontop of each other, startPage shown first
        self.frames = {}
        for Frame in (StartPage, Step1):
            frame = Frame(container, self)
            self.frames[Frame] = frame
            frame.grid(row = 0, column = 0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self,cont):
        frame = self.frames[cont]
        frame.tkraise()

class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        # button to open an image in Step 1,
        button = tk.Button(self, text='Open .jpg File in new frame',
                           command= self.openJPEG)
        button.grid(row=0, column=0, sticky='ew')

        # add empty canvas
        self.canvas = tk.Canvas(self,bg='black')
        self.canvas.grid(row=1, column=0, sticky='nsew')

    def openJPEG(self):
        self.openJPEG
        filePath = filedialog.askopenfilename(initialdir=os.getcwd(),
                                              filetypes=(("JPEG",".jpg"),
                                                         ("All Files","*.*")),
                                              title="Open JPEG")
        # Update the image in Step1 frame
        self.controller.Data.currentJPEG = filePath
        self.controller.frames[Step1].show_current_image()
        # Show Step1 frame
        self.controller.show_frame(Step1)

class Step1(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        # Frame2, this is where image preview is
        frame2 = tk.Frame(self, bg="grey")
        frame2.grid(row=0, column=1, sticky='nsew')    

        # initialize where image preview will be in frame2
        self.canvas = tk.Canvas(frame2,bg='black')
        self.canvas.grid(row=0, column=1, sticky='nsew')

    def show_current_image(self):
        self.image = Image.open(self.controller.Data.currentJPEG)
        self.background_image = ImageTk.PhotoImage(self.image)
        self.canvas.create_image(0, 0, anchor="nw", image=self.background_image, 
                                 tags="bg")

# initalize data class
Data = Data()
# run program
app = program(Data)
app.mainloop()