鱼C论坛

 找回密码
 立即注册
查看: 16597|回复: 36

[技术交流] [Python Programming for Android/IOS] Kivy简明教程

[复制链接]
发表于 2018-2-27 20:30:42 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 jerryxjr1220 于 2018-3-1 13:38 编辑

随着互联网移动平台的兴起,越来越多的程序都逐渐转向移动端app。作为Python开发者,当然也毫不例外地投身移动app的大潮中。

作为Python中Android/IOS开发的利器,Kivy以其Google官方语言及其跨平台的优势,很快占据了Python Programming for Android/IOS的核心地位。

下面就借这篇帖子,普及一下在Python中用Kivy开发移动端App的基本知识。

Kivy总体思想是:kv代码管界面,python代码管逻辑。

在开始教程之前,推荐有英语能力的同学看一下kivy的原版使用说明,这样对于许多我解释不到位的问题都可以在里面找到答案。
下载后请改名xxx.7z.001和xxx.7z.002,然后解压缩:
kivy使用手册(原版).001.zip (2 MB, 下载次数: 122, 售价: 2 鱼币)
kivy使用手册(原版).002.zip (1.95 MB, 下载次数: 87, 售价: 2 鱼币)

第一篇:Kivy的安装

1. 最简单且有效的安装方式当然是用pip
pip install kivy
如果在python环境下运行“impot kivy”,显示:
import kivy
[INFO   ] [Logger      ] Record log in C:\Users\Administrator\.kivy\logs\kivy_18-02-27_10.txt
[INFO   ] [Kivy        ] v1.10.0
[INFO   ] [Python      ] v3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)]
那么恭喜你,你的kivy已经成功被导入了。

2. 但是,还别高兴太早。
kivy在创建窗口的时候还需要导入依赖库,如果缺少的话,会提示“unable to get a window,abort”.
这时候,你就需要pip另外2个依赖库:
pip install kivy.deps.sdl2
pip install docutils pygments pypiwin32 kivy.deps.sdl2 kivy.deps.glew
如果运行下列代码不报错并且成功显示黑窗口的话,那么才是真正完成了kivy的安装。
from kivy.app import App
class TestApp(App):
    def build(self):
        return
TestApp().run()
上面这段代码是最简单的kivy程序,几行代码就构建了一个kivy窗体框架。可以按F1查看窗体配置。

评分

参与人数 6荣誉 +23 鱼币 +23 贡献 +26 收起 理由
小伤口 + 2 + 2 感谢楼主无私奉献!
一个账号 + 1 + 1 鱼C有你更精彩^_^
zltzlt + 10
linglong974 + 5 + 5 + 3 感谢楼主无私奉献!
JAY饭 + 5 + 5 + 3 感谢楼主无私奉献!
小甲鱼 + 10 + 10 + 10 热爱鱼C^_^

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-2-27 20:41:39 | 显示全部楼层
第二篇:Kivy app的“Hello World”

凡是计算机编程语音,最基础的第一个小程序一般都是“Hello World”,Kivy也不例外,我们就称作“Hello Kivy”吧。
from kivy.app import App
from kivy.uix.label import Label #导入标签类

class TestApp(App):
    def build(self):
        return Label(text='Hello Kivy!') #创建并返回标签,定义标签文本“Hello Kivy!”
    
TestApp().run()

运行这个程序,这时,你应该可以在屏幕中间看到“Hello Kivy!”的字样。你的第一个kivy程序已经完成了,是不是很简单?

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-27 20:53:32 | 显示全部楼层
本帖最后由 jerryxjr1220 于 2018-2-27 21:05 编辑

第三篇:Kivy的布局,添加更多控件

单调的“Hello Kivy”是不是看不出任何特点?

Kivy之所以那么强大,就是因为它对于移动端多点触控的良好支持。

那么在上次程序的基础上,让我们加点酷炫的特效上去吧。
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout #导入浮动布局
from kivy.uix.scatter import Scatter #导入缩放功能

