将用户定义的图像添加到 tkinter 框架中的 Pythonic 方法
Pythonic way to add user defined image into tkinter frame
我正在尝试让框架显示通过文件对话框打开的图像。我当前的方法在步骤 1 中使用 "image1path" 初始化框架,然后我想将其更新为用户指定的某些 "image2path"。我试图通过 Data.currentJPEG 属性来做到这一点。
- 有没有更好的方法不使用 "dummy" 图像进行初始化?
- 如果这是正确的方法,为什么我在底部注释掉的行 return: " 'str' object has no attribute '_bind' " 当 Step1 不是字符串时。我认为 Step1 是允许 self.bind 在 startPage 上工作的原因。这不是更新框架中图像的正确方法吗?
- 附加问题:如果我在起始页行中将 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()
我正在尝试让框架显示通过文件对话框打开的图像。我当前的方法在步骤 1 中使用 "image1path" 初始化框架,然后我想将其更新为用户指定的某些 "image2path"。我试图通过 Data.currentJPEG 属性来做到这一点。
- 有没有更好的方法不使用 "dummy" 图像进行初始化?
- 如果这是正确的方法,为什么我在底部注释掉的行 return: " 'str' object has no attribute '_bind' " 当 Step1 不是字符串时。我认为 Step1 是允许 self.bind 在 startPage 上工作的原因。这不是更新框架中图像的正确方法吗?
- 附加问题:如果我在起始页行中将 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()