在 Tkinter 第一个 运行 问题上调整大小 Canvas
Resizing Canvas on Tkinter first run issue
我想制作一个程序,从小 window 开始,然后在给定图像路径时,它最大化屏幕并将图像放在中央。
如果您 运行 下面的代码,您将看到 window 最大化,图像被加载到内存中,代码 运行s 没有错误并且 self.open_image
调用 self.draw_image(self.pimg)
运行 没有错误,但是 canvas.
上不存在图像
如果我单击“修复”按钮并调用 self.fix
,它会调用 self.draw_image(self.pimg)
,其中 运行 没有错误并正确绘制图像。
如何使用相同的参数调用同一个函数两次并得到不同的结果。有什么不同。
我感觉这是在发生,因为主循环中发生了一些在 self.__init__
结束时没有发生的事情,所以当我第二次调用 self.draw_image
时self.cv.create_image
能够与可调整大小的内容进行交互 canvas。
在这个例子中,我很乐意假设程序总是从小的 window 开始,然后变成最大化的 window 直到它关闭,再也不会调整大小,但是在我的真实程序中我想让 window 合理处理调整大小的地方更加动态,这只是一个最小的可重现示例。出于这个原因,我想使用 ResizingCanvas class(或类似的),即使我觉得这可能是我遇到的问题的原因。
我已经尝试使用断点并单步执行代码来观察变量的创建,但是我看不出第一次 self.cv
和单击按钮后的 self.cv
之间的区别。
我在 this question 上读到了类似的问题,他建议将 "<Configure>"
绑定到 canvas 并将坐标从事件传递到 canvas。然而,这已经在 ResizingCanvas
中实施
from tkinter import *
from PIL import Image, ImageTk
class ResizingCanvas(Canvas):
#
def __init__(self,parent,**kwargs):
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
def on_resize(self,event):
""" determine the ratio of old width/height to new width/height"""
wscale = float(event.width)/self.width
hscale = float(event.height)/self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class main():
def __init__(self, name = None):
self.root = Tk()
self.name = name # Filename
myframe = Frame(self.root)
myframe.pack(fill=BOTH, expand=YES)
self.cv = ResizingCanvas(myframe, width=850, height=400, bg="dark grey", highlightthickness=0)
self.cv.pack(fill=BOTH, expand=YES)
self.b = Button(self.cv, text = 'Fix', command = self.fix).grid(row=1,column=1)
self.open_img()
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))
def open_img(self, event=''):
self.pimg = Image.open(self.name)
self.root.state("zoomed")
self.draw_image(self.pimg)
def fix(self, event=''):
self.draw_image(self.pimg)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
path = 'example.png'
app = main(path)
app.run()
视频中应该发生的事情:
我点击 运行 图像立即显示,无需点击修复按钮。
视频中发生了什么:
我单击 运行 并且直到我单击修复按钮后才显示图像,然后它就可以工作了。
改变
self.root.state("zoomed")
到 self.root.state("normal")
在你的代码中(我正在研究Python3)我只能得到:
[
上图,从How to get tkinter canvas to dynamically resize to window width?开始播放了一点
现在代码似乎适用于我:
from time import sleep
from tkinter import *
from PIL import Image, ImageTk
class ResizingCanvas(Canvas):
#
def __init__(self,parent, **kwargs):
# Canvas.__init__(self,parent,**kwargs)
print(kwargs)
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
# self.height = self.winfo_reqheight()
# self.width = self.winfo_reqwidth()
self.height = self.winfo_height()
self.width = self.winfo_width()
# self.height = height
# self.width = width
# self.__dict__.update(kwargs)
def on_resize(self,event):
""" determine the ratio of old width/height to new width/height"""
wscale = (event.width)//self.width
hscale = (event.height)//self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class main():
def __init__(self, name = None):
self.pippo = Tk()
self.name = name # Filename
self.myframe = Frame(self.pippo)
self.myframe.pack(side = BOTTOM, expand=YES)
# myframe.pack(fill=BOTH, expand='TRUE')
self.cv = ResizingCanvas(self.myframe, width=850, height=400, bg="dark grey", highlightthickness=0)
self.cv.pack(fill=BOTH, expand=YES)
# sleep(2)
self.b = Button(self.myframe, text = 'Fix', command = self.fix)#.grid(row=1,column=1)
self.b.pack(side=TOP)
self.open_img()
# self.pippo.mainloop() ## use it if you eliminate def run
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
# self.cv.create_image(self.pippo.winfo_screenwidth()/2,
# self.pippo.winfo_screenheight()/2, image=self.img, tags=('all'))
self.cv.create_image(self.pippo.winfo_width()/2,
self.pippo.winfo_reqheight()/2, image=self.img, tags=('all'))
def open_img(self, event=''):
self.pimg = Image.open(self.name)
self.pippo.state("normal")
self.draw_image(self.pimg)
def fix(self, event=''):
self.draw_image(self.pimg)
def run(self):
self.pippo.mainloop()
if __name__ == "__main__":
path = 'example.png'
app = main(path)
app.run()
虽然不知道你的问题,但想确保你的起始示例工作正常。如果它可能与 python/pillow/tkinter 版本或其他
相关,请告诉我
这是我的 window 在按下修复按钮后广告之前的图像结果:
最后发现只要你使用
你的代码就可以工作
self.root.attributes('-zoomed', True) instead of `self.root.state("zoomed")`
问题就在这里。 self.root.winfo_screenwidth()
将其更改为 self.cv.width
。不知道为什么。
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))
将最后一行改为
self.cv.create_image(self.cv.width/2,
self.cv.height/2, image=self.img, tags=('all'))
解决了问题。
Tk.winfo_screenwidth()
根据https://tkdocs.com/shipman/universal.htmlreturns屏幕的宽度,与window的大小无关,所以即使你有一个小window 在 1920x1080 显示器上,此函数将 return 1920.
self.cv.width
returns canvas 对象的宽度。
我想制作一个程序,从小 window 开始,然后在给定图像路径时,它最大化屏幕并将图像放在中央。
如果您 运行 下面的代码,您将看到 window 最大化,图像被加载到内存中,代码 运行s 没有错误并且 self.open_image
调用 self.draw_image(self.pimg)
运行 没有错误,但是 canvas.
如果我单击“修复”按钮并调用 self.fix
,它会调用 self.draw_image(self.pimg)
,其中 运行 没有错误并正确绘制图像。
如何使用相同的参数调用同一个函数两次并得到不同的结果。有什么不同。
我感觉这是在发生,因为主循环中发生了一些在 self.__init__
结束时没有发生的事情,所以当我第二次调用 self.draw_image
时self.cv.create_image
能够与可调整大小的内容进行交互 canvas。
在这个例子中,我很乐意假设程序总是从小的 window 开始,然后变成最大化的 window 直到它关闭,再也不会调整大小,但是在我的真实程序中我想让 window 合理处理调整大小的地方更加动态,这只是一个最小的可重现示例。出于这个原因,我想使用 ResizingCanvas class(或类似的),即使我觉得这可能是我遇到的问题的原因。
我已经尝试使用断点并单步执行代码来观察变量的创建,但是我看不出第一次 self.cv
和单击按钮后的 self.cv
之间的区别。
我在 this question 上读到了类似的问题,他建议将 "<Configure>"
绑定到 canvas 并将坐标从事件传递到 canvas。然而,这已经在 ResizingCanvas
from tkinter import *
from PIL import Image, ImageTk
class ResizingCanvas(Canvas):
#
def __init__(self,parent,**kwargs):
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
def on_resize(self,event):
""" determine the ratio of old width/height to new width/height"""
wscale = float(event.width)/self.width
hscale = float(event.height)/self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class main():
def __init__(self, name = None):
self.root = Tk()
self.name = name # Filename
myframe = Frame(self.root)
myframe.pack(fill=BOTH, expand=YES)
self.cv = ResizingCanvas(myframe, width=850, height=400, bg="dark grey", highlightthickness=0)
self.cv.pack(fill=BOTH, expand=YES)
self.b = Button(self.cv, text = 'Fix', command = self.fix).grid(row=1,column=1)
self.open_img()
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))
def open_img(self, event=''):
self.pimg = Image.open(self.name)
self.root.state("zoomed")
self.draw_image(self.pimg)
def fix(self, event=''):
self.draw_image(self.pimg)
def run(self):
self.root.mainloop()
if __name__ == "__main__":
path = 'example.png'
app = main(path)
app.run()
视频中应该发生的事情: 我点击 运行 图像立即显示,无需点击修复按钮。
视频中发生了什么:
我单击 运行 并且直到我单击修复按钮后才显示图像,然后它就可以工作了。
改变
self.root.state("zoomed")
到 self.root.state("normal")
在你的代码中(我正在研究Python3)我只能得到:
[
上图,从How to get tkinter canvas to dynamically resize to window width?开始播放了一点
现在代码似乎适用于我:
from time import sleep
from tkinter import *
from PIL import Image, ImageTk
class ResizingCanvas(Canvas):
#
def __init__(self,parent, **kwargs):
# Canvas.__init__(self,parent,**kwargs)
print(kwargs)
Canvas.__init__(self,parent,**kwargs)
self.bind("<Configure>", self.on_resize)
# self.height = self.winfo_reqheight()
# self.width = self.winfo_reqwidth()
self.height = self.winfo_height()
self.width = self.winfo_width()
# self.height = height
# self.width = width
# self.__dict__.update(kwargs)
def on_resize(self,event):
""" determine the ratio of old width/height to new width/height"""
wscale = (event.width)//self.width
hscale = (event.height)//self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all",0,0,wscale,hscale)
class main():
def __init__(self, name = None):
self.pippo = Tk()
self.name = name # Filename
self.myframe = Frame(self.pippo)
self.myframe.pack(side = BOTTOM, expand=YES)
# myframe.pack(fill=BOTH, expand='TRUE')
self.cv = ResizingCanvas(self.myframe, width=850, height=400, bg="dark grey", highlightthickness=0)
self.cv.pack(fill=BOTH, expand=YES)
# sleep(2)
self.b = Button(self.myframe, text = 'Fix', command = self.fix)#.grid(row=1,column=1)
self.b.pack(side=TOP)
self.open_img()
# self.pippo.mainloop() ## use it if you eliminate def run
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
# self.cv.create_image(self.pippo.winfo_screenwidth()/2,
# self.pippo.winfo_screenheight()/2, image=self.img, tags=('all'))
self.cv.create_image(self.pippo.winfo_width()/2,
self.pippo.winfo_reqheight()/2, image=self.img, tags=('all'))
def open_img(self, event=''):
self.pimg = Image.open(self.name)
self.pippo.state("normal")
self.draw_image(self.pimg)
def fix(self, event=''):
self.draw_image(self.pimg)
def run(self):
self.pippo.mainloop()
if __name__ == "__main__":
path = 'example.png'
app = main(path)
app.run()
虽然不知道你的问题,但想确保你的起始示例工作正常。如果它可能与 python/pillow/tkinter 版本或其他
相关,请告诉我这是我的 window 在按下修复按钮后广告之前的图像结果:
最后发现只要你使用
你的代码就可以工作self.root.attributes('-zoomed', True) instead of `self.root.state("zoomed")`
问题就在这里。 self.root.winfo_screenwidth()
将其更改为 self.cv.width
。不知道为什么。
def draw_image(self, img, x = None, y = None):
""" Handles the drawing of the main image"""
self.img = ImageTk.PhotoImage(img)
self.cv.create_image(self.root.winfo_screenwidth()/2,
self.root.winfo_screenheight()/2, image=self.img, tags=('all'))
将最后一行改为
self.cv.create_image(self.cv.width/2,
self.cv.height/2, image=self.img, tags=('all'))
解决了问题。
Tk.winfo_screenwidth()
根据https://tkdocs.com/shipman/universal.htmlreturns屏幕的宽度,与window的大小无关,所以即使你有一个小window 在 1920x1080 显示器上,此函数将 return 1920.
self.cv.width
returns canvas 对象的宽度。