class TestApp(App):
    def build(self):
        l = Label(text='Hello Kivy!', font_size = 50)
        f = FloatLayout()
        s = Scatter()
        
        f.add_widget(s) #布局上加载缩放功能
        s.add_widget(l) #缩放控件上加载标签控件
        
        return f
    
TestApp().run()

这样运行app后,就可以拖动文字,并且按下鼠标右键,可以模拟多点触控进行缩放哦。
kivy.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-27 21:29:57 | 显示全部楼层
本帖最后由 jerryxjr1220 于 2018-2-27 23:18 编辑

第四篇:编译kivy程序

这部分可以参考buildozer:(目前仅支持Lunix和Mac OS系统,暂不支持Windows)

http://buildozer.readthedocs.io/en/latest/

用pip install buildozer也可以很方便的安装。

我着重讲解Kivy的编程,这部分就略过了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-28 00:16:50 | 显示全部楼层
第五篇:kivy language

kivy有自己的一套编程语言,当然其可以完美融入python程序中。

如何做到这一点呢?我们就那上文的程序做例子,用kivy语言改进一下。

首先,我们新建一个kv文件,文件名必须与程序名“xxxApp”中的“xxx”相同,在上例中就是Test.kv文件。
<MyFloatLayout>:
    FloatLayout:
        Scatter:
            Label:
                text: 'Hello Kivy!'
                font_size: 50
kv文件的逻辑很简单,第一行<>中的内容就是我们自定义的类名。

后面依次为不同层次的布局及属性,若同级则相同缩进,若下级则增加缩进。

同时,在原来的py文件中,做相应的修改。
from kivy.app import App
from kivy.uix.label import Label #导入相应控件,不能缺少
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scatter import Scatter

class MyFloatLayout(FloatLayout): #必须在py文件中定义kv文件中的类,内容可以不写,用pass省略。
    pass

class TestApp(App):
    def build(self):
        return MyFloatLayout() #直接返回kv文件中定义的类

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

整个程序运行的效果和上例是一模一样的,只是这里我们使用了kivy语言来写,逻辑上来看更直观一些。

更简略的写法,我们可以把kv文件直接写在py文件中,并用kivy.lang中的Builder的load_string函数加载进来。
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scatter import Scatter
from kivy.lang import Builder

Builder.load_string('''
<MyFloatLayout>:
    FloatLayout:
        Scatter:
            Label:
                text: 'Hello Kivy!'
                font_size: 50
''')

class MyFloatLayout(FloatLayout):
    pass

class TestApp(App):
    def build(self):
        return MyFloatLayout()

if __name__ == '__main__':
    TestApp().run()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-28 00:29:33 | 显示全部楼层
本帖最后由 jerryxjr1220 于 2018-3-1 16:18 编辑

第六篇:kivy的属性

在这篇中,我们创建一个更复杂的程序,并介绍一下如何在kivy中调用各种控件。

看代码:
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput #导入文本输入框
from kivy.uix.boxlayout import BoxLayout #导入盒子布局
from kivy.lang import Builder

Builder.load_string('''
<ScatterTextWidget>:
    orientation: 'vertical' #定义了布局方向,垂直布局
    TextInput:
        id: my_textinput #设定id为my_textinput,方便后续调用
        font_size:150
        size_hint_y: None
        height: 200
        text: 'default' #文本内容
    FloatLayout:
        Scatter:
            Label:
                text: my_textinput.text #直接调用my_textinput的text属性
                font_size: 150
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 150
        Label:
            text: my_textinput.text[:3][::-1] #可以对my_textinput的text切片操作,完全符合python语法规则
            font_size: 100
        Label:
            text: my_textinput.text[-3:][::-1]
            font_size: 100
''')
class ScatterTextWidget(BoxLayout):
    pass

class TutorialApp(App):
    def build(self):
        return ScatterTextWidget()

TutorialApp().run()

