Почему это происходит с моим кодом tkinter?

avatar
SinaSalehi
8 августа 2021 в 21:57
88
1
0

Поэтому я сделал средство просмотра изображений в другой день, и в основном, что оно делает, так это переходит к следующему изображению и возвращается к первому, когда наш набор изображений заканчивается. Я сделал кнопку под названием Старт, которая в основном запускает всю программу просмотра изображений и отображает изображение номер 1, и я также сделал кнопку Закрыть, которая переворачивает все это в начальную точку (вы снова увидите кнопку запуска).

... Но происходит очень странная вещь. когда я нажимаю свои кнопки в следующем порядке: Start, Next, Закрыть, Старт, Далее` — все сходит с ума, и мои кнопки исчезают!

Извините, если я написал код очень неэффективно, потому что я начал изучать Python 2 дня назад! Я также сделал этот вывод в конце каждого 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()
Источник
Matiiss
9 августа 2021 в 01:16
0

Проблема довольно забавная, и она заключается в том, что во второй (и другой) раз, когда вы вызываете next(), cimage по-прежнему устанавливается на второе изображение, поэтому он действует так, как если бы ему приходилось сетку третьего, что, вероятно, выталкивает все из окна, проще всего наверное добавить cimage = 'flower1.jpg' в начало функции start() def, наверное напишу правильный ответ (через несколько часов) а так же постараюсь показать как улучшить ваш код

Ответы (1)

avatar
Matiiss
9 августа 2021 в 11:08
2

Сначала отвечу на вопрос: почему кнопки исчезают при нажатии на них в такой последовательности?
Это связано с тем, что при вызове 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 — Руководство по стилю для кода Python, например, иметь две строки между определениями функций, иметь пробел после запятой (,), но не раньше, не Не ставьте ненужные пробелы, такие как 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(), упомянутый в комментариях).

Обратите внимание, что я не проверял коды, так как у меня не было изображений, но они должны работать (если нет, скажите, я постараюсь это исправить).

Некоторые источники:

Если у вас есть еще вопросы, задавайте их, я постараюсь на них ответить.