📜 ⬆️ ⬇️

Snake fruit or fruit python?

Non biblical story



  1. And made Google Android. He settled in the garden of mobile platforms, giving him a wife - Java.
  2. And he ordered Google Jav : Android , beautiful and fast, and Android : , Jav s. And he forbade them to taste the fruits of the tree of knowledge of frameworks and programming languages, so that their programs do not become slow and objectionable to the user.
  3. The oldest snake, dynamic Python, was the smartest on that tree. He watched Android for a long time , , . Python Android , , . Python Android : did Google tell you the truth, do not eat the fruits of the tree of knowledge of frameworks and programming languages ​​so that your programs do not become slow and objectionable to the user?
  4. Just as the all-powerful Google commanded me, Android answered, and my wife, Java, creates my programs.
  5. Google deceived you, his cunning Python hissed, for he knows that on the day when you taste the fruits of the tree of knowledge of frameworks and programming languages, you will see the light and other developers will reach out to you and start creating programs, and and fast, as from your wife Java, and they will be cross-platform!
  6. And Python plucked the fruit from the tree of knowledge of frameworks and programming languages ​​and stretched Android ʻu, and he ate.
  7. The name of that fruit is Kivy.

Kivy Framework Book (Chapter 2, verse 1-7)



As you may have guessed, it will be about developing mobile applications for the Android platform using the Kivy framework and the Python programming language. On Habré, there are already several articles on this topic, mostly small essays describing in general what kind of fruit this Kivy is, with which it is eaten and a couple of examples, such as Hello World and tic-tac-toe.


On the Internet, there are not many Kivy-written applications, and a rather long list of high-quality games with 2D and 3D graphics. This is partly due to the fact that, thanks to the support of the GPU acceleration, the graphic providers PyGame, OpenGL, SDL, X11, the support of shaders, Kivy, it can be said, by default, it is more oriented for use in the gaming industry, and someone directly says that write a less worthwhile and beautiful application in Kivy will not succeed or you will spend much more time on it than, say, using native code, not to mention projects like WhatsApp.


Well, you can not trust anyone (I can), so in this article we will personally test the capabilities of Kivy in the development of mobile development. No, writing an application, like WatsApp from scratch and saying how easy it is between two cigarettes and a cup of coffee, is done in Kivy, we will not (the article format does not allow), but I see that you have installed a wonderful program CleanMaster: animation, custom controllers, transparent progress bar - sexy, not an application - just what we need to demonstrate the capabilities of the framework!


Here is what it looks like (original Clean Master screens):



We will try to create similar, in Java, Activity: with animation, transformation, beautiful progress, cleared cache counter, etc., and in the next article we will assemble the default installation apk for Android device and analyze its pros and cons. Immediately, I note that there will be no functionality other than the demonstration of UI elements in our application, and the entire animation of the cache cleaning progress, STORAGE / RAM counting animation, is a regular demonstration.


The article format is not for beginners and although the code samples are quite densely supplied with comments, they are not educational in nature and, most likely, the article will be interesting to Java developers in the sense that it is interesting to compare native code and the same implementation using the Kivy framework.


What do we need for this? Most simple tools are coffee and a couple of packs of cigarettes. In fact, we simply will not consider installing Kivy and related tools to build apk, since all this can be easily found on the net.


So, first of all create a directory of our project. Let's call it KivyCleanMasterDemo.



The project structure is arbitrary. You can name directories as you wish and keep the files where you want, the benefit of Python and Kivy do not impose any restrictions on this plan. The only condition: the main.py file must be present in the root folder of the project - this is the entry point to the program. This file will be imported when you run the already installed apk package.


So, we enter into our application!