下面,我们来为这个程序增加一些功能,比如在输入文本时改变Label的颜色。
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
import random

Builder.load_string('''
<ScatterTextWidget>:
    orientation: 'vertical'
    TextInput:
        id: my_textinput
        font_size:150
        size_hint_y: None
        height: 200
        text: 'default'
    FloatLayout:
        Scatter:
            Label:
                id: my_label #设定Label的名称
                text: my_textinput.text
                font_size: 150
                on_text: root.color_change() #当文本变化时,调用函数。root表示最上层的类,本例中就是指ScatterTextWidget类。
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 150
        Label:
            text: my_textinput.text[:3][::-1]
            font_size: 100
        Label:
            text: my_textinput.text[-3:][::-1]
            font_size: 100
''')

class ScatterTextWidget(BoxLayout):
    def color_change(self): #定义函数
        label = self.ids['my_label'] #根据id找到label
        label.color = [random.random() for _ in range(3)] + [1]

class TutorialApp(App):
    def build(self):
        return ScatterTextWidget()

TutorialApp().run()
无标题.png

对于布局以及各种控件的调用,kivy的样例程序提供了一个很好的demo。
kivycatalog.zip (15.35 KB, 下载次数: 8, 售价: 2 鱼币)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-28 03:05:47 | 显示全部楼层
Hello Kivy
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 3 反对 0

使用道具 举报

发表于 2018-2-28 10:09:54 | 显示全部楼层
本帖最后由 JAY饭 于 2018-2-28 10:54 编辑

不支持windows吗...
我在命令行中输入下面这个却始终提示找不到对应的东西,其余的安装都可以,而且,我查了一下,我以下的模块之前都有安装
pypiwin32 , kivy.deps.sdl2, kivy.deps.glew都提示已经在安装目录
不懂这个python -m的意思
python -m pip install docutils pygments pypiwin32 kivy.deps.sdl2 kivy.deps.glew

然后执行hello world,答应出来的是,无法创建窗口,程序终止
No window is created. Terminating application run.
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-28 11:07:25 | 显示全部楼层
已经解决了,你写的第一个测试程序好像是因为没有返回项,所以创建不了窗口,而下面的代码都能创建
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-28 15:10:44 | 显示全部楼层
JAY饭 发表于 2018-2-28 11:07
已经解决了,你写的第一个测试程序好像是因为没有返回项,所以创建不了窗口,而下面的代码都能创建

第一个程序没有创建任何控件,只是测试一下安装kivy是否成功,如果不报错就是安装成功了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-28 16:55:24 | 显示全部楼层
jerryxjr1220 发表于 2018-2-28 15:10
第一个程序没有创建任何控件,只是测试一下安装kivy是否成功,如果不报错就是安装成功了。

。。。那说明我还是没安装成功
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-28 23:36:29 | 显示全部楼层
继续
第七篇:kivy画板控件

和其他GUI开发程序一样,kivy也有自己的画板控件。

首先,让我们导入画图的几种形状,比如矩形、椭圆、直线等,再导入画板颜色控件。
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.vertex_instructions import Rectangle, Ellipse, Line #导入矩形、椭圆、直线
from kivy.graphics.context_instructions import Color #导入颜色控件
from kivy.lang import Builder
import random

Builder.load_string('''
<ScatterTextWidget>:
    orientation: 'vertical'
    canvas: #创建画板
        Color:
            rgba: 0,0,1,1 #设置rgba色值
        Ellipse: #椭圆
            pos: 0, 400
            size: 200, 100
        Line: #直线段
            points: [0,0,500,600,400,300]
            close: True #直线闭合
            width: 3 #线条宽度
    TextInput:
        id: my_textinput
        font_size:150
        size_hint_y: None
        height: 200
        text: 'default'
    FloatLayout:
        Scatter:
            Label:
                id: my_label
                text: my_textinput.text
                font_size: 150
                on_text: root.color_change()
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 150
        Label:
            text: my_textinput.text[:3][::-1]
            font_size: 100
        Label:
            text: my_textinput.text[-3:][::-1]
            font_size: 100
''')
class ScatterTextWidget(BoxLayout):
    def color_change(self):
        label = self.ids['my_label']
        label.color = [random.random() for _ in range(3)] + [1]

