目标:
从子(boxlayout)类定期更新父(屏幕)类/ UI。 conf2.dat
偶尔会更新(来自其他各种屏幕),我希望通过重新运行此类,每隔5秒左右更新一次UI。
最新代码更新:
__init__
函数中,我有Clock.schedule_interval(self.create_button, 1)
,这会导致create_button
函数每秒重新运行。create_button
函数的顶部,我有self.box_share.clear_widgets()
,它应该清除所有小部件,以便可以重新填充(根据create_button
函数下面的说明)。 动作:
NoTags
屏幕
conf2.dat
文件。SequenceScreen
屏幕。此SequenceScreen
屏幕是我希望更新的屏幕,以反映对conf2.dat文件所做的更改。 结果:
与SequenceScreen(Screen)
类相关联的用户界面仍然不会根据与NoTags(Screen)
类关联的用户界面所做的更改进行更新。
然而,当我完全重启应用程序时,我发现SequenceScreen
UI已成功更新。
怀疑:
我只需要一行代码就可以让这个UI正确更新。
Python代码:
from kivy.app import App
# kivy.require("1.10.0")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.scrollview import ScrollView
#from kivy.base import runTouchApp
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from kivy.storage.dictstore import DictStore
import pickle
import datetime, threading
import time
from kivy.clock import mainthread
class BackHomeWidget(Widget):
pass
class SequenceBoxLayout_NoEdits(BoxLayout):
box_share = ObjectProperty()
config_file = DictStore('conf2.dat')
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoEdits, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 1)
def create_button(self, *args):
self.box_share.clear_widgets()
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(25):
top_button_share -= .4
id_ = "part" + str(i + 1)
if self.config_file.exists(id_):
btn_color = self.config_file[id_]["background_color"]
else:
self.config_file.put(id_, background_color=color)
btn_color = color
button_share = Button(background_normal='',
background_color=btn_color,
id=id_,
pos_hint={"x": 0, "top": top_button_share},
size_hint_y=None,
height=60,
font_size = 30,
text= str(i+1)
self.box_share.add_widget(button_share)
#Clock.schedule_interval(self.parent.ids.updatedisplay.create_button(self, *args) , 1)
#self.parent.ids.updatedisplay.create_button(self, *args)
class SequenceBoxLayout_NoTag(BoxLayout):
box_share = ObjectProperty()
config_file = DictStore('conf2.dat')
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoTag, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_once(self.create_button)
def create_button(self, *args):
df = pd.read_excel("Test.xlsx","Sheet1")
parts = df['parts'].values.tolist()
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(len(parts)):
top_button_share -= .4
id_ = "part" + str(i + 1)
if self.config_file.exists(id_):
btn_color = self.config_file[id_]["background_color"]
else:
self.config_file.put(id_, background_color=color)
btn_color = color
button_share = Button(background_normal='',
background_color=btn_color,
id=id_,
pos_hint={"x": 0, "top": top_button_share},
size_hint_y=None,
height=60,
font_size = 30,
text= str(i+1)+ ". " + str(parts[i]))
if self.parent.name == 'notags':
button_share.bind(on_press=self.update_buttons_notag)
self.box_share.add_widget(button_share)
def update_buttons_notag(self, button):
button.background_color = 0.86,0.54,0.04,1
self.config_file.put(button.id, background_color=(0.86,0.54,0.04,1))
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class SequenceScreen(Screen):
pass
class NoTags(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("updatelistexample.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
KV代码:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
NoTags:
SequenceScreen:
<SmallNavButton@Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<BigButton@Button>:
font_size: 40
size_hint: 0.5, 0.15
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
BigButton:
on_release: app.root.current = "notags"
text: "updating sequence"
pos_hint: {"x":0.25, "top": 0.4}
BigButton:
on_release: app.root.current = "sequence"
text: "sequence display"
pos_hint: {"x":0.25, "top": 0.7}
<AnotherScreen>:
name: "newgarage"
<NoTags>:
name: "notags"
SequenceBoxLayout_NoTag:
BackHomeWidget:
FloatLayout:
BigButton:
text: "Select Parts w/o Tags"
pos_hint: {"x":0.5, "top": 0.6}
background_normal: ''
background_color: (0.4,0.4,0.4,1)
<SequenceBoxLayout_NoEdits>:
box_share: box_share
ScrollView:
GridLayout:
id: box_share
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<SequenceBoxLayout_NoTag>:
box_share: box_share
ScrollView:
GridLayout:
id: box_share
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<SequenceScreen>:
name: "sequence"
SequenceBoxLayout_NoEdits:
id: updatedisplay
BackHomeWidget:
答案 0 :(得分:0)
Clock
有一个schedule_interval
方法,其工作方式与schedule_once
方法非常相似,但每个 n
秒都会被称为,而不仅仅是一次n
秒后。
因此,您当然可以在__init__
中更改该通话,并通过调用create_button
启动self.box_share.clear_widgets()
,以便在重新创建之前从之前的通话中移除小部件。
这可能有点浪费,如果你发现自己重新创建了许多小部件,即使没有改变,所以你可以添加一些逻辑来首先检查数据是否没有改变,或者即使它没有改变,只需重用如果可能的话,旧按钮。
box = self.box_share
old_buttons = box.children[:]
box.clear_widgets()
# [your logic about computing the list of parts]
for i, p in enumerate(parts): # this is much better than doing range(len(parts))
# [the logic to get the content of the button]
if old_buttons: # check there are still buttons in the list of old buttons
btn = old_buttons.pop()
else:
btn = Button(
background_normal='',
background_color=btn_color,
pos_hint={"x": 0, "top": top_button_share},
# etc, all the things that are common to all your buttons
# but really, hardcoding them like this is a bit painful,
# you should use a subclass of button so you can style it
# in a kv rule so it would apply to all of them directly
)
btn.id=id_
btn.text = "{}. {}".format(i+1, p)
btn.pos_hint["top"] = top_button_share
# re-apply any other property that might have changed for this part
box.add_widget(btn)
但实际上,这种逻辑非常普遍,而且在更多情况下你可以采取其他措施来改善事物,尽管这是相当有用的。
值得庆幸的是,你并不是第一个需要这样做的人,而且我们很幸运地拥有了精彩的RecycleView
,它可以自动完成所有这些以及更多功能,你需要提供data
目录,它将创建必要的小部件来填充滚动视图的可见部分,如果有足够的小部件来保证滚动,并在数据更改时自动更新,并滚动查看列表的不同部分。我鼓励你自己检查一下。但最终结果肯定会像。
<PartButton@Button>:
id_: None
part: ''
text: '{}. {}'.format(self.id, self.part)
<SequencedBoxLayout@Recycleview>:
parts: self.get_parts()
viewclass: 'PartButton'
data:
[
{
id_: i,
part: part
} for i, p in enumerate(self.parts or [])
]
RecycleBoxLayout:
答案 1 :(得分:0)
<强>信用卡:强>
基于@Tshirtman在发布的问题的评论主题中提供的建议......
<强>要点:强>
代码的问题与我有两个不同的DictStore指向同一个文件的事实有关,这个文件正在绊倒两个类之间的通信。
解决方案是只使用一个DictStore并在App类中定义该变量,然后引用子类中的特定变量[using App.get_running_app()
],如下所示:
在App类中定义config_file:
class MainApp(App):
config_file = DictStore('conf2.dat')
def build(self):
return presentation
子类中的参考App变量:
class SequenceBoxLayout_NoEdits(BoxLayout):
...
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoEdits, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 1)
def create_button(self, *args):
self.box_share.clear_widgets()
app = App.get_running_app()
...
for i in range(len(parts)):
...
if app.config_file.exists(id_):
btn_color = app.config_file[id_]["background_color"]
else:
app.config_file.put(id_, background_color=color)
...
...
class SequenceBoxLayout_NoTag(BoxLayout):
...
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoTag, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_once(self.create_button)
def create_button(self, *args):
...
app = App.get_running_app()
...
for i in range(len(parts)):
...
if app.config_file.exists(id_):
btn_color = app.config_file[id_]["background_color"]
else:
app.config_file.put(id_, background_color=color)
...
...
def update_buttons_notag(self, button):
app = App.get_running_app()
...
app.config_file.put(button.id, background_color=(0.86,0.54,0.04,1))