main.py
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # main.py # #    .     program.py. #   ,       . # from __future__ import print_function import os import sys import traceback try: import kivy kivy.require("1.9.1") from kivy.app import App from kivy.config import Config #     ,   # ,    . Config.set("kivy", "keyboard_mode", "system") # Activity  . from Libs.uix.bugreporter import BugReporter except Exception: print("\n\n{}".format(traceback.format_exc())) sys.exit(1) __version__ = "0.0.1" def main(): app = None try: from program import Program #    #  . app = Program() app.run() except Exception as exc: print(traceback.format_exc()) traceback.print_exc(file=open("{}/error.log".format( os.path.split(os.path.abspath(sys.argv[0]))[0]), "w")) if app: #       app.start_screen.clear_widgets() class Error(App): """    .""" def callback_report(self, *args): """  -""" try: import webbrowser import six.moves.urllib txt = six.moves.urllib.parse.quote( self.win_report.txt_traceback.text.encode( "utf-8")) url = "https://github.com/HeaTTheatR/KivyCleanMasterDemo" \ "/issues/new?body=" + txt webbrowser.open(url) except Exception: sys.exit(1) def build(self): self.win_report = BugReporter( callback_report=self.callback_report, txt_report=str(exc), icon_background="Data/Images/logo.png") return self.win_report Error().run() if __name__ in ("__main__", "__android__"): main() 

Everything is simple and there is no need for additional comments. We are interested in the code:


 from program import Program #    app = Program() app.run() #   

From main.py we follow the code further. Consider the main class of our demo application from the program.py module



program .py
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # program.py # #    . # try: import kivy kivy.require("1.9.1") from kivy.app import App from kivy.uix.button import Button from kivy.uix.screenmanager import Screen, FadeTransition from kivy.clock import Clock from kivy.core.window import Window #      Activity . #   ,   , . from Libs.uix.about import About from Libs.uix.startscreen import StartScreen from Libs.uix.junkfiles import JunkFiles # ,   Activity   . from Libs.programclass import ShowScreens, AnimationProgress except Exception: import traceback raise Exception(traceback.format_exc()) __version__ = "0.0.1" class Program(App, ShowScreens, AnimationProgress): """ """ #  . title = "Clean Master" #    icon = "Data/Images/logo.png" #   def __init__(self, **kvargs): super(Program, self).__init__(**kvargs) #   /   -. Window.bind(on_keyboard=self.on_events) #      programclass. self.About = About self.Clock = Clock self.JunkFiles = JunkFiles self.prog_dir = self.directory self.new_color = \ [0.1568627450980392, 0.34509803921568627, 0.6784313725490196] def build(self): #  Activity . self.start_screen = StartScreen(events_callback=self.on_events) #  Activity      #         # ( ). self.start_screen.body_storage_ram.bind(pos=self.animation_storage_ram) self.start_screen.body_storage_ram.bind(size=self.animation_storage_ram) #     STORAGE/RAM. Clock.schedule_interval(self.calc_elliptical_length, .03) return self.start_screen def on_events(self, *args): """  .""" try: _args = args[0] #   -     event = _args if isinstance(_args, str) else _args.id except AttributeError: event = args[1] #  ,   -    if event == "About": #  Activity About self.show_about() elif event == "on_previous" or event == 27: #     Activity self.back_screen() elif event == "JUNK FILES": #  Activity JUNK FILES self.show_junk_files() self.Clock.unschedule(self.calc_elliptical_length) #    STORAGE/RAM self.Clock.schedule_interval(self.animation_clean, 0.2) #     elif event == "STOP": #   JUNK FILES Clock.unschedule(self.animation_clean) self.back_screen() def show_new_screen(self, instance_new_screen, string_new_name_screen): """  .""" #        , , About  About. name_current_screen = self.start_screen.screen_manager.current if name_current_screen == string_new_name_screen: return self.start_screen.screen_manager.add_widget(screen) #  Activity    self.start_screen.screen_manager.transition = FadeTransition() #     self.start_screen.screen_manager.current = string_new_name_screen #  Activity   self.start_screen.action_previous.title = string_new_name_screen #   Activity  ActionBar self.start_screen.action_previous.app_icon = "Data/Images/arrow_left.png" 

In essence, this class sets the main screen of the application, starts the STORAGE / RAM counting progress animation, monitors events and switches the screens of our demo application.


