【问题标题】:Kivy/Python - How to display results from sqlite db in Popup from RecycleViewKivy/Python - 如何在 RecycleView 的弹出窗口中显示来自 sqlite db 的结果
【发布时间】:2019-07-17 03:48:43
【问题描述】:

我的这部分代码基于此处其他帖子的文档和信息,例如:

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

【问题讨论】:

    标签: python sqlite popup kivy


    【解决方案1】:

    我认为处理此问题的最简单方法是将单词数据保存在Dictionary 中,并使用该Dictionary 来提供RecycleViewMessageBox。为此,我从您的app.py 中删除了全局word_dict,并将其添加到您的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 数据,因此kv 文件中不需要data: 属性。所以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 的代码。我不确定它的用途,但这可能需要修改。

    【讨论】:

    • 谢谢,这似乎工作得很好。我在完整程序中对其进行了测试并进行了一些调整。 word_dict 是我删除的代码的剩余部分,以便在此处发布。第一页有一个按钮,您可以按此按钮获取一个随机单词,然后将其从“未读”单词 db 中删除并添加到“已读”单词 db。我认为我可以将其保留为全局,因为 RandomWords 类和 UnreadWords 类页面都从同一个数据库中提取,但它在获取随机单词时会引发关键错误。所以我把 word_dict 作为一个全局删除,将它添加到 RandomWords,并在 UnreadWords 类中添加了一个 unread_dict。
    • 我仍然有标签被打印两次的问题,但你已经解决了我的主要问题,谢谢。我已经添加了该问题的图片。
    • 我在代码中的任何位置都找不到显示在图像中的文本,我也看不到这种效果。当您单击SelectableButton 时会出现这种情况吗?如果是这样,它可能与MessageBoxon_open 属性有关。尝试将其注释掉。
    • 抱歉,该示例中的文本来自我不知道如何在此处包含的 db 文件。根据您之前的建议,我已经删除了 on_open 属性。我发现了这个问题——我需要在 之前添加一个破折号:在 kv 文件中:。使用这个小部件,我结合了缩放按钮和可选按钮的属性。我不确定,但我认为没有破折号它同时使用了自定义画布指令以及从小部件继承的指令。再次感谢您帮助我解决这些问题。
    猜你喜欢
    • 2017-02-01
    • 2016-02-08
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 1970-01-01
    • 2014-09-04
    • 1970-01-01
    • 2020-07-03
    相关资源
    最近更新 更多