问候大家! 在对我的文章的评论中进行了激烈的讨论后,
Kivy成为了跨平台开发第一号的驴框架 ,并且
值得一提的是 ,我们(
Mirimon ,
SeOd )认为,将我们和读者进行比较,对Kivy来说将是一件有趣的事情, Xamarin.Forms和React Native,在它们上编写相同的应用程序,并在Habré
上发表相应的文章,
在GitHub上使用萝卜,并诚实地告诉您实施过程中遇到了哪些困难。 在收集了Telegram并讨论了详细信息之后,我们开始工作。
为了进行比较,我们决定编写一个具有三个屏幕的简单任务计划程序。 以比我们选择的示例更为庞大的示例来简化当今这三个平台的状态,因为每个人都在忙于自己的项目/工作/在家中,这太长了。 尽管我们的应用程序很简单,但是它可以让您清楚地演示每种环境下应用程序开发的原理,使用数据,UI等。
三个框架-一个实验。 Xamarin.Forms。 第二部分三个框架-一个实验。 反应本机。 第三部分本文是该周期的第一篇,因此我想从我们自己绘制的ToR开始,以便得出类似的结果。
选项TK:
- 注释应按项目进行组织。
- 注释可以由其他人添加,因此必须指出注释的作者
- 必须添加/删除/编辑项目内的注释。
- 备注内容应为大小,但不得超过150像素
- 删除笔记应同时通过笔记本身的上下文菜单和滑动
一个示例UI应该看起来像这样:
开始之前,请先对Kivy进行一些帮助:Kivy是一个基于OpenGL ES 2的,用Python / Cython编程语言编写的跨平台图形框架,旨在创建现代用户界面,更侧重于使用触摸设备。 Kivy应用程序可在Linux,OS X,Windows,Android,iOS和Rapberry Pi等平台上运行。 在开发中,您可以访问从请求到NumPy和OpenCV的各种Python库。 Kivy可以通过PyJNIus(Android)和PyOBJus(iOS)来访问几乎所有本机移动API(GPS,相机,加速度计,Android的Google API),它们会自动将Java / Objective-C代码包装在Python中。
奇异果快。 这适用于应用程序开发和应用程序执行速度。 所有关键功能都在C级别实现。 Kivy还会在有意义的地方使用GPU。 GPU完成了大部分工作,从而大大提高了性能。
Kivy非常灵活。 这意味着Kivy的快速发展使您可以立即适应新技术。 Kivy开发人员反复添加了对新的外部设备和软件协议的支持,有时甚至在其发布之前。 Kivy可以与许多不同的第三方解决方案结合使用。 例如,在Windows上,Kivy支持WM_TOUCH,这意味着任何具有Windows 7 Pen&Touch驱动程序的设备都可以与Kivy一起使用。 在OS X上,您可以使用具有Multi-Touch支持的Apple设备,例如触控板和鼠标。 在Linux上,您可以使用HID输入输入事件。 此外,Kivy支持TUIO(可识别用户界面对象)和许多其他输入源。
您可以用几行代码编写一个简单的应用程序。 Kivy程序是使用Python编程语言创建的,该语言具有广泛的功能和强大的功能,但易于使用。 此外,Kivy开发人员还创建了自己的GUI标记语言,以创建复杂的自定义GUI。 使用该语言,您可以快速配置,连接和组织应用程序的元素。
而且,是的,Kivy是完全免费的。 您可以在任何地方使用它! 在商业产品或开放源代码中。 我将提供所有应用程序代码,并充分详细地展示为移动平台开发时如何实现这些元素。 作为一个IDE,我始终使用
PyCharm ,它完美支持
Kv语言语法-一种特殊的DSL语言,用于编写应用程序的UI表示。 该应用程序的框架是使用
CreatorKivyProject控制台实用程序创建的,该实用程序使用MVVM模板提供了基本屏幕。
baseclass文件夹在
Kv语言的 kv接口描述文件中包含用Python编程语言实现的小部件和控件的逻辑。
applibs目录用于第三方库,在
data文件夹中有媒体内容,数据库和其他数据。
main.py文件是应用程序的入口。 除了启动UI
TodoList()外,他什么都不做
。运行()渲染,捕获错误(如果发生)并显示一个窗口以发送错误报告,该报告由
CreatorKivyProject实用程序自动创建,与编写我们的应用程序无关,因此不予考虑。
带有程序代码的
todolist.py文件实现了
TodoList类,该类加载界面布局,初始化其实例,监视设备的硬键事件并返回我们的第一个屏幕,该屏幕在“活动”管理器中列出。 在
TodoList()。Run()之后 ,将调用
构建函数,并返回将在屏幕上显示的小部件。
例如,显示带有图像的单个屏幕的简单程序的代码如下所示:
这是我们的应用程序类的图:
我们的应用程序仅包含三个
Activity ,即屏幕管理器(
ScreenMenager ),我们将其返回到
构建功能,负责
切换 :
#:import ListProjectsActivity libs.uix.baseclass.ListProjectsActivity.ListProjectsActivity #:import ListNotesActivity libs.uix.baseclass.ListNotesActivity.ListNotesActivity #:import AddNewNoteActivity libs.uix.baseclass.AddNewNoteActivity.AddNewNoteActivity #:import ActivityManager libs.uix.baseclass.ActivityManager.ActivityManager <RootScreen@BoxLayout>: orientation: 'vertical' spacing: dp(2) ActivityManager: id: activityManager ListProjectsActivity: id: listProjectsActivity ListNotesActivity: id: listNotesActivity AddNewNoteActivity: id: addNewNoteActivity
当应用程序启动时,将首先安装在ActivityManager中指定的Activity。 在我们的例子中,它是
ListProjectsActivity 。 在用于项目和任务列表的应用程序中,我使用了
ScrollView 。 尽管它更正确
-RecycleView 。 因为第一个,如果有一百多个帖子和项目,那就做不到。 更准确地说,呈现列表将花费很长时间。
RecycleView允许
您几乎立即显示任意长度的列表。 但是由于在任何情况下,对于大型列表,您都必须使用将动态数据加载到列表中或进行分页的方法,但这在TOR中没有进行讨论,因此我使用
ScrollView 。 第二个原因是我太懒了,无法重做
RecycleView下的列表(它在使用上与
ScrollView根本不同),而且时间不多,因为整个应用程序是在四个小时内抽烟和喝咖啡休息的。
带有项目列表(ListProjectsActivity.kv和ListProjectsActivity.py)的开始屏幕如下所示:
由于ListProjectsActivity屏幕的布局已经显示在屏幕上,因此我将显示其控件类的外观:
# -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.screenmanager import Screen as Activity from libs.uix.baseclass.InputDialog import InputDialog from . ProjectItem import ProjectItem class ListProjectsActivity(Activity): objApp = App.get_running_app() def setListProjects(self, objApp): for nameProject in objApp.dataProjects.keys(): self.ids.layoutContainer.add_widget(ProjectItem(projectName=nameProject)) def createNewProject(self, projectName): if projectName and not projectName.isspace(): self.ids.layoutContainer.add_widget(ProjectItem(projectName=projectName)) self.objApp.addProjectInBase(projectName) def deleteProject(self, instance): for projectName in self.objApp.dataProjects: if instance.projectName == projectName: self.objApp.deleteProjectFromBase(projectName) break def showDialogCreateProject(self, *args): InputDialog( title=' ', hintText=' ', textButtonCancel='', textTuttonOk='', eventsCallback=self.createNewProject).show()
对话框调用:
在工作中,调用窗口并创建一个新项目将如下所示:
我将不讨论有关应用程序数据的问题,因为数据是以下形式的常规字典
{"Name Project": [{"pathToAvatar": "", "nameDate": "", "nameAuthor": "", "textNote": ""}]}
并且作为简单的json文件存储在
数据目录中。
让我们看看带有项目名称的项目是什么,以及如何使用Kivy通过滑动从列表中删除项目? 为此,我们必须从
SwipeToDelete库的
SwipeBehavior类继承列表
中小部件的行为:
ProjectItemActivity.py from kivy.properties import StringProperty from kivy.uix.boxlayout import BoxLayout from libs.applibs.swipetodelete import SwipeBehavior class ProjectItemActivity(SwipeBehavior, BoxLayout): projectName = StringProperty() def on_touch_down(self, touch): if self.collide_point(touch.x, touch.y): self.move_to = self.x, self.y return super(ProjectItemActivity, self).on_touch_down(touch) def on_touch_move(self, touch): if self.collide_point(touch.x, touch.y): self.reduce_opacity() return super(ProjectItemActivity, self).on_touch_move(touch) def on_touch_up(self, touch): if self.collide_point(touch.x, touch.y): self.check_for_left() self.check_for_right() return super(ProjectItemActivity, self).on_touch_up(touch)
以及Kv标记中的项目项说明:
ProjectItemActivity.kv <ProjectItemActivity>: swipe_rectangle: self.x, self.y , self.width, self.height swipe_timeout: 1000000 swipe_distance: 1 event_after_swipe: app.listActivity.deleteProject OneLineListItem: text: root.projectName on_press: app.listActivity.setNotesProject(root.projectName)
通常,Kivy中的每个小部件都具有
on_touch方法,您可以使用该方法捕获屏幕上发生的任何事件。 这是可用事件列表的一小部分:
['double_tap_time', 'grab_state', 'is_double_tap', 'is_mouse_scrolling', 'is_touch', 'is_triple_tap', 'move', 'push', 'push_attrs', 'push_attrs_stack', 'scale_for_screen', 'time_end', 'time_start', 'time_update', 'triple_tap_time', 'ungrab', 'update_time_end']
为Android实现上下文菜单...
这里也没有问题,因为这只是一个标准的DropDown小部件。 由于您可以在自己的想象力范围内自定义Kivy中的所有小部件和控件,因此我很容易获得了一个漂亮的菜单。 左边是基本的DropDown,右边是我的:
上下文菜单列表的布局:
ContextMenuAndroidActivity.kv #:import MDSeparator libs.applibs.kivymd.card.MDSeparator #:import MenuItem libs.applibs.animdropdown.MenuItem <ContextMenuAndroidActivity>: MenuItem: text: '' menu: root on_press: root.tapOnItem(self.text) MDSeparator: MenuItem: text: '' menu: root on_press: root.tapOnItem(self.text)
上下文菜单的软件部分:
ContextMenuAndroidActivity.kv from kivy.app import App from kivy.clock import Clock from libs.applibs.animdropdown import AnimMenuDropDown class ContextMenuAndroidActivity(AnimMenuDropDown): def tapOnItem(self, textItem): objApp = App.get_running_app() if textItem == '': objApp.listActivity.deletePost() else: objApp.activityManager.current = 'add new note activity' Clock.schedule_once(objApp.addNewNoteActivity.editNote, .5)
接下来,我们从
animdropdown库中导入
MenuDropDown按钮,并向
其传递上下文菜单的对象作为参数,然后将其添加到所需的屏幕中。 在我们的应用程序中,这是便签卡右侧的按钮:
标记活动卡注释:
基类
NoteActivity :
from kivy.app import App from kivy.properties import StringProperty from kivy.uix.boxlayout import BoxLayout from libs.applibs.animdropdown import MenuButton from libs.applibs.swipetodelete import SwipeBehavior from . ContextMenu import ContextMenu class NoteActivity(SwipeBehavior, BoxLayout): nameDate = StringProperty() textNote = StringProperty() pathToAvatar = StringProperty() def __init__(self, **kwargs): super(NoteActivity, self).__init__(**kwargs) self.objApp = App.get_running_app() menuButton = MenuButton( dropdown_cls=ContextMenu, icon='dots-vertical', _on_dropdown_fnc=self.setCurrentPost) self.ids.titleBox.add_widget(menuButton) def setCurrentPost(self, *args): self.objApp.listNotesActivity.checkCurentPost = self
ListNotesActivity的软件实现:
如何管理应用程序的活动? 为了从一个活动切换到另一个活动,我们必须向屏幕管理器指示新活动的名称:
class ListNotesActivity(Activity): ... def addNewNote(self, *args): self.objApp.activityManager.current = 'add new note activity'
...其中
“添加新笔记活动”是添加新笔记的活动的名称。
AddNewNoteActivity活动的屏幕和布局:
基类:
from kivy.app import App from kivy.animation import Animation from kivy.uix.screenmanager import Screen as Activity from kivy.metrics import dp from libs.uix.baseclass.NoteActivity import NoteActivity class AddNewNoteActivity(Activity): objApp = None edit = False oldTextNote = '' def animationButton(self): self.objApp = App.get_running_app() self.ids.toolBar.title = self.objApp.listNotesActivity.ids.toolBar.title Animation(size=(dp(56), dp(56)), d=.5, t='in_out_cubic').start(self.ids.floatingButton) def addNewNotes(self, textNote): if self.edit: nameProject = self.ids.toolBar.title self.objApp.addEditNoteInBase(nameProject, textNote, self.oldTextNote) self.objApp.activityManager.backActivity('list notes activity', self.ids.floatingButton) self.objApp.listNotesActivity.checkCurentPost.textNote = textNote self.edit = False return self.objApp.listNotesActivity.ids.layoutContainer.add_widget( NoteActivity( textNote=textNote, nameDate='%s\n%s' % ( self.objApp.nameAuthor, self.objApp.getDate()), pathToAvatar='data/images/avatar.png')) self.objApp.addNoteInBase(self.ids.toolBar.title, textNote, 'data/images/avatar.png') def editNote(self, interval): self.edit = True self.ids.textInput.text = self.objApp.listNotesActivity.checkCurentPost.textNote self.oldTextNote = self.ids.textInput.text
为了给按钮添加动画效果,我使用了
on_enter事件,该事件在屏幕上安装了Activity时引发:
在标记中:
<AddNewNoteActivity> on_enter: root.animationButton()
在Python代码中:
class AddNewNoteActivity(Activity): def animationButton(self): Animation(size=(dp(56), dp(56)), d=.5, t='in_out_cubic').start(self.ids.floatingButton)
与Xamarin.Forms不同,Kivy中的UI到处看起来都是一样的。 因此,如果要为两个平台(Android和iOS)编写应用程序,则在标记界面并指定小部件的属性时应考虑到这一点。 或为两个平台做两个标记(逻辑保持不变)。 这样做是有好处的,因为UI呈现和事件与平台的功能无关,因此您无需使用本机API来控制这些操作,从而使您的应用程序几乎可以在任何平台上轻松执行。 所有图形都是使用GPU上的本机OpenGL和SDL2调用渲染的,这使您可以快速绘制菜单,按钮和其他图形界面,包括2D和3D图形。
该应用程序使用Android UI MaterialDesign。 例如,我的上一个项目具有自适应接口:
这是材料设计风格的可能性的证明:
就像我说的那样,Kivy不使用本机API来呈现UI,因此它允许您使用
屏幕模块来模拟各种模型的设备和平台。 使用必要的参数运行项目就足够了,这样就可以像在真实设备上运行一样在计算机上打开经过测试的应用程序的窗口。 听起来很奇怪,但是由于Kivy在呈现UI时是从平台中抽象出来的,因此消除了对繁琐缓慢的仿真器进行测试的需求。 这仅适用于UI。 例如,本文中描述的测试应用程序已使用
-m屏幕参数进行了测试
:droid2,portrait,scale = .75一对一对应于我的真实设备。
最后可以说什么? Kivy好吗? 无疑不错! 如果您了解出色的Python编程语言,则可以使用同样出色的Kivy框架轻松地为移动(不仅是)平台制作应用程序。
使用Kivy框架开发应用程序的优势:- 由于我们正在处理Python,因此应用程序的开发速度比任何其他编程语言或框架的开发速度快几倍。
- 您可以在项目中使用的成百上千个现成的Python库: OpenCV,Django,Flask,NumPy,ffmpeg,sqlite3,lxml以及其他数千种。
- 由于Kivy使用OpenGL和GPU以及其自己的小部件和控件来渲染图形,因此UI渲染速度非常快,您完全可以免除头痛,这在其他框架中也存在,这些框架需要深入了解本机部分以实现界面的某些部分。
- 仅在需要访问特定平台功能(仅在真正的跨平台框架中根本不存在)的地方使用本机:例如,访问地理位置,访问摄像头,蓝牙技术...
使用PyJnius实现对本地Android API的访问以获取IMEI和设备模型:
def _get_model_android(): from jnius import autoclass Build = autoclass('android.os.Build') return str(Build.DEVICE) def _get_imei_android(): from jnius import autoclass Service = autoclass('org.renpy.android.PythonActivity').mActivity Context = autoclass('android.content.Context') TelephonyManager = Service.getSystemService(Context.TELEPHONY_SERVICE) return str(TelephonyManager.getDeviceId())
例如,用Java实现本机接收IMEI设备的实现:
import android.content.Context; import android.telephony.TelephonyManager; public class GetImeiAndroid { public String getImeiAndroid() { TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); String IMEINumber = tm.getDeviceId(); return IMEINumber; } }
- 对于Android,您可以在项目中使用第三方jar库。
- 您完全拥有屏幕上发生的所有事件:触摸,多点触摸,滑动,打孔和其他事件,而无需使用本机,因此,这是Kivy不可或缺的一部分。
触摸设备中的Kivy功能:
尽管具有所有优点,但Kivy有几个缺点:- “冷启动”的速度,即从部署所有库的那一刻起首次启动应用程序的速度就相当长。 后续的是普通的,但比本地的长,这要取决于移动设备的处理器负载。
- 使用列表。 您可以在半秒内显示大小为100,000项的列表(例如,用户卡片,展示柜,报价),但有一个条件-所有卡片的高度必须相同。 例如,如果您显示的是引文列表,但预先包含未知数量的文本,但全部显示,则一次不能显示十多个点,因为这大约需要10到15秒。 在这种情况下,滚动列表时,您将必须分别加载10-15个项目。
- 无法显示超过6500个字符的文本(3.5页打印文本)-我们得到黑屏。 解决方法是先将文本分开,然后将其粘合在一起,但这似乎仍是一个拐杖。 但是,不清楚一次会向谁输出这样大量的文本。 特别是在移动平台方面。
→
有关基维的更多文章来自Kivy开发人员
的虚拟机 (ZenCODE的第一篇文章)已准备就绪,并已配置为两个Python分支都构建项目。