The main Activity of the application is displayed in the following piece of code:


 #  Activity . self.start_screen = StartScreen(events_callback=self.on_events) return self.start_screen 

However, before you see what the layout file of the start-up Activity looks like, a few words about building a UI in Kivy.


The interface layout in Kivy can be built in two ways - directly in the code:


 root = MyRootWidget() box = BoxLayout() byt1 = Button() byt2 = Button() box.add_widget(btn1) box.add_widget(btn2) root.add_widget(box) 

I do not recommend using this method, since it will be quite difficult to trace the hierarchy and interaction of widgets in a more or less complex layout, and you will spend a very long time on making changes to the layout built this way.


A completely different UI built using a special markup language - Kv Language, is very similar to QML in Qt, where the hierarchy of widgets is distinguished using identities and clearly shows which layout this or that control belongs to, and property management is carried out in the same code class via widget id:


 <MyRootWidget@BoxLayout>: Button: id: btn1 Button: text: "  btn2" 

 class MyRootWidget(BoxLayout): def __init__(self. **kvargs): super(MyRootWidget, self).__init__(**kvargs) self.ids.btn1.text = "  btn1" 

Well, we gave the basic information for reading the interface layouts (Java programmers should have no difficulty at all on this), now let's see how the starting Activity created in Kivy looks like:



But the markup of this Activity in Kv Language:



startscreen.kv
 #:kivy 1.9.1 <StartScreen> ActionBar: id: action_bar canvas: Color: rgb: root.color_blue Rectangle: pos: self.pos size: self.size ActionView: ActionPrevious: id: action_previous app_icon: "Data/Images/previous_app_icon.png" previous_image: "Data/Images/previous_image.png" with_previous: True on_press: root.events_callback("on_previous") ActionOverflow: id: action_overflow overflow_image: "Data/Images/overflow_image.png" #  . ScreenManager: id: screen_manager #   . Screen: id: screen #  STORAGE/RAM FloatLayout: id: float_layout canvas: Color: rgb: root.color_blue Rectangle: pos: self.pos size: self.size #   STORAGE Color: rgba: root.color_ellipse_static Line: width: 3. circle: (self.center_x / 1.5, self.center_y / .65, \ min(self.width, self.height) / 4.5, 220, 500, 50) #    STORAGE Color: rgba: 1.0, 1.0, 1.0, 1 Line: width: 3. #   RAM Color: rgba: root.color_ellipse_static Line: width: 3. circle: (self.center_x / .65, self.center_y / .69, \ min(self.width, self.height) / 7, 220, 500, 50) #    RAM Color: rgba: 1.0, 1.0, 1.0, 1 Line: width: 3. #    STORAGE Image: id: storage_numeral_one size_hint: .14, .14 pos_hint: {"center_x": .28, "center_y": .76} allow_stretch: True Image: id: storage_numeral_two size_hint: .14, .14 pos_hint: {"center_x": .39, "center_y": .76} allow_stretch: True Image: size_hint: .06, .06 pos_hint: {"center_x": .47, "center_y": .82} allow_stretch: True source: "Data/Images/percent.png" #    RAM Image: id: ram_numeral_one size_hint: .07, .07 pos_hint: {"center_x": .74, "center_y": .72} source: "Data/Images/3.png" allow_stretch: True Image: id: ram_numeral_two size_hint: .07, .07 pos_hint: {"center_x": .80, "center_y": .72} source: "Data/Images/4.png" allow_stretch: True Image: size_hint: .05, .05 pos_hint: {"center_x": .85, "center_y": .74} allow_stretch: True source: "Data/Images/percent.png" #    Label: text: "STORAGE" bold: True pos_hint: {"center_x": .34, "center_y": .63} color: root.color_label Label: text: "124.40MB/704.99MB" bold: True pos_hint: {"center_x": .34, "center_y": .68} color: root.color_label font_size: "10sp" Label: text: "RAM" bold: True pos_hint: {"center_x": .78, "center_y": .63} color: root.color_label #   today_cleaned Label: size_hint: 1, .05 text: "Today cleaned: 0.0B Total: 0.0B" pos_hint: {"top": .45} canvas: Color: rgba: 1.0, 1.0, 1.0, 0.3 Rectangle: pos: self.pos size: self.size #    "JUNK FILES", "MEMORY BOOST" # "APP MANAGER", "SECURITY & PRIVACY" GridLayout: id: body_buttons_menu size_hint: 1, .4 cols: 2 padding: 10 spacing: 5 #    canvas.before: Color: rgb: 1.0, 1.0, 1.0 Rectangle: pos: self.pos size: self.size # ,    Color: rgb: 0, 0, 0 Line: points: [0, self.size[1] / 1.9, self.size[0], self.size[1] / 1.9] width: 1 Line: points: [self.size[0] / 2, 0, self.size[0] / 2, self.size[1] / 1.001] width: 1 

