为什么我的tkinter代码会发生这种情况?

2024-09-30 08:32:01 发布

您现在位置:Python中文网/ 问答频道 /正文

所以我改天制作了一个图像查看器,它的基本功能是跳转到下一张图像,当我们的一组图片结束时返回到第一张图像。 我制作了一个名为开始的按钮,它基本上启动整个图片查看器并显示图片编号1, 我还制作了一个关闭按钮,它将整个过程反转到起始点(您将再次看到开始按钮)

…但一件非常奇怪的事情发生了。 当我按下按钮时,顺序如下:StartNext,关闭, 开始, 下一步`-整个事情变得疯狂,我的按钮消失了

很抱歉,如果我以一种非常低效的方式编写代码,那是因为我两天前开始学习python! 我也在每个if/elif的末尾打印了这篇文章,以使调试更容易,但它没有帮助

from tkinter import *
from PIL import ImageTk,Image
from tkinter.font import *
root = Tk()
img = None
label1 = None
root.geometry('1920x1080')
cimage = "flower1.jpg"
# Functions
def rightpic() :
    global cimage
    if cimage == "flower1.jpg" :
        label1.grid_forget()
        label2.grid()
        cimage = "flower2.jpg"
        print("flower1skip")
    elif cimage == "flower2.jpg" :
        label2.grid_forget()
        label3.grid()
        cimage = "flower3.jpg"
        print("flower2skip")
    elif cimage == "flower3.jpg" :
        label3.grid_forget()
        label4.grid()
        cimage = "flower4.jpg"
        print("flower3skip")
    elif cimage == "flower4.jpg" :
        label4.grid_forget()
        label1.grid()
        cimage = "flower1.jpg"
        print("flower4skip")
def start() :
    global cimage
    label1.grid(row=0)
    buttonright.place(x=900, y=950)
    buttonclose.place(x=700, y=950)
def close() :
    global cimage
    if cimage == "flower1.jpg" :
        label1.grid_forget()
        buttonclose.place_forget()
        buttonright.place_forget()
        print("closed1")
    elif cimage == "flower2.jpg" :
        label2.grid_forget()
        buttonclose.place_forget()
        buttonright.place_forget()
        print("closed2")
    elif cimage == "flower3.jpg" :
        label3.grid_forget()
        buttonclose.place_forget()
        buttonright.place_forget()
        print("closed3")
    elif cimage == "flower4.jpg" :
        label4.grid_forget()
        buttonclose.place_forget()
        buttonright.place_forget()
        print("closed4")

buttons = Button(root,text = "Start!",command=start , bg = "#29a8ab",fg="#cccccc" , padx = 50 ,font=('Helvetica 19 bold'))
buttons.place(x=960,y=540)

buttonright= Button(root,text = "Next!",command=rightpic , bg = "#0e9aa7",fg="#f6cd61" , padx = 50 ,font=('Helvetica 19 bold'))
# buttonright.place(x=900, y=950)
buttonclose= Button(root,text = "Close!",command=close , bg = "#0e9aa7",fg="#f6cd61" , padx = 50 ,font=('Helvetica 19 bold'))





image1 = Image.open("flower1.jpg")
image1 = image1.resize((1920,950) , Image.ANTIALIAS)
image12 = ImageTk.PhotoImage(image1)
label1 = Label(root,image=image12)

image2 = Image.open("flower2.jpg")
image2 = image2.resize((1920,950) , Image.ANTIALIAS)
image13 = ImageTk.PhotoImage(image2)
label2 = Label(root,image=image13)

image3 = Image.open("flower3.jpg")
image3 = image3.resize((1920,950) , Image.ANTIALIAS)
image14 = ImageTk.PhotoImage(image3)
label3 = Label(root,image=image14)

image4 = Image.open("flower4.jpg")
image4 = image4.resize((1920,950) , Image.ANTIALIAS)
image15 = ImageTk.PhotoImage(image4)
label4 = Label(root,image=image15)

# label1.grid(row = 0)

mainloop()

Tags: imageplaceroot按钮gridjpgprintelif
1条回答
网友
1楼 · 发布于 2024-09-30 08:32:01

首先,我将回答以下问题:为什么按顺序单击按钮时按钮会消失?
这是因为当调用start()时,它会对第一个图像进行网格化,然后当调用rightpic()时,它会检查cimage的值是多少,并相应地进行操作,因为当您第一次启动程序时,该值被设置为"flower1.jpg"它会计算第一个if语句,并忘记第一个已网格化的图像,并对第二个图像进行网格化,然后调用close(),它再次检查图像并删除所有内容,但是cimage现在是"flower2.jpg",当您再次调用start()时,它会对第一个图像进行网格化,但是当调用rightpic()时,它会转到elif cimage == "fower2.jpg"但是由于第二个图像尚未网格化,因此没有什么可忘记的,因此,它只是将第三个图像置于第一个图像下方,由于其他按钮是place()d,因此在place()之后调用的网格方法现在将第三个图像置于这些按钮上方。
解决方案是简单地做到这一点:

def start() :
    global cimage
    cimage = "flower1.jpg"
    label1.grid(row=0)
    buttonright.place(x=900, y=950)
    buttonclose.place(x=700, y=950)

这将在每次调用start()时将变量重置为第一个映像

现在,我将向您展示如何改进代码(因为答案必须包含问题的实际答案,而不仅仅是建议),主要问题是创建不必要的标签和大量重复

重要
我想在其他东西之前提到这一点:我强烈建议不要使用通配符(*)导入东西时,您应该导入您需要的东西,例如from module import Class1, func_1, var_2等等,或者导入整个模块:import module,然后您还可以使用别名:import module as md或类似的东西,关键是,除非你真的知道自己在做什么,否则不要导入所有内容;名称冲突是一个问题。
此外,我还强烈建议您遵循PEP 8 - Style Guide for Python Code,例如,函数定义之间有两行,逗号(,)后有空格,但不要在逗号之前有空格,不要在应该是def func():的时候放不必要的空格,比如def func() :,一般来说格式是一致的,因为函数和变量名使用snake_case,对于类名,使用CapitalCase。您只需看一下我将在这里编写的代码,就可以更好地理解我的意思


第一个改进(也可以改进,但稍后会改进)是只有一个简单配置的标签(阅读代码中的注释):

# import only what you need to keep the namespace clean
from tkinter import Tk, Label, Button
from PIL import Image, ImageTk


# my preference is to define everything that doesn't have to be defined
# after instantiating `Tk` before `Tk` such as these variables and functions
# as they will be called after `Tk` anyways so doesn't really matter but at least helps keep
# the parts that are strictly `tkinter` more together
cimage = "flower1.jpg"


# Functions (changed to `snake_case`)
def right_pic():
    # here come some major changes to reduce the code
    # what is commented out can be deleted and is how
    # it were previously
    global cimage
    if cimage == "flower1.jpg":
        # label1.grid_forget()
        # label2.grid()
        image_label.config(image=image13)
        cimage = "flower2.jpg"
        # print("flower1skip")
    elif cimage == "flower2.jpg":
        # label2.grid_forget()
        # label3.grid()
        image_label.config(image=image14)
        cimage = "flower3.jpg"
        # print("flower2skip")
    elif cimage == "flower3.jpg":
        # label3.grid_forget()
        # label4.grid()
        image_label.config(image=image15)
        cimage = "flower4.jpg"
        # print("flower3skip")
    elif cimage == "flower4.jpg":
        # label4.grid_forget()
        # label1.grid()
        image_label.config(image=image12)
        cimage = "flower1.jpg"
        # print("flower4skip")


# put two spaces between funcs (if you have a comment like this that corresponds
# to the function then it also has have two spaces between other functions)
def start():
    global cimage
    cimage = "flower1.jpg"
    image_label.grid(row=0)
    button_right.place(x=900, y=950)
    button_close.place(x=700, y=950)


def close():
    # here comes the first redundant code (if you read line by line)
    # if you notice then every if/elif clause you always call
    # button_right.place_forget() and button_close.place_forget()
    # since they get always called might as well simply put them only at the end
    # (commented out what can be deleted) (what has double ## was even more before)
    # global cimage
    # if cimage == "flower1.jpg":
    #     label1.grid_forget()
    #     # button_close.place_forget()
    #     # button_right.place_forget()
    #     print("closed1")
    # elif cimage == "flower2.jpg":
    #     label2.grid_forget()
    #     # button_close.place_forget()
    #     # button_right.place_forget()
    #     print("closed2")
    # elif cimage == "flower3.jpg":
    #     label3.grid_forget()
    #     # button_close.place_forget()
    #     # button_right.place_forget()
    #     print("closed3")
    # elif cimage == "flower4.jpg":
    #     label4.grid_forget()
    #     # button_close.place_forget()
    #     # button_right.place_forget()
    #     print("closed4")
    # 
    # # simply have these here at the end, no need to repeat them in each clause
    # button_close.place_forget()
    # button_right.place_forget()
    
    # the latest version using only one label is as simple as
    image_label.grid_forget()
    button_close.place_forget()
    button_close.place_forget()


root = Tk()
root.geometry('1920x1080')

buttons = Button(root, text="Start!", command=start, bg="#29a8ab", fg="#cccccc", padx=50, font='Helvetica 19 bold')
buttons.place(x=960, y=540)

# changed names to match `snake_case`, the main changes are minor
# things such as removing spaces or adding those where needed (take a close look
# this is how it should be to be PEP 8 compliant (not necessary to follow but
# it is how python code was intended to be formatted))
button_right = Button(root, text="Next!", command=right_pic, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')
button_close = Button(root, text="Close!", command=close, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')

# here comes the second repetition (commented out can be deleted)
# if you wanted to make it even more compact you could have done
# sth like this:
# image1 = Image.open("flower1.jpg").resize((1920, 950), Image.ANTIALIAS)
# image12 = ImageTk.PhotoImage(image1)

image1 = Image.open("flower1.jpg")
image1 = image1.resize((1920, 950), Image.ANTIALIAS)
image12 = ImageTk.PhotoImage(image1)
# label1 = Label(root, image=image12)

image2 = Image.open("flower2.jpg")
image2 = image2.resize((1920, 950), Image.ANTIALIAS)
image13 = ImageTk.PhotoImage(image2)
# label2 = Label(root, image=image13)

image3 = Image.open("flower3.jpg")
image3 = image3.resize((1920, 950), Image.ANTIALIAS)
image14 = ImageTk.PhotoImage(image3)
# label3 = Label(root, image=image14)

image4 = Image.open("flower4.jpg")
image4 = image4.resize((1920, 950), Image.ANTIALIAS)
image15 = ImageTk.PhotoImage(image4)
# label4 = Label(root, image=image15)

image_label = Label(root, image=image12)

# best practice is to use `<Tk>.mainloop()` instead of plain `mainloop()`
# so in this case `Tk` is assigned to `root` so this:
root.mainloop()

同样的代码刚刚删除了注释:

from tkinter import Tk, Label, Button
from PIL import Image, ImageTk


cimage = "flower1.jpg"


def right_pic():
    global cimage
    if cimage == "flower1.jpg":
        image_label.config(image=image13)
        cimage = "flower2.jpg"
    elif cimage == "flower2.jpg":
        image_label.config(image=image14)
        cimage = "flower3.jpg"
    elif cimage == "flower3.jpg":
        image_label.config(image=image15)
        cimage = "flower4.jpg"
    elif cimage == "flower4.jpg":
        image_label.config(image=image12)
        cimage = "flower1.jpg"


def start():
    global cimage
    cimage = "flower1.jpg"
    image_label.grid(row=0)
    button_right.place(x=900, y=950)
    button_close.place(x=700, y=950)


def close():
    image_label.grid_forget()
    button_close.place_forget()
    button_close.place_forget()


root = Tk()
root.geometry('1920x1080')

buttons = Button(root, text="Start!", command=start, bg="#29a8ab", fg="#cccccc", padx=50, font='Helvetica 19 bold')
buttons.place(x=960, y=540)

button_right = Button(root, text="Next!", command=right_pic, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')
button_close = Button(root, text="Close!", command=close, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')


image1 = Image.open("flower1.jpg")
image1 = image1.resize((1920, 950), Image.ANTIALIAS)
image12 = ImageTk.PhotoImage(image1)

image2 = Image.open("flower2.jpg")
image2 = image2.resize((1920, 950), Image.ANTIALIAS)
image13 = ImageTk.PhotoImage(image2)

image3 = Image.open("flower3.jpg")
image3 = image3.resize((1920, 950), Image.ANTIALIAS)
image14 = ImageTk.PhotoImage(image3)

image4 = Image.open("flower4.jpg")
image4 = image4.resize((1920, 950), Image.ANTIALIAS)
image15 = ImageTk.PhotoImage(image4)

image_label = Label(root, image=image12)

root.mainloop()

(如您所见,代码已经短了很多)

现在,对于一个不使用if/elif的高级方法,基本上是非常可扩展的:

from tkinter import Tk, Label, Button
from PIL import Image, ImageTk


# define some lists here (since no direct relation to `tkinter`)
# the main loop is after `Tk`

# note that these are basically relative paths to images (also note that now
# to add new image you really only need to add it to this list)
img_names = ['flower1.jpg', 'flower2.jpg', 'flower3.jpg', 'flower4.jpg']
# another fancier option would be to use sth like this:
# import os
# img_names = [f'images/{name}' for name in os.listdir('images')]
# now this would create a list of relative paths to all files listed in
# directory named `"images"` that is relative to this script (obs
# you can change the dir name and stuff, this is just an example)
# so now if you only had images in that dir, you could simply only
# add images to that directory and they will be added to this list every time you
# run this code which is great

# this is to keep track of the current index, that allows for easier looping
# note tho that there are many options of how to loop over
# those images, this should be the most understandable tho
index = 0


def right_pic():
    # now that there is an image list you can simply iterate over that
    # keeping track of the index
    global index
    index += 1
    # add this if statement to not cause errors
    # and to allow for looping over
    if index == len(img_lst):  # use length because the last index is the length - 1
        index = 0
    image_label.config(image=img_lst[index])


def start():
    # define index as global so that it can be changed
    global index
    # reset it to 0 every time you call `start()`
    index = 0
    # config the label to now contain the first image
    image_label.config(image=img_lst[index])
    image_label.grid(row=0)
    button_right.place(x=900, y=950)
    button_close.place(x=700, y=950)


def close():
    image_label.grid_forget()
    button_close.place_forget()
    button_close.place_forget()


root = Tk()
root.geometry('1920x1080')

# create images and append to the above defined list
img_lst = []
for img in img_names:
    image = Image.open(img).resize((1920, 950), Image.ANTIALIAS)
    photo = ImageTk.PhotoImage(image)
    img_lst.append(photo)
# a short list comprehension to do this exact same thing but in one line:
# img_lst = [ImageTk.PhotoImage(Image.open(img_name).resize((1920, 950), Image.ANTIALIAS)) for img_name in img_names]

buttons = Button(root, text="Start!", command=start, bg="#29a8ab", fg="#cccccc", padx=50, font='Helvetica 19 bold')
buttons.place(x=960, y=540)

button_right = Button(root, text="Next!", command=right_pic, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')
button_close = Button(root, text="Close!", command=close, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')

# create the image label but don't yet put image there
# so that it can be done in the `start()` function
# so that it resets every time you press `start()`
image_label = Label(root)

root.mainloop()

下面是上面没有注释的代码(但请花时间阅读它们以获得所有建议):

from tkinter import Tk, Label, Button
from PIL import Image, ImageTk


img_names = ['flower1.jpg', 'flower2.jpg', 'flower3.jpg', 'flower4.jpg']
index = 0


def right_pic():
    global index
    index += 1
    if index == len(img_lst):
        index = 0
    image_label.config(image=img_lst[index])


def start():
    global index
    index = 0
    image_label.config(image=img_lst[index])
    image_label.grid(row=0)
    button_right.place(x=900, y=950)
    button_close.place(x=700, y=950)


def close():
    image_label.grid_forget()
    button_close.place_forget()
    button_close.place_forget()


root = Tk()
root.geometry('1920x1080')

img_lst = []
for img in img_names:
    image = Image.open(img).resize((1920, 950), Image.ANTIALIAS)
    photo = ImageTk.PhotoImage(image)
    img_lst.append(photo)

buttons = Button(root, text="Start!", command=start, bg="#29a8ab", fg="#cccccc", padx=50, font='Helvetica 19 bold')
buttons.place(x=960, y=540)

button_right = Button(root, text="Next!", command=right_pic, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')
button_close = Button(root, text="Close!", command=close, bg="#0e9aa7", fg="#f6cd61", padx=50, font='Helvetica 19 bold')

image_label = Label(root)

root.mainloop()

您可以很容易地看到这段代码有多短,但您也必须理解 它的可扩展性有多大,因为您真正需要添加更多图像的只是向列表中添加更多名称(或者使用注释中提到的os.listdir()方法)

请注意,我没有测试任何代码,因为我没有图像,但它们应该可以工作(如果没有,请告诉我,我将尝试修复)

一些资料来源:

如果你还有任何问题,问他们,我会尽力回答他们

相关问题 更多 >

    热门问题