【问题标题】:How to retrieve data from sqlite Db and set to kivy TextInput fields?如何从 sqlite Db 检索数据并设置为 kivy TextInput 字段?
【发布时间】:2018-07-20 09:39:49
【问题描述】:

我通过使用 kivy 文档和其他在线资源制作小型应用程序来学习 kivy。当前代码有 两个文本输入字段 (UserID, UserName) 将值存储在 db 中,使用 RecycleView 显示存储的数据 在带有 Kivy GUI 的按钮上。

按下按钮时,我需要将数据库中的相应数据设置为相应的文本输入字段。即开 按下 UserID 为 1 的按钮,文本输入字段应显示该行的 UserID 和 UserNames 的值。

ma​​in.py

import sqlite3
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior

class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
                                  RecycleGridLayout):
    ''' Adds selection and focus behaviour to the view. '''


class SelectableButton(RecycleDataViewBehavior, Button):
    ''' Add selection support to the Button '''
    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
        return super(SelectableButton, self).refresh_view_attrs(rv, index, data)

    def on_touch_down(self, touch):
        ''' Add selection on touch down '''
        if super(SelectableButton, self).on_touch_down(touch):
            return True
        if self.collide_point(*touch.pos) and self.selectable:
            return self.parent.select_with_touch(self.index, touch)

    def apply_selection(self, rv, index, is_selected):
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected

class RV(BoxLayout):
    ''' Creates Db conn, table, and saves data, retrives stored data and
    displays in the RecycleView .'''
    data_items = ListProperty([])

    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.create_table()
        self.get_users()

    def create_table(self):
        connection = sqlite3.connect("demo.db")
        cursor = connection.cursor()
        sql = """CREATE TABLE IF NOT EXISTS Users(
        UserID integer PRIMAY KEY,
        UserName text NOT NULL)"""
        cursor.execute(sql)
        connection.close()

    def get_users(self):
        connection = sqlite3.connect("demo.db")
        cursor = connection.cursor()

        cursor.execute("SELECT * FROM Users ORDER BY UserID ASC")
        rows = cursor.fetchall()

        # create data_items
        for row in rows:
            for col in row:
                self.data_items.append(col)

    def save(self):
        connection = sqlite3.connect("demo.db")
        cursor = connection.cursor()

        UserID = self.ids.no.text
        UserName = self.ids.name.text

        try:
            save_sql="INSERT INTO Users (UserID, UserName) VALUES (?,?)"
            connection.execute(save_sql,(UserID, UserName))
            connection.commit()
            connection.close()
        except sqlite3.IntegrityError as e:
            print("Error: ",e)

class TestApp(App):
    title = "Kivy RecycleView & SQLite3 Demo"

    def build(self):
        return RV()


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

test.kv

#:kivy 1.10.0
<TextInputPopup>:
    title: "Popup"
    size_hint: None, None
    size: 400, 400
    auto_dismiss: False

    BoxLayout:
        orientation: "vertical"
        TextInput:
            id: txtinput
            text: root.obj_text
        Button:
            size_hint: 1, 0.2
            text: "Save Changes"
            on_release:
                root.obj.update_changes(txtinput.text)
                root.dismiss()
        Button:
            size_hint: 1, 0.2
            text: "Cancel Changes"
            on_release: root.dismiss()


<SelectableButton>:
    # Draw a background to indicate selection
    canvas.before:
        Color:
            rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
        Rectangle:
            pos: self.pos
            size: self.size

<RV>:
    BoxLayout:
        orientation: "vertical"
        user_no_text_input: no
        user_name_text_input: name

        Label:
            text: "USER NUMBER"
            size_hint: (.5, None)
            height: 30
        TextInput:
            id: no
            size_hint: (.5, None)
            height: 30
            multiline: False
        Label:
            text: "USER NAME"
            size_hint: (.5, None)
            height: 30
        TextInput:
            id: name
            size_hint: (.5, None)
            height: 30
            multiline: False
        Button:
            id: save_btn
            text: "SAVE BUTTON"
            height: 50
            width: 100
            on_press: root.save()

        GridLayout:
            size_hint: 1, None
            size_hint_y: None
            height: 25
            cols: 2

            Label:
                text: "User ID"
            Label:
                text: "User Name"

        BoxLayout:
            RecycleView:
                viewclass: 'SelectableButton'
                data: [{'text': str(x)} for x in root.data_items]
                SelectableRecycleGridLayout:
                    cols: 2
                    default_size: None, dp(26)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'
                    multiselect: True
                    touch_multiselect: True