As you can see, Kv Language is almost Python, in the sense that in addition to marking UI elements, you can perform calculations in kv files, pull callbacks and pass parameters to them, import and use standard or external Python modules.


Now let's take a look at the class that controls our layout, since it can be seen that not all elements are created in the markup file: for example, there is no spinner in the ActionBar, and only the root box is created under the menu buttons.



startscreen.py
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # startscreen.py # #   . # from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.image import Image from kivy.uix.button import Button from kivy.uix.behaviors import ButtonBehavior from kivy.uix.actionbar import ActionItem from kivy.lang import Builder from kivy.properties import ObjectProperty, ListProperty class ImageButton(ButtonBehavior, Image): pass class MyOwnActionButton(Button, ActionItem): pass class StartScreen(BoxLayout): events_callback = ObjectProperty(None) """   .""" color_blue = ListProperty( [0.1607843137254902, 0.34901960784313724, 0.6784313725490196]) """ background . """ color_label= ListProperty( [0.6784313725490196, 0.7294117647058823, 0.8392156862745098, 1]) """   STORAGE/RAM. """ color_ellipse_static= ListProperty( [0.38823529411764707, 0.5254901960784314, 0.7764705882352941, 1]) """   STORAGE/RAM. """ Builder.load_file("Libs/uix/kv/startscreen.kv") """ """ def __init__(self, **kvargs): super(StartScreen, self).__init__(**kvargs) self.orientation = "vertical" #   . self.layouts = self.ids self.body_storage_ram = self.ids.float_layout self.screen_manager = self.ids.screen_manager self.action_previous = self.ids.action_previous self.background_action_bar = self.ids.action_bar.canvas.children[3] self.ellips_storage = self.body_storage_ram.canvas.children[8] self.ellips_ram = self.body_storage_ram.canvas.children[14] self._action_overflow = self.ids.action_overflow self.create_spinner_items() self.create_menu_buttons() def create_spinner_items(self): """      ActionBar.""" for item_name in ["Settings", "Update", "Like Us", "Feedback", "FAQ", "About"]: item_button = \ MyOwnActionButton( text=item_name, id=item_name, on_press=self.events_callback, color=[.1, .1, .1, 1], background_normal="Data/Images/background_action_item.png", background_down="Data/Images/background_down.png", on_release=lambda *args: self._action_overflow._dropdown.select( self.on_release_select_item_spinner())) self._action_overflow.add_widget(item_button) def create_menu_buttons(self): """    .""" name_path_buttons_menu = { "JUNK FILES": "Data/Images/clean_cache.png", "MEMORY BOOST": "Data/Images/clean_memory.png", "APP MANAGER": "Data/Images/clean_apk.png", "SECURITY & PRIVACY": "Data/Images/clean_privacy.png"} for name_button in name_path_buttons_menu.keys(): item_box = BoxLayout(orientation="vertical") item_label = Label(text=name_button, color=[.1, .1, .1, 1]) item_button = \ ImageButton(source=name_path_buttons_menu[name_button], id=name_button, on_press=self.events_callback) item_box.add_widget(item_button) item_box.add_widget(item_label) self.ids.body_buttons_menu.add_widget(item_box) def on_release_select_item_spinner(self): """  release    ActionBar.   ,     .""" pass 

