如何使用Django构建一个Kivy应用程序

389 阅读9分钟

使用Django构建一个Kivy应用程序

Kivy是一个流行的库,用于开发富含媒体、支持多点触摸的应用程序,可以在所有主要平台上运行。然而,可能会有这样的情况,需要你的应用程序获取并操作由一个网站上的Web服务器提供的数据。

在这篇文章中,我们将学习如何创建一个Kivy应用程序,以及如何使用requests 库向服务器发出请求。

我们将使用Django来开发持有我们想要操作的数据的服务器。

前提条件

要想跟着学习,重要的是。

  • 你已经安装了Djangodjangorestframework 框架。
  • 你熟悉使用Django REST框架构建API。
  • 你已经安装了kivy 库。如果你没有安装它,你可以运行pip install kivy
  • Python的基本知识很重要。了解面向对象的编程会有帮助。

主要收获

  • 提高你的Python技能。
  • 学习如何使用kivy 库构建应用程序。
  • 学习如何为你的kivy应用程序使用Django。

开始学习

我们将创建一个简单的待办事项应用程序,允许人们查看可用的任务,提供一个添加新任务的选项。

我们将首先用djangorestframework 创建任务API,然后用kivy 创建我们的应用程序。

我们将使用requests 库来向我们的Django服务器发出请求。

创建任务API

在你选择的文件夹中,通过运行创建一个新项目。

django-admin startproject TodoAPI

TodoAPI 项目中,创建一个名为tasks 的新应用,它将处理任务的创建。

运行下面的命令来创建该应用程序。

python3 manage.py startapp tasks

你的项目结构应该是这样的。

.
└── TodoAPI
    ├── manage.py
    ├── tasks
    │   ├── admin.py
    │   ├── apps.py
    │   ├── __init__.py
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    └── TodoAPI
        ├── asgi.py
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

我们需要注册我们的tasks 应用程序和rest_framework 来使用该应用程序。

INSTALLED APPS 下编辑settings.py 文件,如下所示。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'tasks',
    'rest_framework'

让我们继续并创建我们的任务模型。编辑models.py 文件,如下所示。

from django.db import models

# Create your models here.
class Task(models.Model):
    name = models.CharField(max_length=200)
    def __str__(self):
        return self.name

让我们创建一个serializer.py 文件,处理任务实例的序列化和去序列化。

序列化是以一种可以被不同应用程序以统一格式访问的方式保存对象的过程。

去序列化是序列化的反面。

serializer.py 文件中加入以下代码。

from rest_framework import serializers
from .models import *

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        field = '__all__'

创建视图

我们将创建一些视图来渲染数据到网页上。

视图是处理网络请求并返回网络响应的python函数。有不同的方法来创建视图,我们将使用基于函数的视图。

基于函数的视图是将Django中的视图写成python函数。

编辑views.py 文件,使之与以下内容相匹配。

from django.shortcuts import render
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from .serializers import *
from .models import *

# Create your views here.
@api_view(['GET'])
def all_tasks(request):
    tasks = Task.objects.all()
    serializer = TaskSerializer(tasks, many=True)
    return Response(serializer.data, status=status.HTTP_200_OK)

@api_view(['POST'])
def create_task(request):
    data = request.data
    serializer = TaskSerializer(data=data)
    if serializer.is_valid():
        return Response(serializer.data, status=status.HTTP_200_OK)

我们首先定义了一个函数all_tasks ,它将返回一个包含我们所有任务的响应。该函数通过tasks = Task.objects.all() 行获得所有的任务。

然后,数据在后续的行中被序列化,该函数返回响应。第@api_view(['GET']) 行是一个装饰器,它将GET 作为函数应该响应的HTTP方法。

同样的概念也适用于第二个函数,只是这一次它取了一个POST HTTP方法。

路由视图

然后我们创建一个urls.py 文件,用于路由我们的视图。

创建该文件并添加以下代码。

from django.urls import path
from . import views