class TutorialApp(App):
    def build(self):
        return ScatterTextWidget()

TutorialApp().run()
无标题2.png

同样,也可以画矩形。
无标题.png

还可以把矩形的位置设置成控件大小,这样就等于是全屏显示了,哪怕拖动界面都可以自动放缩。
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.vertex_instructions import Rectangle, Ellipse, Line
from kivy.graphics.context_instructions import Color
from kivy.lang import Builder
import random

Builder.load_string('''
<ScatterTextWidget>:
    orientation: 'vertical'
    canvas:
        Color:
            rgba: 0,0,1,1
        Rectangle:
            pos: self.pos
            size: self.size
    TextInput:
        id: my_textinput
        font_size:150
        size_hint_y: None
        height: 200
        text: 'default'
    FloatLayout:
        Scatter:
            Label:
                id: my_label
                text: my_textinput.text
                font_size: 150
                on_text: root.color_change()
    BoxLayout:
        orientation: 'horizontal'
        size_hint_y: None
        height: 150
        Label:
            text: my_textinput.text[:3][::-1]
            font_size: 100
        Label:
            text: my_textinput.text[-3:][::-1]
            font_size: 100
''')
class ScatterTextWidget(BoxLayout):
    def color_change(self):
        label = self.ids['my_label']
        label.color = [random.random() for _ in range(3)] + [1]

class TutorialApp(App):
    def build(self):
        return ScatterTextWidget()

TutorialApp().run()
无标题1.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-3-1 20:56:01 | 显示全部楼层
第八篇:用kivy编写倒计时小程序

现在我们已经掌握了足够多的控件知识了,让我们来编写一个倒计时小程序吧。

界面很简单:
无标题.png
这里,我们引入了一个新的kivy控件Clock时钟控件,通过clock.schedule_once函数可以按时调用callback函数。

另外,slider是滑块控件,可以设置最大、最小值和当前值。

其他应该都很容易理解了,几十行的代码量,就可以编译成一款app在手机上直接执行,很酷吧?

看源代码吧。
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider
from kivy.graphics import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.clock import Clock
from kivy.lang import Builder

import time

class MyCalculater(BoxLayout):        
    def start(self):
        global stime, dtime
        stime = time.time()
        dtime = self.ids['time_slider'].value
        self.on_update()
        
    def callback(self,*argv):
        global stime, dtime
        if stime+dtime < time.time():
            self.ids['time_slider'].value = 0
            self.ids['time_counter'].text = '00:00.00'
            Clock.unschedule(self.callback)
            return False
        self.ids['time_slider'].value = dtime+stime-time.time()
        self.on_update()
    
    def on_update(self):
        Clock.schedule_once(self.callback,0.2)

class MyClockApp(App):
    def build(self):
        return MyCalculater()
    
stime = 0
dtime = 0

Builder.load_string('''
<MyCalculater>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            canvas.before:
                Color:
                    rgb: 0,0,1
                Rectangle:
                    pos: self.pos
                    size: self.size
            id: time_counter
            text: '00:00.00'
            font_size: 50
        Slider:
            id: time_slider
            min: 0
            max: 90
            value: 0
            on_value: time_counter.text = '%02d:%02d.%02d' % (time_slider.value//60, int(time_slider.value%60), int(time_slider.value%1*100))
        Button:
            text: 'Start!'
            on_press: root.start()
            
''')

if __name__ == '__main__':
    MyClockApp().run()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-3-2 13:04:59 | 显示全部楼层
第九篇:用kivy编写碰撞球小游戏

