如何从sqlite Db检索数据并将其设置为kivy TextInput字段?

时间:2018-07-20 09:39:50

标签: python sqlite kivy textinput

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

在按下按钮时,我需要将来自db的相应数据设置为相应的textinput字段。即在 按下用户ID为1的按钮,文本输入字段应显示该行中的用户ID和用户名的值。

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, 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

1 个答案:

答案 0 :(得分:0)

解决方案

解决方案如下:有关详细信息,请参见代码段,示例和输出。

kv文件

  1. 在类规则on_press下添加<SelectableButton>:事件。当on_press事件发生时,它将在根类 RV 中调用root.on_press(self)方法,并传递 button self的实例。
  2. 将两个ObjecProperty挂钩(user_no_text_input: nouser_name_text_input: name)移到正确的位置,即从BoxLayout:下移到<RV>:
  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. 添加NumericProperty以导入kivy.properties的语句。
  2. user_no_text_input = ObjectProperty(None)user_name_text_input = ObjectProperty(None)
  3. 添加 ObjecProperty
  4. total_col_headings = NumericProperty(0)添加 NumericProperty
  5. on_press()事件创建on_press方法。
  6. 创建一个get_table_column_headings()方法以获取表中的总列标题。这用于创建列范围。
  7. 增强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

输出

Img01 Img02