Kivy不显示组件

0 投票
1 回答
1992 浏览
提问于 2025-04-18 09:25
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, DictProperty
from kivy.network.urlrequest import UrlRequest
import json
from kivy.uix.listview import ListItemButton
from kivy.uix.label import Label
from kivy.factory import Factory

class LocationButton(ListItemButton):
    pass

class WeatherRoot(BoxLayout):

    def show_current_weather(self,location):
        self.clear_widgets()
        current_weather = Factory.CurrentWeather()
        current_weather.location = location
        self.add_widget(current_weather)

    def show_add_location_form(self):
        self.clear_widgets()
        self.add_widget(AddLocationForm())

class AddLocationForm(BoxLayout):

    search_input = ObjectProperty()
    search_results = ObjectProperty()

    def search_location_name(self):
        search_template = 'http://api.openweathermap.org/data/2.5/' + 'find?q={}&type=like'
        search_url = search_template.format(self.search_input.text)
        request = UrlRequest(search_url, self.found_location_name)
        if len(self.search_results.item_strings) == 0:
            self.search_results.item_strings.append('City not found')

    def search_location_coor(self):
        try:
            search_coor = self.search_input.text
            search_input_coordinates = search_coor.split(',')
            search_template_coordinates = 'http://api.openweathermap.org/data/2.5/' + "weather?lat=%s&lon=%s&radius=10" % (search_input_coordinates[0] , search_input_coordinates[1])
            request_coordinates = UrlRequest(search_template_coordinates, self.found_location_coordinates)
        except (IndexError):
            self.search_results.item_strings.append('Coordinates needed are Lattitude and Longtitude!')


    def found_location_coordinates(self,request_coordinates,data_coordinates):
        try:
            data_coordinates = json.loads(data_coordinates.decode()) if not isinstance(data_coordinates, dict) else data_coordinates
            cities_coordinates = ['{} ({})'.format(data_coordinates['name'],data_coordinates['sys']['country'])]
            self.search_results.item_strings = cities_coordinates
            self.search_results.adapter.data[:]
            self.search_results.adapter.data.extend(cities_coordinates)
            self.search_results._trigger_reset_populate()
        except KeyError:
            self.search_results.item_strings.append('Wrong Coordinates')

    def found_location_name(self, request, data):
        data = json.loads(data.decode()) if not isinstance(data, dict) else data
        cities = ['{} ({})'.format(d['name'],d['sys']['country']) for d in data['list']]
        self.search_results.item_strings = cities
        self.search_results.adapter.data[:]
        self.search_results.adapter.data.extend(cities)
        self.search_results._trigger_reset_populate()
        if len(self.search_results.item_strings) == 0:
            self.search_results.item_strings.append('City not found')


class WeatherApp(App):
    pass


if __name__ == '__main__':
    WeatherApp().run()

还有这个kv文件:

#:import main main
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
WeatherRoot:

<WeatherRoot>:
    AddLocationForm:
        orientation: 'vertical'
        search_input: search_box
        search_results: search_results_list
        BoxLayout:
            height: '40dp'
            size_hint_y: None
            TextInput:
                id: search_box
                size_hint_x: .5
            Button:
                text: 'Search'
                size_hint_x: .25
                on_press: self.parent.parent.search_location_name()
            Button:
                text: 'Current Location'
                size_hint_x: .25
                on_press: self.parent.parent.search_location_coor()
        ListView:
            id: search_results_list
            item_strings: []
            adapter:
                 ListAdapter(data=[], cls=main.LocationButton)
<LocationButton>:
    on_press: app.root.show_current_weather(self.text)
<CurrentWeather@BoxLayout>:
    location: ''
    conditions: None
    temp: None
    temp_min: None
    temp_max: None
    orientation: 'vertical'
    Label:
        text: root.location
    BoxLayout:
        size_hint_y: None
        height: '40dp'
        Button:
            text: 'Add Location'
            on_press: app.root.show_add_location_form()
        Button:
            text: 'Forecast'

这个程序能做什么和不能做什么:

  1. 可以通过城市名称或坐标来搜索城市

  2. 会列出所有符合你搜索条件的城市

  3. 你可以选择一个城市

  4. 会打开一个黑色的页面,上面有城市的名字和两个按钮(添加位置和天气预报)

如果你点击添加位置的按钮,它应该打开最初的窗口,但我却看到一个黑屏,我是不是漏掉了什么?

这段代码来自书籍《用kivy创建应用程序》

1 个回答

2

我不太清楚这本书想表达什么,但你给出的代码有个问题,就是点击“添加位置”会运行 show_add_location_form 这个方法。

def show_add_location_form(self):
        self.clear_widgets()
        self.add_widget(AddLocationForm())

代码的第一部分会清除你所有的控件,这就是为什么你看到黑屏。第二部分是添加一个 AddLocationForm,但如果你看看其他的代码,你会发现这个表单其实没有定义外观……所以屏幕还是黑的。

我通常会给一些建议来解决这个问题,但因为这是书里的内容,你确定这不是故意的,书里只是还没讲到下一步吗?

不过,我觉得下面的修改可能会有效,就是把 AddLocationForm 的定义从 WeatherRoot 规则中去掉,这样每个 AddLocationForm 实例就会有相同的规则。在这种情况下,这应该能让你的代码正常工作,因为它会把规则应用到新的 AddLocationForm 实例上。不过我还没试过。

<WeatherRoot>:
    AddLocationForm:

<AddLocationForm>
    orientation: 'vertical'
    search_input: search_box
    search_results: search_results_list
    BoxLayout:
        height: '40dp'
        size_hint_y: None
        TextInput:
            id: search_box
            size_hint_x: .5
        Button:
            text: 'Search'
            size_hint_x: .25
            on_press: self.parent.parent.search_location_name()
        Button:
            text: 'Current Location'
            size_hint_x: .25
            on_press: self.parent.parent.search_location_coor()
    ListView:
        id: search_results_list
        item_strings: []
        adapter:
             ListAdapter(data=[], cls=main.LocationButton)

请注意(假设它有效),这只是一个快速的解决办法,如果你想用多个屏幕显示不同的内容,还有更好的方式来组织你的应用。我想这本书后面会讲到这些。

撰写回答