This class has only two functions, the task of which is to create buttons for the ActionBar drop-down list, buttons and menu labels. The class also initializes attributes for further use in the code — widgets obtained from their identifiers from the startscreen.kv markup file.


For further work, we need a custom widget button ...



... which is used in the next two Activity. It is small and will help to better understand the interaction between the interface markup and the program code that manipulates its properties.



custombutton.kv


 #:kivy 1.9.1 <CustomButton@Button> #    Button . #   root -    CustomButton. id: root.id text: root.button_text background_normal: "Data/Images/background_action_item.png" background_down: "Data/Images/background_down.png" size_hint_y: None text_size: root.width - 150, root.height valign: "middle" height: root.button_height color: 0.1, 0.1, 0.1, 1 on_press: if callable(root.event_callback): root.event_callback(root.id) #     Image: source: root.icon size_hint_y: None height: root.icon_height pos: root.x - 25, root.y + self.height / 2 #     Image: source: root.icon_load size_hint_y: None pos: root.width / 2 - 25, root.y size: root.size 

custombutton.py


 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # custombutton.py # from kivy.lang import Builder from kivy.uix.button import Button from kivy.properties import StringProperty, NumericProperty, ObjectProperty Builder.load_file("Libs/uix/kv/custombutton.kv") class CustomButton(Button): id = StringProperty("") button_height = NumericProperty(65) button_text = StringProperty("") icon = StringProperty("") icon_height = NumericProperty(30) icon_load = StringProperty("Data/Images/loading.gif") event_callback = ObjectProperty(None) 

Well, since we already have a spinner ready, a custom button widget and in the main Program class, the on_events function handles the "About" event ...



... let's create this Activity and its control class.



about.kv
 #:kivy 1.9.1 <About>: orientation: "vertical" ScrollView: GridLayout: cols: 1 size_hint_y: None spacing: 10 padding: 10 height: self.minimum_height canvas: Color: rgb: root.about_background Rectangle: pos: self.pos size: self.size #   Image: source: "Data/Images/logo.png" size_hint_y: None #   Label: text: "Clean Master 5.4.0.1395" size_hint_y: None height: "10pt" color: 0.1, 0.1, 0.1, 1 italic: True #   share GridLayout: id: box_share cols: 1 size_hint_y: None height: self.minimum_height #   Label: text: root.text_license text_size: self.size font_size: dp(12) valign: "top" size_hint_y: None height: root.height / 2.5 color: 0.1, 0.1, 0.1, 1 #   BoxLayout: orientation: "vertical" size_hint: 1, .2 canvas: Color: rgb: root.about_background Rectangle: pos: self.pos size: self.size Color: rgb: 0.5843137254901961, 0.5843137254901961, 0.5843137254901961 Line: points: [0, self.size[1], self.size[0], self.size[1]] width: 1 Label: text: "Copyright 2016 Demo Clean Master\nby HeaTTheatR" halign: "center" color: 0.1, 0.1, 0.1, 1 

about.py
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # about.py # from kivy.uix.boxlayout import BoxLayout from kivy.lang import Builder from kivy.properties import ObjectProperty, StringProperty, ListProperty from .custombutton import CustomButton class About(BoxLayout): events_callback = ObjectProperty(None) """   .""" text_license = StringProperty("Clean Master") about_background = ListProperty( [0.7294117647058823, 0.7686274509803922, 0.8470588235294118]) Builder.load_file("Libs/uix/kv/about.kv") """ """ def __init__(self, **kvargs): super(About, self).__init__(**kvargs) self.create_button_share(self.ids.box_share) def create_button_share(self, box_share): """   share  . :type box_share: <'kivy.weakproxy.WeakProxy'> :param box_share: <'kivy.uix.gridlayout.GridLayout'> """ about_items_share = { "Share this app": "Data/Images/about_share.png", "Like us on Facebook": "Data/Images/about_facebook.png", "Join our beta testing group": "Data/Images/google_plus.png", "Help us with localization": "Data/Images/about_localization.png", "For Business Cooperation": "Data/Images/skype_icon.png"} for name_item in about_items_share.keys(): box_share.add_widget( CustomButton(icon_load="Data/Images/previous_image.png", icon=about_items_share[name_item], button_text=name_item, button_height=45, icon_height=25)) 

