如何从Child(BoxLayout)类定期更新父(屏幕)类UI(使用Kivy的Python)

时间:2018-03-01 15:27:49

标签: python kivy

目标:

从子(boxlayout)类定期更新父(屏幕)类/ UI。 conf2.dat偶尔会更新(来自其他各种屏幕),我希望通过重新运行此类,每隔5秒左右更新一次UI。

最新代码更新:

  1. __init__函数中,我有Clock.schedule_interval(self.create_button, 1),这会导致create_button函数每秒重新运行。
  2. create_button函数的顶部,我有self.box_share.clear_widgets(),它应该清除所有小部件,以便可以重新填充(根据create_button函数下面的说明)。
  3. 动作:

    1. 我运行代码
    2. 点击带有文字标题“更新顺序”
    3. 的按钮,导航到NoTags屏幕
    4. 我通过点击它们来更改在scrollview下动态创建的按钮。他们成功改变了颜色。此信息将写入conf2.dat文件。
    5. 首先单击“主页”按钮,然后单击“序列显示”按钮,导航到SequenceScreen屏幕。此SequenceScreen屏幕是我希望更新的屏幕,以反映对conf2.dat文件所做的更改。
    6. 结果:

      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:
      

2 个答案:

答案 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))