上一篇中,我们用kivy编写了一个倒计时的小程序。今天我们再增加一点难度,用kivy编写一个碰撞球小游戏,这个小游戏的原理与@小甲鱼的打飞机小游戏原理差不多,都是调用碰撞检查,然后改变位置。

看一下最终的游戏界面:
Untitled.png
它可以编译成apk在手机安装,并支持双人对战(多点触控)

第一步,我们先来创建程序。
# -*- coding: utf-8 -*-
from random import randint
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty #导入kivy属性

Builder.load_string('''
<PongGame>:
        ball: pong_ball #通过id引用PongBall对象
        canvas:
                Rectangle:
                        pos: self.center_x - 5, 0
                        size: 10, self.height
        Label:
                font_size: 70
                center_x: root.width / 4
                top: root.top - 50
                text: '0'

        Label:
                font_size: 70
                center_x: root.width * 3 / 4
                top: root.top - 50
                text: '0'

        PongBall:
                id: pong_ball
                center: self.parent.center

<PongBall>:
        size: 50, 50
        canvas:
                Ellipse:
                        pos: self.pos
                        size: self.size
''')

class PongGame(Widget):
        ball = ObjectProperty(None) #声明ball是Object对象,这点与通常的python动态语言不同,通常python语言是不需要声明的。
        def update(self, dt):
                self.ball.move()
                if self.ball.y<0 or self.ball.top>self.height: #纵向碰壁反弹
                        self.ball.velocity_y *= -1
                if self.ball.x<0 or self.ball.right>self.width: #横向碰壁反弹
                        self.ball.velocity_x *= -1

        def serve_ball(self): #发球
                self.ball.center = self.center
                self.ball.velocity = Vector(4,0).rotate(randint(0,360)) #矢量合成并旋转任意角度

class PongBall(Widget):
        velocity_x = NumericProperty(0)
        velocity_y = NumericProperty(0)

        velocity = ReferenceListProperty(velocity_x,velocity_y)

        def move(self):
                self.pos = Vector(*self.velocity) + self.pos #位置更新

class PongApp(App):
        def build(self):
                game = PongGame()
                game.serve_ball()
                Clock.schedule_interval(game.update, 1.0/60.0)
                return game

if __name__ == '__main__':
        PongApp().run()
如果运行上述程序,我们已经可以看到有一个碰撞球在屏幕上来回滚动了。具体说明见注释。

第二步,为程序添加控制功能。
# -*- coding: utf-8 -*-
from random import randint
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty #导入kivy属性

Builder.load_string('''
<PongGame>:
        ball: pong_ball 
        player1: player_left #设定玩家1
        player2: player_right #设定玩家2
        canvas:
                Rectangle:
                        pos: self.center_x - 5, 0
                        size: 10, self.height
        Label:
                font_size: 70
                center_x: root.width / 4
                top: root.top - 50
                text: str(root.player1.score)

        Label:
                font_size: 70
                center_x: root.width * 3 / 4
                top: root.top - 50
                text: str(root.player2.score)

        PongBall:
                id: pong_ball
                center: self.parent.center

        PongPaddle:
                id: player_left
                x: root.x
                center_y: root.center_y

        PongPaddle:
                id: player_right
                x: root.width - self.width
                center_y: root.center_y

<PongBall>:
        size: 50, 50
        canvas:
                Ellipse:
                        pos: self.pos
                        size: self.size

<PongPaddle>:
        size: 25, 200
        canvas:
                Rectangle:
                        pos: self.pos
                        size: self.size
''')