Now we can open the drop-down list in the ActionBar, select the About item and accordingly go to the selected screen. I allowed myself some liberties regarding the About text, replacing it with the GNU GPL license, but I think this is not essential for our demonstration.



We can return to the start screen by clicking the arrow on the left in the ActionBar. We recorded the callback callback in the startscreen.kv layout:



And they caught him in the main Program class in the on_events function:



Pay attention to the show_about and back_screen functions - these are functions from the ShowScreens class of the same-name module of the programclass package, which jump in on_events. This class opens three layouts of our demo application and switches between them.



Of course, it was possible to write these functions in the main Program class, but I don’t like it when the code takes on the appearance of a three-story building, and it’s much easier to make edits when everything is on the shelves in sight, and not lost in dozens of functions and classes.


Let's take a look at the ShowScreens class.


ShowScreens
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # ShowScreens.py # class ShowScreens(object): """  .""" background_action_bar = \ [0.1568627450980392, 0.34509803921568627, 0.6784313725490196] def show_about(self): try: text_license = open("{}/LICENSE".format(self.prog_dir)).read() except Exception: text_license = "Clean Master" #   ,  About    "JUNK FILES". if self.start_screen.layouts.screen.manager.current == "JUNK FILES": self.Clock.unschedule(self.animation_clean) self.start_screen.background_action_bar.rgb = self.background_action_bar #       "Memory boost"  # "Cache junk". self.screen_junk.button_memory_bust.remove_widget( self.screen_junk.button_memory_bust_icon_state) self.screen_junk.button_cache_junk.remove_widget( self.screen_junk.button_cache_junk_icon_state) screen_about = \ self.About(events_callback=self.on_events, text_license=text_license) self.show_new_screen(screen_about, "About") def show_junk_files(self): self.set_default_tick_rgb() self.screen_junk = self.JunkFiles(events_callback=self.on_events) self.show_new_screen(self.screen_junk, "JUNK FILES") def back_screen(self): """   ActionPrevious  ActionBar.        .""" current_screen = self.start_screen.screen_manager.current if current_screen in ("About", "JUNK FILES"): #     ,  #  . if current_screen == "JUNK FILES": self.Clock.unschedule(self.animation_clean) #     ,   #  STORAGE/RAM. self.Clock.schedule_interval(self.calc_elliptical_length, .03) if len(self.start_screen.screen_manager.screens) != 1: self.start_screen.screen_manager.screens.pop() self.start_screen.screen_manager.current = \ self.start_screen.screen_manager.screen_names[-1] self.start_screen.action_previous.title = \ self.start_screen.screen_manager.current if current_screen in ("About", "JUNK FILES"): #    actionbar,      # About,    "JUNK FILES". self.start_screen.background_action_bar.rgb = self.new_color #  "",    actionbar. if self.start_screen.screen_manager.screen_names[-1] != \ "JUNK FILES" or current_screen == "": self.start_screen.background_action_bar.rgb = \ self.background_action_bar #   previous    actionbar. if self.start_screen.screen_manager.screens[-1].name != \ "JUNK FILES": self.start_screen.action_previous.app_icon = \ "Data/Images/previous_app_icon.png" 

So! , , , Activity JUNK FILES . Activity JUNK FILES :



junkfiles.py junkfiles.kv:



junkfiles.kv
 #:kivy 1.9.1 <JunkFiles> orientation: "vertical" FloatLayout: id: float_layout canvas: Color: rgb: 0.1607843137254902, 0.34901960784313724, 0.6784313725490196 Rectangle: pos: self.pos size: self.size #    BoxLayout: pos_hint: {"center_x": .5, "center_y": .75} size_hint: .7, 1 Image: id: storage_numeral_one size_hint: .52, .52 source: "Data/Images/6.png" Image: id: storage_numeral_two size_hint: .52, .52 source: "Data/Images/5.png" Image: id: point size_hint: .25, .25 pos_hint: {"center_y": .21} source: "Data/Images/dot.png" Image: id: numeral_float size_hint: .52, .52 source: "Data/Images/8.png" Image: size_hint: .22, .22 pos_hint: {"center_y": .48} source: "Data/Images/gb.png" #   ProgressLine: id: progress_line size_hint: 1, .1 pos_hint: {"center_y": .05} Label: id: progress_label text: "Scanning:" text_size: root.width - 20, root.height pos_hint: {"center_y": .05} valign: "middle" ScrollView: size_hint: 1, .6 canvas.before: Color: rgb: 1.0, 1.0, 1.0, Rectangle: pos: self.pos size: self.size GridLayout: id: grid_layout cols: 1 size_hint_y: None height: self.minimum_height BoxLayout: size_hint_y: None height: 80 padding: 10, 10 canvas.before: Color: rgb: 1.0, 1.0, 1.0, Rectangle: pos: self.pos size: self.size Button: id: button_stop text: "STOP" font_size: "19sp" bold: True markup: True background_normal: "Data/Images/stop_progress.png" background_down: "Data/Images/stop_progress_down.png" color: 0.1, 0.1, 0.1, 1 

junkfiles.py
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # junkfiles.py # #   . # from kivy.uix.boxlayout import BoxLayout from kivy.lang import Builder from kivy.properties import ObjectProperty from .progressline import ProgressLine from .custombutton import CustomButton class JunkFiles(BoxLayout): events_callback = ObjectProperty(None) """   .""" Builder.load_file("Libs/uix/kv/junkfikes.kv") """ """ def __init__(self, **kvargs): super(JunkFiles, self).__init__(**kvargs) self.create_custom_button() #   . self.layouts = self.ids self.button_memory_bust = self.layouts.grid_layout.children[0] self.button_cache_junk = self.layouts.grid_layout.children[1] self.button_memory_bust_icon_state = self.button_memory_bust.children[0] self.button_cache_junk_icon_state = self.button_cache_junk.children[0] self.progress_line = self.layouts.progress_line self.progress_label = self.layouts.progress_label self.button_stop = self.layouts.button_stop self.background = self.ids.float_layout.canvas.children[0] def create_custom_button(self): """        .""" junk_files_items = {"Memory boost": "Data/Images/memory_boost.png", "Cache junk": "Data/Images/cache_junk.png"} for action_clean in junk_files_items.keys(): path_to_icon_action = junk_files_items[action_clean] self.ids.grid_layout.add_widget( CustomButton(id=action_clean, icon=path_to_icon_action, button_text=action_clean, on_press=self.events_callback)) self.ids.button_stop.bind( on_press=lambda *args: self.events_callback("STOP")) 

ProgressLine junkfiles.kv:



junkfiles.py:



 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # progressline.py # from kivy.uix.widget import Widget from kivy.utils import get_color_from_hex from kivy.graphics import Color, Line class ProgressLine(Widget): """ .""" bar_value_percent = 0 color = "#ffffff56" def __init__(self, **kwargs): super(ProgressLine, self).__init__(**kwargs) self.bind(pos=self.redraw) self.bind(size=self.redraw) def redraw(self, *args): """    .""" with self.canvas: self.canvas.clear() line_width = float(self.height) / 2 + 1 new_y = self.y + line_width new_x = self.x + self.width * self.bar_value_percent / 100 Color(*get_color_from_hex(self.color)) Line(points=[self.x, new_y, new_x, new_y], width=line_width, cap="none") 


, .