urlpatterns = [
    path('', views.all_tasks, name='all_tasks'),
    path('create', views.create_task, name='create_task')
]

上面的映射意味着请求将首先被这个文件处理,然后被路由到一个相应的视图函数。

例如,当我们访问http://127.0.0.1:8000/createcreate_task 函数被调用和实现。

我们必须为我们的tasks 应用程序创建一个路由。

这样一来,路由首先发生在TodoAPI/urls.py 文件上,然后是tasks/urls.py

因此,像这样在TodoAPI 中配置我们的urls.py 文件。

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('tasks.urls'))
]

我们现在可以运行。

python3 manage.py makemigrations

python3 manage.py migrate

当我们运行时。

python3 manage.py runserver

我们应该有一个与此类似的页面。

Tasks Home Page

当你按如下方式创建一个任务实例时。

Create Task Page

你应该能看到类似这样的东西。

Task Created

我们的Django API已经工作了!我们现在可以继续创建kivy 应用程序。

创建Kivy应用程序

让我们在选定的文件夹中创建一个main.py 文件。

这里,我们将使用与我们的TodoAPI 项目文件夹相同的目录。

我们将从main.py 文件中的这几行代码开始。TodoApp 是我们应用程序的主要入口点,每一次执行都从这里开始。

[YOUR-FOLDER]/main.py

from kivy.app import App

class TodoApp(App):
    pass

if __name__ == '__main__':
    TodoApp().run()

当你运行该文件时,你应该得到以下结果。

Original Kivy App

我们现在要用以下几行代码来替换main.py 文件。

from kivy.app import App

from kivy.uix.screenmanager import ScreenManager
from kivy.uix.boxlayout import BoxLayout

#menu
class Menu(BoxLayout):
    pass
#Screen Management
class ScreenManagement(ScreenManager):
    pass
#app class
class TodoApp(App):
    pass

if __name__ == '__main__':
    TodoApp().run()

我们还需要创建一个kv 文件,其中涉及到用户界面的所有细节。

创建一个todo.kv 文件,并确保它与main.py 文件在同一个文件夹中。该文件应该类似于这个文件。

BoxLayout:
    orientation: 'vertical'
    Menu:
        size_hint_y: .1
        manager: screen_manager
    ScreenManagement:
        size_hint_y: .9
        id: screen_manager
<Menu>:
    orientation: "vertical"
    ActionBar:
        top: 0
        ActionView:
            ActionPrevious:
            ActionButton:
                text: 'Home'
            ActionButton:
                text: 'Add New'

这就是我们的文件所发生的事情。

  • 我们首先创建一个Menu 类,它继承自BoxLayout 类。这个菜单将包含我们将用于探索应用程序的按钮。
  • 然后我们继续创建ScreenManger 类,该类管理应用程序中显示的内容。
  • todo.kv 文件中,我们声明一个BoxLayout 作为应用程序显示的主界面。
  • 然后,我们给这个菜单一个由size_hint_y: .1 设置的底部位置。
  • 我们还声明它是由ScreenManager 管理的。我们通过声明manager: screen_manager 来做到这一点。
  • 通过将ScreenManagerid 属性设置为screen_manager ,菜单的位置现在是在顶部。
  • 我们声明我们的Menu 类的属性。
  • 该类将有一个动作栏,它将包含两个按钮,即Home 动作按钮和Add New 动作按钮。

你的应用程序应该类似于下面的例子。

Creating The Action Bar

我们现在需要在创建一个任务时过渡到不同的屏幕。

因此,我们需要声明两个屏幕,以便一个显示任务,另一个用于添加新任务。这两个屏幕将由ScreenManager 类来管理。

编辑你的main.py 文件,使其看起来像这样。

from kivy.app import App

from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout

#menu
class Menu(BoxLayout):
    pass
#screens
class HomeScreen(Screen):
    pass
class AddScreen(Screen):
    pass
#Screen Management
class ScreenManagement(ScreenManager):
    pass
#app class
class TodoApp(App):
    pass