class PongGame(Widget):
        ball = ObjectProperty(None) #声明ball是Object对象,这点与通常的python动态语言不同,通常python语言是不需要声明的。
        def update(self, dt):
                self.ball.move()

                self.player1.bounce_ball(self.ball)
                self.player2.bounce_ball(self.ball)

                if self.ball.y<0 or self.ball.top>self.height: #纵向碰壁反弹
                        self.ball.velocity_y *= -1

                if self.ball.x<self.x:
                        self.player2.score += 1
                        self.serve_ball(vel=Vector((4,0)))
                if self.ball.x>self.width:
                        self.player1.score += 1
                        self.serve_ball(vel=Vector((-4,0)))

        def on_touch_move(self, touch): #设定滑动碰撞杆时的动作
                if touch.x < self.width / 3:
                        self.player1.center_y = touch.y
                if touch.x > self.width * 2 / 3:
                        self.player2.center_y = touch.y

        def serve_ball(self, vel=Vector((4,0))): #发球
                self.ball.center = self.center
                self.ball.velocity = vel.rotate(randint(0,360)) #矢量合成并旋转任意角度

class PongBall(Widget):
        velocity_x = NumericProperty(0)
        velocity_y = NumericProperty(0)

        velocity = ReferenceListProperty(velocity_x,velocity_y)

        def move(self):
                self.pos = Vector(*self.velocity) + self.pos #位置更新

class PongPaddle(Widget):
        score = NumericProperty(0)
        def bounce_ball(self, ball):
                if self.collide_widget(ball): #碰撞判断
                        speedup = 1.1
                        vx, vy = ball.velocity
                        offset = (ball.center_y-self.center_y) / (self.height / 2)
                        bounced = Vector(-1*vx,vy) #Vector是kivy的一个矢量支持类,可以对矢量进行计算和旋转。
                        vel = bounced * speedup
                        ball.velocity = vel.x, vel.y+offset

class PongApp(App):
        def build(self):
                game = PongGame()
                game.serve_ball()
                Clock.schedule_interval(game.update, 1.0/60.0)
                return game

if __name__ == '__main__':
        PongApp().run()
再次运行程序,这样一个碰撞球小游戏已经可以正常运行了,拖动或点击左右两侧的滑杆可以控制滑杆的移动。

最后,我们再对这个小程序进行优化,添加背景图像、改变图形颜色和字体颜色等。
最终代码:
# -*- coding: utf-8 -*-
from random import randint
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty #导入kivy属性
from kivy.uix.image import Image
from kivy.graphics import Color

Builder.load_string('''
<PongGame>:
        ball: pong_ball #通过id引用PongBall对象
        player1: player_left
        player2: player_right
        canvas.before:
                BorderImage:
                        source: 'images.jpg'
                        pos: self.pos
                        size: self.size
        canvas.after:
                Color: 
                        rgba: 1,1,1,1
                Rectangle:
                        pos: self.center_x - 5, 0
                        size: 10, self.height        
        Label:
                font_size: 70
                color: 1,0,0,1
                center_x: root.width / 4
                top: root.top - 50
                text: str(root.player1.score)

        Label:
                font_size: 70
                color: 1,0,0,1
                center_x: root.width * 3 / 4
                top: root.top - 50
                text: str(root.player2.score)

        PongBall:
                id: pong_ball
                center: self.parent.center

        PongPaddle:
                id: player_left
                x: root.x
                center_y: root.center_y

        PongPaddle:
                id: player_right
                x: root.width - self.width
                center_y: root.center_y

<PongBall>:
        size: 50, 50
        canvas:
                Color: 
                        rgba: 0,0,1,1
                Ellipse:
                        pos: self.pos
                        size: self.size

<PongPaddle>:
        size: 25, 200
        canvas:
                Color: 
                        rgba: 1,1,0,1
                Rectangle:
                        pos: self.pos
                        size: self.size
''')