AnimationProgress.py
 #! /usr/bin/python2.7 # -*- coding: utf-8 -*- # # AnimationProgress.py # from random import randint class AnimationProgress(object): """  .""" def __init__(self): self.set_default_tick_rgb() self.scan_packages = range(100) def animation_storage_ram(self, *args): """   STORAGE/RAM.""" if isinstance(args[0], int): #    elliptical_length_storage = elliptical_length_ram = args[0] else: #      elliptical_length_storage = 317 elliptical_length_ram = 401 if self.tick <= 34: self.start_screen.ellips_storage.circle = \ ((self.start_screen.body_storage_ram.center_x / 1.5, self.start_screen.body_storage_ram.center_y / .65, min(self.start_screen.body_storage_ram.width, self.start_screen.body_storage_ram.height) / 4.5, 220, elliptical_length_storage, 50)) if self.tick <= 65: self.start_screen.ellips_ram.circle = \ ((self.start_screen.body_storage_ram.center_x / .65, self.start_screen.body_storage_ram.center_y / .69, min(self.start_screen.body_storage_ram.width, self.start_screen.body_storage_ram.height) / 7, 220, elliptical_length_ram, 50)) def animation_clean(self, interval): #       "Memory boost"  # "Cache junk"  -. if int(self.tick) == 50: self.screen_junk.button_memory_bust_icon_state.source = \ "Data/Images/app_uninatall.png" elif int(self.tick) == 99: self.screen_junk.button_cache_junk_icon_state.source = \ "Data/Images/app_uninatall.png" #      STOP. self.screen_junk.button_stop.background_normal = \ "Data/Images/done_progress.png" self.screen_junk.button_stop.text = \ "CLEAN JUNK {}MB".format(self.tick) self.screen_junk.button_stop.color = [1.0, 1.0, 1.0, 1] #   Activity. self.set_new_color() self.screen_junk.background.rgb = self.new_color self.start_screen.background_action_bar.rgb = self.new_color #     . value = (self.tick * 100) / 100 print value self.screen_junk.progress_line.bar_value_percent = value self.screen_junk.progress_line.redraw() self.screen_junk.progress_label.text = \ "Scanning: org.package {}".format(self.scan_packages[self.tick]) self.animation_percent( self.screen_junk.layouts, self.animation_clean, iteration=100) def animation_percent(self, layout, callback, iteration=65): """   . :type layout: <class 'Libs.uix.startscreen.StartScreen'> and <class 'Libs.uix.junkfiles.JunkFiles'>; :param callback: animation_clean and calc_elliptical_length; """ self.tick += 1 if self.tick == iteration: self.set_default_tick_rgb() self.Clock.unschedule(callback) return numeral_one, numeral_two = divmod(self.tick, 10) if self.tick <= 34 or iteration != 65: layout.storage_numeral_one.source = \ "Data/Images/{}.png".format(int(numeral_one)) layout.storage_numeral_two.source = \ "Data/Images/{}.png".format(int(numeral_two)) try: layout.numeral_float.source = \ "Data/Images/{}.png".format(randint(1, 9)) except AttributeError: pass try: if self.tick <= 65: layout.ram_numeral_one.source = \ "Data/Images/{}.png".format(int(numeral_one)) layout.ram_numeral_two.source = \ "Data/Images/{}.png".format(int(numeral_two)) except AttributeError: pass def calc_elliptical_length(self, interval): """     Activity.""" elliptical_length = ((self.tick * 500) // 178) + 222 self.animation_storage_ram(elliptical_length) self.animation_percent( self.start_screen.layouts, self.calc_elliptical_length) def set_default_tick_rgb(self): """      Activity JUNK FILES.""" self.tick = 9 self.R = 41. self.G = 89. self.B = 173. def set_new_color(self): """      Activity JUNK FILES.""" self.R += 2 self.G += 1 self.B -= 1 self.new_color = self.R / 255., self.R / 255., self.B / 255. 

:



KivyCleanMasterDemo githab — https://github.com/HeaTTheatR/KivyCleanMasterDemo



')

Source: https://habr.com/ru/post/300960/


All Articles