Kivy / Python-如何在RecycleView的Popup中显示来自sqlite db的结果

时间:2019-07-17 03:48:43

标签: python sqlite popup kivy

我的代码这一部分基于此处的其他文章的文档和信息:

How to fetch data from database and show in table in kivy+python

目标是要有一个滚动的单词列表,其中仅带有单词作为可选按钮,当您单击该按钮时,单词,发音和翻译应显示在弹出窗口中。所有这些都是从不断更新的sqlite数据库中的行中提取的。

我基本上都能正常工作,但我不知道怎么做的一件事就是分离数据(以字符串字典的形式),以仅在列表中显示单词(第1列),并显示所有弹出窗口中的信息(第1、2和3列)。我也不想遍历整个字典,因为尽管这里的示例数据库只有8个项目,但真正的数据库却有数千个项目。当我单击可选按钮时,我希望从数据库中提取信息。

是否可以从以下代码的各个行中提取文本?

data: [{'text': f'{entry[0], entry[1], entry[2]}'} for entry in root.rows]

或将数据分别设置为变量,然后使用它们-

data: [{'text': f'{entry[0]}'} for entry in root.rows]

data2: [{'text': f'{entry[1]}'} for entry in root.rows]

data3: [{'text': f'{entry[2]}'} for entry in root.rows]

还有其他我想念的方式吗?

我遇到的另一个问题是,可选按钮上的文本显示了两次-在按钮上以及在下面(在缩小窗口时可以看到),我不知道为什么或如何修复它

感谢您的帮助。

数据库的最低代码:https://github.com/nitro9a/word_a_day_minimum

app.py

import csv
import sqlite3
import random
import textwrap
from utils import database, scalelabel, scrollablelabel, recycleselect
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.recycleview import RecycleView
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty, StringProperty

word_dict = {}

class MessageBox(Popup):
    def popup_dismiss(self):
        self.dismiss()

    obj = ObjectProperty(None)
    obj_text = StringProperty('')

    def __init__(self, obj, **kwargs):
        super(MessageBox, self).__init__(**kwargs)
        self.obj = obj
        self.obj_text = obj.text #what is in the message box, will display same on click

class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout):
    """ Adds selection and focus behaviour to the view. """

class SelectableButton(RecycleDataViewBehavior, Button):
    """ Add selection support to the Label """
    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

    def refresh_view_attrs(self, rv, index, data):
        """ Catch and handle the view changes """
        self.index = index
        print(type(data))
        #print(f'Data: {data.items()}, Index: {index},rv: {rv}, Type: {type(data)}')
        return super(SelectableButton, self).refresh_view_attrs(rv, index, data)

    def apply_selection(self, rv, index, is_selected):
        self.selected = is_selected

    def on_press(self):
        popup = MessageBox(self)
        popup.open()

    def update_changes(self, txt):
        self.text = txt

class RV(RecycleView):
    #data_items = ListProperty([])
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)

class WindowManager(ScreenManager):
    pass

class UnreadWords(Screen):

    unread_table = ObjectProperty(None)
    rows = ListProperty([("Word", "Pronunciation", "English")])

    def __init__(self, **kwargs):
        super(UnreadWords, self).__init__(**kwargs)

    def display_database(self):
        con = sqlite3.connect('italian_unread.db')
        cursor = con.cursor()
        cursor.execute("SELECT Word, Pronunciation, English from Italian_a")
        self.rows = cursor.fetchall()

kv = Builder.load_file("layout.kv")

class WordApp(App):
    def build(self):
        return kv

if __name__=="__main__":
    WordApp().run() 

layout.kv

#: import NoTransition kivy.uix.screenmanager.NoTransition
#: include italian_a

<RV>:
    viewclass: 'SelectableButton'
    RecycleBoxLayout:
        bcolor: 1,1,1,1
        padding: "15dp", "5dp", "15dp", "15dp"
        default_size: None, dp(25)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