class PongGame(Widget):
        ball = ObjectProperty(None) #声明ball是Object对象,这点与通常的python动态语言不同,通常python语言是不需要声明的。
        def update(self, dt):
                self.ball.move()

                self.player1.bounce_ball(self.ball)
                self.player2.bounce_ball(self.ball)

                if self.ball.y<0 or self.ball.top>self.height: #纵向碰壁反弹
                        self.ball.velocity_y *= -1

                if self.ball.x<self.x:
                        self.player2.score += 1
                        self.serve_ball(vel=Vector((4,0)))
                if self.ball.x>self.width:
                        self.player1.score += 1
                        self.serve_ball(vel=Vector((-4,0)))

        def on_touch_move(self, touch):
                if touch.x < self.width / 3:
                        self.player1.center_y = touch.y
                if touch.x > self.width * 2 / 3:
                        self.player2.center_y = touch.y

        def serve_ball(self, vel=Vector((4,0))): #发球
                self.ball.center = self.center
                self.ball.velocity = vel.rotate(randint(0,360)) #矢量合成并旋转任意角度

class PongBall(Widget):
        velocity_x = NumericProperty(0)
        velocity_y = NumericProperty(0)

        velocity = ReferenceListProperty(velocity_x,velocity_y)

        def move(self):
                self.pos = Vector(*self.velocity) + self.pos #位置更新

class PongPaddle(Widget):
        score = NumericProperty(0)
        def bounce_ball(self, ball):
                if self.collide_widget(ball):
                        speedup = 1.1
                        vx, vy = ball.velocity
                        offset = (ball.center_y-self.center_y) / (self.height / 2)
                        bounced = Vector(-1*vx,vy)
                        vel = bounced * speedup
                        ball.velocity = Vector(vel.x, vel.y+offset).rotate(randint(-90,90))

class PongApp(App):
        def build(self):
                game = PongGame()
                game.serve_ball()
                Clock.schedule_interval(game.update, 1.0/60.0)
                return game

if __name__ == '__main__':
        PongApp().run()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-3-5 19:38:22 | 显示全部楼层
不错,多谢分享......
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-3-5 19:45:40 | 显示全部楼层
实话说还是只有论坛里的写这样的教程才最接地气, 人性化,
学起来轻松..
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2018-3-5 20:09:35 | 显示全部楼层
LargeCat 发表于 2018-3-5 19:45
实话说还是只有论坛里的写这样的教程才最接地气, 人性化,
学起来轻松..

可惜真正想学习的人还是少数,多数人只是觉得游戏好玩,来看一下,装一下,玩一下就完了。
其实什么都没学到。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2018-3-10 12:13:45 | 显示全部楼层
jerryxjr1220 发表于 2018-3-5 20:09
可惜真正想学习的人还是少数,多数人只是觉得游戏好玩,来看一下,装一下,玩一下就完了。
其实什么都没 ...

说真的,一般人第一感觉是很厉害,然后再一看,看不懂,看不下去了,
你之前出的那个小练习,我做了七八十道了,最开始的时候做不出来看
答案,即使没有涉及到复杂的模块,有些逻辑转换太快还是看不懂。
即使到了现在,很多题目我都是自己去写,而没有看答案,其实除了想锻
炼自己之外有一部分原因是真的觉得看了答案也看不懂。
      怎么说呢,有的东西你觉得很好理解,但是对于一般的人来说,比如我
这么笨的,是真的要绕很久才懂,更何况那些其实学习欲望并不特
别强的人,几乎就是走过场,比如这个帖子中的模块导入,如果仅仅照葫芦
画瓢,你导入什么我们就导入什么。那真的什么都学不到,因为真的没法理
解这些模块,方法到底是干嘛的。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-3-10 12:17:15 | 显示全部楼层
老实说,都快半年了,我现在都不会看源代码,好气啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-3-10 13:52:25 From FishC Mobile | 显示全部楼层
JAY饭 发表于 2018-3-10 12:13
说真的,一般人第一感觉是很厉害,然后再一看,看不懂,看不下去了,
你之前出的那个小练习,我做了七八 ...

学语言要沉下心来学。
我接触python也只有短短2年不到的时间,以前也只有VB一些基础知识。而且我现在的主业和编程也完全没关系,纯粹是爱好。
真的有兴趣,就要沉浸去学,理解编程的逻辑和算法原理,这样才能学好。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-22 08:07

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表