【问题标题】:widgets disappears after click on button in tkinter?单击tkinter中的按钮后小部件消失?
【发布时间】:2025-12-26 05:25:15
【问题描述】:

我设置了一个天气应用程序。我创建了两个Threads,第一个Thread1 抓取天气状况,第二个Thread2 设置背景图像,但是当我单击按钮时tkinter 小部件消失,悬停后它会出现。我认为thread 存在一些问题。我能理解吗?

我的代码:

import requests
from tkinter import *
from bs4 import BeautifulSoup
from threading import  Thread
from io import BytesIO
from PIL import ImageTk, Image

class gui(Tk):
    def __init__(self):
        super().__init__()
        self.call_weather_True = False
        self.icon_link = ''
        self.text = ''
        self.weather = ''
        self.icon_set = ''
        self.back_icon = ''

        # title of app
        self.title('AccWeather')
        self.geometry('700x500')

        # API
        self.api = 'vvfhMQuqmjOgcq1ctGqgmH55bqMPVH3c'

        # main canvas with image
        self.main = Label(self)
        self.main.place(relx=0, rely=0, relwidth=1, relheight=1)


        # button and input
        self.button_frame = Frame(self.main)
        self.button_frame.place(relx=.1, rely=.1, relwidth=.8)
        self.city = Entry(self.button_frame, bd=0, font=3, fg='black', width=50)
        self.city.pack(side=LEFT)
        Button(self.button_frame, text='Get Weather', bg='DodgerBlue', font=5, bd=1, command=self.get_weather_call).pack(side=RIGHT)

        self.put_out = Label(self.main, font=20, anchor='nw', bg='white', justify='left', bd=5)  # border = bd
        self.put_out.place(relx=.1, rely=.4, relwidth=.5, relheight=.5)

    def get_weather_call(self):
        self.call_weather_True = True
        city = self.city.get()
        url = 'https://images.unsplash.com/photo-1451154488477-d20dee2a4e46?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=753&q=80'

        thread_background_img = Thread(target=self.background_icon, args=(url,))
        thread2 = Thread(target=self.get_weather_call, args=[city])
        thread2.start()
        thread_background_img.start()

    def get_weather(self, city):
        # autocomplete location
        try:
            auto_url = f"http://dataservice.accuweather.com/locations/v1/cities/autocomplete?apikey={self.api}&q=" + city
            data = requests.get(auto_url).json()
            check = ''
            for i in data:
                for a in i:
                    if a=='ServiceUnavailable':
                        check = True
            if  check:
                self.put_out['text'] = 'Error: '+data['Message']
            else:
                try:
                    key = data[0]['Key']
                    city_name = ', '.join([data[0]['LocalizedName'], data[0]['Country']['LocalizedName']])
                    api = requests.get(f"http://dataservice.accuweather.com/currentconditions/v1/{key}?apikey={self.api}").json()
                    self.icon_link = api[0]['Link']

                    temp = api[0]['Temperature']['Metric']['Value']
                    self.text = api[0]['WeatherText']
                    self.weather = f'City: {city_name}\nTemperature (c): {int(temp)}\nCondition: {self.text}'
                    self.put_out['text'] = self.weather

                except Exception as e:
                    self.put_out['text'] = e
        except Exception as e:
            print(e)

    def background_icon(self, url):
        img_data = requests.get(url).content
        self.back_icon = ImageTk.PhotoImage(Image.open(BytesIO(img_data)))
        self.main['image'] = self.back_icon


if __name__ == '__main__':
    start = gui()
    start.mainloop()


我花了一天时间想通了,但我发现了。为什么tkinter button,entry 消失了?

【问题讨论】:

  • 当我在终端/控制台中运行代码时,我看到了不同的错误,因为它获取了没有域 /textinputassistant/tia.png 的图像 url,所以 requests.get(img) 无法加载此图像。
  • 但我在 tkinter 中的中心位置获取此图像。
  • 你输入天气位置点击按钮就知道了
  • 谷歌似乎在不同的地区/国家给出了不同的结果。
  • 我也收到了Thread 错误。

标签: python python-3.x multithreading tkinter tkinter-canvas


【解决方案1】:

我重构了你的代码,你为什么使用线程?

无论如何,如果我了解您想在工作脚本下面做什么,主要问题是 Api 返回授权失败。

#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox

import requests
import json
from PIL import ImageTk, Image
from io import BytesIO


class App(tk.Tk):
    """Docstring about this class"""

    def __init__(self):
        super().__init__()

        self.protocol("WM_DELETE_WINDOW", self.on_exit)
        self.geometry('800x400')
        s = "{0}".format('AccWeather')
        self.title(s)

        self.text = tk.StringVar()
        self.code = tk.StringVar()
        self.message = tk.StringVar()
        self.reference = tk.StringVar()

        self.init_ui()

    def init_ui(self):

        img = self.set_backgroundn()

        f = ttk.Label(image=img)

        f.image = img

        ttk.Label(f, text="Search a city!").pack()

        self.txText = ttk.Entry(f, textvariable=self.text).pack(side=tk.TOP, fill=tk.X, expand=0)

        ttk.Label(f, textvariable=self.code).pack(fill=tk.X,padx=2,pady=2)
        ttk.Label(f, textvariable=self.message).pack(fill=tk.X,padx=2,pady=2)
        ttk.Label(f, textvariable=self.reference).pack(fill=tk.X,padx=2,pady=2)

        f.pack(fill=tk.BOTH, expand=1)

        w = tk.Frame()

        tk.Button(w, text="Get Weather", width=8, command=self.get_weather).pack()
        tk.Button(w, text="Close", width=8, command=self.on_exit).pack()

        f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        w.pack(side=tk.RIGHT, fill=tk.BOTH,  padx=5, pady=5, expand=0)

        self.set_backgroundn()

    def get_weather(self,):

        api = 'vvfhMQuqmjOgcq1ctGqgmH55bqMPVH3c'

        auto_url = "http://dataservice.accuweather.com/locations/v1/cities/autocomplete?apikey={0}&q={1}".format(api,self.text.get())

        response = requests.get(auto_url)

        d = response.json()
        self.code.set(d['Code'])
        self.message.set(d['Message'])
        self.reference.set(d['Reference'])

    def set_backgroundn(self,):

        url = 'https://images.unsplash.com/photo-1451154488477-d20dee2a4e46?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=753&q=80'
        img = requests.get(url).content

        return ImageTk.PhotoImage(Image.open(BytesIO(img)))


    def on_exit(self):
        """Close all"""
        if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
            self.destroy()               

if __name__ == '__main__':
    app = App()
    app.mainloop()

【讨论】:

  • 我能知道为什么是-1吗?您有更好、更有效的解决方案吗?
  • 那么from tkinter import tkfrom tkinter import ttk有什么区别?
  • @RohitDalal: from tkinter import tk 会失败; tkinter 模块没有名为 tk 的东西。
  • 此代码小部件在单击按钮后消失。为什么会这样?