if __name__ == '__main__':
    TodoApp().run()

同时,修改todo.kv 文件,使其如下所示。

BoxLayout:
    orientation: 'vertical'
    Menu:
        size_hint_y: .1
        manager: screen_manager
    ScreenManagement:
        size_hint_y: .9
        id: screen_manager
<Menu>:
    orientation: "vertical"
    ActionBar:
        top: 0
        ActionView:
            ActionPrevious:
            ActionButton:
                text: 'Home'
                on_press: root.manager.current = 'screen_home'

            ActionButton:
                text: 'Add New'
                on_press: root.manager.current = 'screen_add'
<ScreenManagement>:
    id: screen_manager
    HomeScreen:
        name: 'screen_home'
        manager: 'screen_manager'
    AddScreen:
        name: 'screen_add'
        manager: 'screen_manager'
<HomeScreen>:
    Label:
        text: "Home"
<AddScreen>:
    Label:
        text: "Add to list..."

在上面的代码中。

  • 我们声明了HomeScreen 类,并给它一个Label 实例,文本为Home
  • 我们也为AddScreen 做了同样的事情,标签文本为Add to list...
  • 我们把这两个屏幕作为ScreenManagement 的子屏幕。
  • 我们还通过启用对屏幕的访问来指定动作按钮的功能。我们通过定义on_press: root.manager.current = 'screen_home'on_press: root.manager.current = 'screen_add' 来做到这一点。

现在你应该能够探索这两个屏幕,并看到HomeScreen 中的 "Home "文本,以及 中的 "Add to list..."。AddScreen

使用Django的API

让我们创建一个类,它有一个向服务器发送可用任务请求的函数。

class Menu(BoxLayout): 声明下,添加以下几行代码。

#recycle view for home screen
class MyRecycleView(RecycleView):
    def __init__(self, **kwargs):
        super(MyRecycleView, self).__init__(**kwargs)
        self.load_data()
        Clock.schedule_interval(self.load_data, 1)
    def load_data(self, *args):
        store = requests.get('http://127.0.0.1:8000/').json()

        list_data = []
        for item in store:
            list_data.append({'text': item['name']})

        self.data = list_data

你需要在文件的顶部进行这些导入。

import requests
from kivy.clock import Clock
from kivy.uix.recycleview import RecycleView

修改你的todo.kv 文件,看起来像这样。

BoxLayout:
    orientation: 'vertical'
    Menu:
        size_hint_y: .1
        manager: screen_manager
    ScreenManagement:
        size_hint_y: .9
        id: screen_manager
<Menu>:
    orientation: "vertical"
    ActionBar:
        top: 0
        ActionView:
            ActionPrevious:
            ActionButton:
                text: 'Home'
                on_press: root.manager.current = 'screen_home'

            ActionButton:
                text: 'Add New'
                on_press: root.manager.current = 'screen_add'
<ScreenManagement>:
    id: screen_manager
    HomeScreen:
        name: 'screen_home'
        manager: 'screen_manager'
    AddScreen:
        name: 'screen_add'
        manager: 'screen_manager'
<HomeScreen>:
    BoxLayout:
        orientation: "vertical"
        MyRecycleView:
<MyRecycleView>:
    viewclass: 'Label'
    RecycleBoxLayout:
        color: 1,1,1,1
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
<AddScreen>:
    Label:
        text: "Add to list..."
  • MyRecycleView 类的初始化是有一个函数load_data ,它使用requests 库向服务器发出请求。
  • 数据被附加到一个列表中,该列表包含有关键的任务字典text 。该函数将该列表作为一个data 变量返回。
  • 通过设置时钟间隔为1,该函数每秒钟被调用一次。

todo.kv 文件中,我们用一个包含我们的MyRecycleView 类的BoxLayout 替换我们的HomeScreen 的内容。

然后我们将我们的MyRecycleView 的属性声明为包含一个标签列表的BoxLayout ,每个标签都有从我们的服务器收到的文本。