<SelectableButton>:
    state_image: self.background_normal if self.state == 'normal' else self.background_down
    disabled_image: self.background_disabled_normal if self.state == 'normal' else self.background_disabled_down
    _scale: 1. if self.texture_size[0] < self.width else float(self.width) / self.texture_size[0]
    orientation: 'horizontal'
    canvas:
        Color:
            rgba: self.background_color
        BorderImage:
            border: self.border
            pos: self.pos
            size: self.size
            source: self.disabled_image if self.disabled else self.state_image
        PushMatrix
        Scale:
            origin: self.center
            x: self._scale or 1.
            y: self._scale or 1.
        Color:
            rgba: self.disabled_color if self.disabled else self.color
        Rectangle:
            texture: self.texture
            size: self.texture_size
            pos: int(self.center_x - self.texture_size[0] / 2.), int(self.center_y - self.texture_size[1] / 2.)
        PopMatrix

<MessageBox>:
    name: 'mbox'
    lbl: lbl
    title: ''
    size_hint: None, None
    size: 400, 400
    on_open:
        root.obj.update_changes(lbl.text)

    BoxLayout:
        orientation: 'vertical'
        Label:
            id: lbl
            text: root.obj_text
        Button:
            size_hint: 1, 0.2
            text: 'OK'
            on_press:
                root.dismiss()

WindowManager:
    transition: NoTransition()
    UnreadWords:

<UnreadWords>:
    name: "unread"
    unread_table: unread_table

    BoxLayout:
        orientation: "vertical"
        rows: 4
        cols: 1

        GridLayout:
            cols: 2
            rows: 1
            size_hint_y: 5

            ScaleButton:
                id: page2
                text: "Page 2"
                on_release:
                    app.root.current = "unread"
                    root.display_database()

        GridLayout:
            cols: 1
            rows: 1
            size_hint_y: 5

            ScaleLabel:
                text: "Unread Words"
                size_hint_y: 5
                color: (0/255., 0/255., 0/255., 1)
                background_normal: ''
                bcolor: (155/255., 155/255., 155/255., 1)

        GridLayout:
            cols: 1
            rows: 1
            size_hint_y: 80

            BoxLayout:
                id: unread_table
                RV:
                    id: dat
                    viewclass: 'SelectableButton'
                    size_hint_y: 1
                    font_size: self.height * 0.5

                    data: [{'text': f'{entry[0], entry[1], entry[2]}'} for entry in root.rows]
                    #data: [{'text': f'{entry[0]}'} for entry in root.rows]

Image of label being displayed twice

1 个答案:

答案 0 :(得分:0)

我认为处理此问题的最简单方法是将单词数据保存在Dictionary中,并使用该Dictionary来填充RecycleViewMessageBox。为此,我从您的word_dict中删除了全局app.py,并将其添加为您的UnreadWords Screen的方式为:

class UnreadWords(Screen):

    unread_table = ObjectProperty(None)
    rows = ListProperty([("Word", "Pronunciation", "English")])

    def __init__(self, **kwargs):
        super(UnreadWords, self).__init__(**kwargs)
        self.word_dict = {}

    def display_database(self):
        con = sqlite3.connect('italian_unread.db')
        cursor = con.cursor()
        cursor.execute("SELECT Word, Pronunciation, English from Italian_a")
        self.rows = cursor.fetchall()

        # populate the self.word_dict dictionary
        for row in self.rows:
            self.word_dict[row[0]] = [row[1], row[2]]

        # create the `data` for the RecycleView
        self.ids.dat.data = [{'text': key} for key in self.word_dict.keys()]

由于在以上代码中创建了RecycleView数据,因此data:文件中不需要kv属性。因此RV条目看起来像:

            RV:
                id: dat
                viewclass: 'SelectableButton'
                size_hint_y: 1
                font_size: self.height * 0.5

MessageBox类变为:

class MessageBox(Popup):
    def popup_dismiss(self):
        self.dismiss()

    obj = ObjectProperty(None)
    obj_text = StringProperty('')

    def __init__(self, obj, **kwargs):
        super(MessageBox, self).__init__(**kwargs)
        self.obj = obj

        # set the Popup text to the pronunciation and translation
        # from the word_dict
        word_data = kv.get_screen('unread').word_dict[obj.text]
        self.obj_text = word_data[0] + '\n' + word_data[1]

MessageBox中有修改SelectableButton的代码。我不确定它的用途,但是可能需要修改。