【问题讨论】:

    标签: python sqlite kivy textinput


    【解决方案1】:

    解决方案

    解决方法如下:详情请参考sn-ps、示例和输出。

    kv 文件

    1. 在类规则&lt;SelectableButton&gt;: 下添加on_press 事件。当 on_press 事件触发时,它将调用根类 RV 中的 root.on_press(self) 方法,并传递 按钮 的实例 self
    2. 将两个 ObjecProperty 挂钩(user_no_text_input: nouser_name_text_input: name)移动到正确的位置,即从BoxLayout: 下方到&lt;RV&gt;:
    3. data: [{'text': str(x)} for x in root.data_items] 替换为data: root.data_items

    片段 - kv

    <SelectableButton>:
        ...
        on_press:
            app.root.on_press(self)
    
    <RV>:
        user_no_text_input: no
        user_name_text_input: name
    
        BoxLayout:
            orientation: "vertical"
    
            Label:
            ...
    
            BoxLayout:
                RecycleView:
                    viewclass: 'SelectableButton'
                    data: root.data_items
                    SelectableRecycleGridLayout:
    

    Python 代码

    在 RV 类中,执行以下操作:

    1. 在 kivy.properties 的导入语句中添加 NumericProperty
    2. user_no_text_input = ObjectProperty(None)user_name_text_input = ObjectProperty(None) 添加ObjecProperty
    3. total_col_headings = NumericProperty(0) 添加NumericProperty
    4. on_press 事件创建一个on_press() 方法。
    5. 创建一个get_table_column_headings() 方法来获取表格中的总列标题。这用于创建列范围。
    6. 增强get_users()方法,创建一个包含db列值、db主键值和db列范围的列表。使用数据库列范围的技巧是使用单击/按下的行中的值填充 TextInput 小部件。

    片段 - Python

    from kivy.properties import BooleanProperty, ListProperty, NumericProperty, ObjectProperty
    ...
    
    class RV(BoxLayout):
        ''' Creates Db conn, table, and saves data, retrives stored data and
        displays in the RecycleView .'''
        data_items = ListProperty([])
        user_no_text_input = ObjectProperty(None)
        user_name_text_input = ObjectProperty(None)
        total_col_headings = NumericProperty(0)
    
        def __init__(self, **kwargs):
            super(RV, self).__init__(**kwargs)
            self.create_table()
            self.get_table_column_headings()
            self.get_users()
    
        def on_press(self, instance):
            columns = self.data_items[instance.index]['range']
            self.user_no_text_input.text = self.data_items[columns[0]]['text']
            self.user_name_text_input.text = self.data_items[columns[1]]['text']
    
        def get_table_column_headings(self):
            connection = sqlite3.connect("demo.db")
    
            with connection:
                # With the with keyword, the Python interpreter automatically releases the resources.
                # It also provides error handling
                cursor = connection.cursor()
                cursor.execute("PRAGMA table_info(Users)")
                col_headings = cursor.fetchall()
                self.total_col_headings = len(col_headings)
        ...
    
        def get_users(self):
            connection = sqlite3.connect("demo.db")
            cursor = connection.cursor()
    
            cursor.execute("SELECT * FROM Users ORDER BY UserID ASC")
            rows = cursor.fetchall()
    
            # create list with db column, db primary key, and db column range
            data = []
            low = 0
            high = self.total_col_headings - 1
    
            for row in rows:
                for col in row:
                    data.append([col, row[0], [low, high]])
                low += self.total_col_headings
                high += self.total_col_headings
    
            # create data_items
            self.data_items = [{'text': str(x[0]), 'Index': str(x[1]), 'range': x[2]} for x in data]
    

    示例

    main.py

    ​​>
    import sqlite3
    from kivy.app import App
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.recycleview.views import RecycleDataViewBehavior
    from kivy.uix.button import Button
    from kivy.properties import BooleanProperty, ListProperty, NumericProperty, ObjectProperty
    from kivy.uix.recyclegridlayout import RecycleGridLayout
    from kivy.uix.behaviors import FocusBehavior
    from kivy.uix.recycleview.layout import LayoutSelectionBehavior
    
    
    class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
                                      RecycleGridLayout):
        ''' Adds selection and focus behaviour to the view. '''
    
    
    class SelectableButton(RecycleDataViewBehavior, Button):
        ''' Add selection support to the Button '''
        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
            return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
    
        def on_touch_down(self, touch):
            ''' Add selection on touch down '''
            if super(SelectableButton, self).on_touch_down(touch):
                return True
            if self.collide_point(*touch.pos) and self.selectable:
                return self.parent.select_with_touch(self.index, touch)
    
        def apply_selection(self, rv, index, is_selected):
            ''' Respond to the selection of items in the view. '''
            self.selected = is_selected
    
    
    class RV(BoxLayout):
        ''' Creates Db conn, table, and saves data, retrives stored data and
        displays in the RecycleView .'''
        data_items = ListProperty([])
        user_no_text_input = ObjectProperty(None)
        user_name_text_input = ObjectProperty(None)
        total_col_headings = NumericProperty(0)
    
        def __init__(self, **kwargs):
            super(RV, self).__init__(**kwargs)
            self.create_table()
            self.get_table_column_headings()
            self.get_users()
    
        def on_press(self, instance):
            columns = self.data_items[instance.index]['range']
            self.user_no_text_input.text = self.data_items[columns[0]]['text']
            self.user_name_text_input.text = self.data_items[columns[1]]['text']
    
        def get_table_column_headings(self):
            connection = sqlite3.connect("demo.db")
    
            with connection:
                # With the with keyword, the Python interpreter automatically releases the resources.
                # It also provides error handling
                cursor = connection.cursor()
                cursor.execute("PRAGMA table_info(Users)")
                col_headings = cursor.fetchall()
                self.total_col_headings = len(col_headings)
    
        def create_table(self):
            connection = sqlite3.connect("demo.db")
            cursor = connection.cursor()
            sql = """CREATE TABLE IF NOT EXISTS Users(
            UserID integer PRIMAY KEY,
            UserName text NOT NULL)"""
            cursor.execute(sql)
            connection.close()
    
        def get_users(self):
            connection = sqlite3.connect("demo.db")
            cursor = connection.cursor()
    
            cursor.execute("SELECT * FROM Users ORDER BY UserID ASC")
            rows = cursor.fetchall()
    
            # create list with db column, db primary key, and db column range
            data = []
            low = 0
            high = self.total_col_headings - 1
    
            for row in rows:
                for col in row:
                    data.append([col, row[0], [low, high]])
                low += self.total_col_headings
                high += self.total_col_headings
    
            # create data_items
            self.data_items = [{'text': str(x[0]), 'Index': str(x[1]), 'range': x[2]} for x in data]
    
        def save(self):
            connection = sqlite3.connect("demo.db")
            cursor = connection.cursor()
    
            UserID = self.user_no_text_input.text
            UserName = self.user_name_text_input.text
    
            try:
                save_sql = "INSERT INTO Users (UserID, UserName) VALUES (?,?)"
                cursor.execute(save_sql, (UserID, UserName))
                connection.commit()
                connection.close()
            except sqlite3.IntegrityError as e:
                print("Error: ", e)
    
    
    class TestApp(App):
        title = "Kivy RecycleView & SQLite3 Demo"
    
        def build(self):
            return RV()
    
    
    if __name__ == "__main__":
        TestApp().run()
    

    test.kv

    #:kivy 1.11.0
    
    <SelectableButton>:
        # Draw a background to indicate selection
        canvas.before:
            Color:
                rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
            Rectangle:
                pos: self.pos
                size: self.size
        on_press:
            app.root.on_press(self)
    
    <RV>:
        user_no_text_input: no
        user_name_text_input: name
    
        BoxLayout:
            orientation: "vertical"
    
            Label:
                text: "USER NUMBER"
                size_hint: (.5, None)
                height: 30
            TextInput:
                id: no
                size_hint: (.5, None)
                height: 30
                multiline: False
            Label:
                text: "USER NAME"
                size_hint: (.5, None)
                height: 30
            TextInput:
                id: name
                size_hint: (.5, None)
                height: 30
                multiline: False
            Button:
                id: save_btn
                text: "SAVE BUTTON"
                height: 50
                width: 100
                on_press: root.save()
    
            GridLayout:
                size_hint: 1, None
                size_hint_y: None
                height: 25
                cols: 2
    
                Label:
                    text: "User ID"
                Label:
                    text: "User Name"
    
            BoxLayout:
                RecycleView:
                    viewclass: 'SelectableButton'
                    data: root.data_items
                    SelectableRecycleGridLayout:
                        cols: 2
                        default_size: None, dp(26)
                        default_size_hint: 1, None
                        size_hint_y: None
                        height: self.minimum_height
                        orientation: 'vertical'
                        multiselect: True
                        touch_multiselect: True
    

    输出

    【讨论】:

    • 能否请您详细说明这一行:self.data_items = [{'text': str(x[0]), 'Index': str(x[1]), ' range': x[2]} for x in data],这个嵌套字典适用于检索和显示 2 个数据字段,但不能用于更多字段。因此想了解它是如何工作的!
    • self.data_items 中的每个字典都用于每一列。键值对 'Index': str(xyz) 实际上是主键。当 DB 表的字段超过 2 个时,请执行以下操作: (1) 更改 SELECT 语句以仅检索将显示的感兴趣的列。 (2) 更改lowhigh 的值,尤其是lowhigh 之间的increments/steps。在 Kivy 应用中添加print() 函数以显示rowsdataself.data_items 以详细显示其工作原理。
    猜你喜欢
    • 2020-09-18
    • 1970-01-01
    • 2017-06-17
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 2019-02-21
    • 1970-01-01
    相关资源
    最近更新 更多