【问题标题】:Resizing Canvas on Tkinter first run issue在 Tkinter 首次运行问题上调整画布大小
【发布时间】:2021-11-02 20:02:11
【问题描述】:

我想制作一个以小窗口开始的程序,然后当给定图像路径时,它会最大化屏幕并将图像放置在中心。

如果您运行下面的代码,您将看到窗口最大化,图像被加载到内存中,代码运行没有错误,self.open_image 调用self.draw_image(self.pimg) 运行没有错误,但是图像不存在画布。

如果我点击“修复”按钮并调用self.fix,它会调用self.draw_image(self.pimg),它会正常运行并正确绘制图像。

如何使用相同的参数调用相同的函数两次并得到不同的结果。有什么不同。

我感觉正在发生这种情况,因为在 self.__init__ 末尾没有发生的主循环中发生了一些事情,所以当我第二次调用 self.draw_image 时,self.cv.create_image 能够与可调整大小的画布中的某些内容进行交互。

在这个例子中,我很高兴假设程序将始终作为一个小窗口开始并成为一个最大化的窗口,直到它被关闭,永远不会再次调整大小,但是在我的实际程序中,我想让它更加动态,其中窗口可以合理地调整大小,这只是一个最小的可重现示例。正是出于这个原因,我想使用 ResizingCanvas 类(或类似的类),即使我觉得这很可能是我遇到的问题的原因。

我尝试过使用断点并单步执行代码以观察变量的创建,但我无法看到第一次出现的 self.cv 和单击按钮后的 self.cv 之间的区别。

我在this question 上读到了类似的问题,他建议将"<Configure>" 绑定到画布并将坐标从事件传递到画布。不过这已经在ResizingCanvas中实现了

from tkinter import *
from PIL import Image, ImageTk

class ResizingCanvas(Canvas):
    # https://stackoverflow.com/a/22837522/992644
    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()

视频中应该发生的事情: 我点击运行,图像立即显示,无需点击修复按钮。

视频中发生了什么: 我单击运行,直到单击修复按钮后才显示图像,然后它才起作用。

【问题讨论】:

  • hi @hamsolo474 尝试了您的代码,但无法使其运行我正在添加与我一起使用的偶然版本,但不知道如何回答您的问题
  • @pippo1980 感谢您的回复,我已按原样附上了该错误的视频和我的代码。我在 Windows 10 上运行它,在你的链接中它说 Linux 上的 self.root.attributes('-zoomed', True) 相当于 Windows 上的 self.root.attributes("-fullscreen", True),我正在寻找最大化而不是全屏。我猜你的问题是由于 Linux 上的 Tkinter 和 Windows 上的 Tkinter 之间的细微差别。感谢您发布修复以使我的示例在 Linux 上运行,如果任何 Windows 用户看到这一点并且也无法运行我的代码,请说些什么。
  • 尝试将 #self.pippo.state("zoomed") 从 'def open_png:' 移动到 'def fix:' 你会得到 3 个不同的结果调用相同的函数 3 个不同的时间
  • 尝试在 'def on_resize:' 之后添加 'print('EVENT : ' ,event)' 看看你是否能分辨出来

标签: python python-3.x tkinter python-imaging-library tkinter-canvas


【解决方案1】:

变化

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):
    # https://stackoverflow.com/a/22837522/992644
    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 版本或其他东西有关

这是我按下修复按钮后广告前的窗口图像结果:

最后发现你的代码只要你用就可以工作

self.root.attributes('-zoomed', True) instead of `self.root.state("zoomed")`

【讨论】:

【解决方案2】:

问题就在这里。 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()根据@​​987654321@返回屏幕宽度,与窗口大小无关,所以即使你有一个1920x1080显示器的小窗口,这个函数也会返回1920。

self.cv.width 返回画布对象的宽度。

【讨论】:

  • 您还期望winfo_screenwidth()..返回什么?它返回您最初设置的 850 和 400
  • 它不应该返回 850 和 400,w.winfo_screenwidth() Returns the width of the screen in pixels.
  • 它应该返回 1920 和 1080(在我的显示器上)。在我的实际代码中,我有一个稍微复杂的设置,我在这个例子中调整了窗口大小,我的内容调整大小以适应新的窗口尺寸(在这个例子中没有看到),我想我在玩这个代码并交换了self.cv.widthself.root.winfo_screenwidth() 尝试解决这个问题,然后产生了新的问题,我忘记了这个变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 1970-01-01
  • 2014-05-15
  • 1970-01-01
  • 2021-04-28
相关资源
最近更新 更多