现在你应该能够看到我们在前面基于网络的界面中创建的code 任务。

Displaying Tasks from server

现在让我们来处理创建一个新任务的功能。

我们首先创建一个表单,向我们的服务器提交创建请求。

然后我们把这个表单添加到我们的AddScreen 屏幕上。这将使我们的应用程序变得完整。

让我们把我们的main.py 文件修改成这样。

import requests
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView

from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout

#menu
from kivy.uix.widget import Widget

class Menu(BoxLayout):
    pass
#recycle view for home screen
class MyRecycleView(RecycleView):
    def __init__(self, **kwargs):
        super(MyRecycleView, self).__init__(**kwargs)
        self.load_data()
        Clock.schedule_interval(self.load_data, 1)
    def load_data(self, *args):
        store = requests.get('http://127.0.0.1:8000/').json()

        list_data = []
        for item in store:
            list_data.append({'text': item['name']})
        self.data = list_data
#screens
class HomeScreen(Screen):
    pass
class AddNewForm(Widget):
    text_input = ObjectProperty(None)

    input = StringProperty('')

    def submit_input(self):
        self.input = self.text_input.text
        post = requests.post('http://127.0.0.1:8000/create', json={'name': self.input})

        self.input = ''
class AddScreen(Screen):
    def __init__(self, **kwargs):
        super(AddScreen, self).__init__(**kwargs)
        self.box = BoxLayout()
        self.box.orientation = "vertical"
        self.box.add_widget(Label(text="Add To List...", color="blue",pos_hint={"top": 1}))
        self.addNewForm = AddNewForm()
        self.box.add_widget(self.addNewForm)
        self.add_widget(self.box)
#Screen Management
class ScreenManagement(ScreenManager):
    pass
#app class
class TodoApp(App):
    pass

if __name__ == '__main__':
    TodoApp().run()

让我们也将todo.kv 修改为。

BoxLayout:
    orientation: 'vertical'
    Menu:
        size_hint_y: .1
        manager: screen_manager
    ScreenManagement:
        size_hint_y: .9
        id: screen_manager
<Menu>:
    orientation: "vertical"
    ActionBar:
        top: 0
        ActionView:
            ActionPrevious:
            ActionButton:
                text: 'Home'
                on_press: root.manager.current = 'screen_home'

            ActionButton:
                text: 'Add New'
                on_press: root.manager.current = 'screen_add'
<ScreenManagement>:
    id: screen_manager
    HomeScreen:
        name: 'screen_home'
        manager: 'screen_manager'
    AddScreen:
        name: 'screen_add'
        manager: 'screen_manager'
<HomeScreen>:
    BoxLayout:
        orientation: "vertical"
        MyRecycleView:
<MyRecycleView>:
    viewclass: 'Label'
    RecycleBoxLayout:
        color: 1,1,1,1
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'
<AddNewForm>:
    text_input: input
    TextInput:
        id: input
        pos: root.center_x - 220, 300
        size: 400,50
    Button:
        size: 130,40
        pos: root.center_x - 100, 200
        text: 'Submit'
        on_release: root.submit_input()

AddNewForm 包含一个函数submit_input ,它向服务器发出一个POST 请求,将输入的文本作为数据传递。

表单有一个TextInput ,人们可以输入任务,还有一个按钮,释放时调用submit_input 函数。

然后我们声明一个BoxLayout 类,它将包含AddNewForm 和一个标签文本 "添加到列表..."。

当你现在点击Add New ,你现在应该看到下面的情况。

Creating a task

当你创建一个任务,比如说 "完成申请",并点击一次提交按钮,然后点击Home ,你应该看到如下内容。

Task submitted successfully

结论

在本教程中,我们通过创建一个简单的todo kivy应用程序,涵盖了kivy的基础知识,它允许人们查看和添加任务。

我们还看到了如何使用Django作为应用程序的后端,创建一个服务器来保存我们的任务。

有了这些知识,你可以创建类似的应用程序来满足你的不同需求。