By using the previous "Kv Language Basics", you should have somehow understood the basic usage of Kivy. This time, we will actually create a simple app to deepen our understanding.
What you create is a calculator app. Enter numbers and operators with the buttons and press the "=" button to display the calculation results. If you press switch again, the design will change.
The contents to be newly learned are as follows
Creating a calculator app in Kivy is often argued in overseas tutorials.
I will give you a reference.
There are various ways to write code using Kivy. This is just an example. Also, the source code used when writing this article is listed on Github. However, fonts are not placed, so please prepare and place them yourself if necessary.
The verification environment is as follows.
OS: Windows10 64bit Kivy:1.9.1
Python3.4※
Below, I will actually post the code and results.
The execution result is as follows.
When you start it, the screen will be displayed. The same operation as a normal calculator is possible. * 1 Press the switch button on one bar to switch to the following design * 2
The program is as follows.
main.py
# -*- coding: utf-8 -*
from kivy.config import Config
Config.set('graphics', 'width', '400')
Config.set('graphics', 'height', '800')
import kivy
kivy.require('1.9.1')
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.core.window import Window
from kivy.properties import BooleanProperty
from kivy.utils import get_color_from_hex
from kivy.resources import resource_add_path
from kivy.factory import Factory
#from kivy.core.window import Window
#Window.size = (450, 600)
#Change the default font
resource_add_path('fonts')
LabelBase.register(DEFAULT_FONT, 'mplus-2c-regular.ttf') #Specify a Japanese font so that Japanese can be used
class Calculator1(BoxLayout):
clear_bool = BooleanProperty(False)
def print_number(self, number):
'''Enter the entered value'''
if self.clear_bool:
self.clear_display()
text = "{}{}".format(self.display.text, number) #The character string entered so far and the value entered are displayed.
self.display.text = text
print("Number "{0}Was pressed".format(number))
def print_operator(self, operator):
if self.clear_bool:
self.clear_bool = False
text = "{} {} ".format(self.display.text, operator)
self.display.text = text
print("operator"{0}Was pressed".format(operator))
def print_point(self, operator):
# 「.Processing when "" is pressed
print("(Unimplemented) Operator "{0}Was pressed".format(operator))
def clear_display(self):
self.display.text = ""
self.clear_bool = False
print(""C" was pressed")
def del_char(self):
''' "<x"Display the calculation result when is pressed'''
self.display.text = self.display.text[:-1]
print("「<x "was pressed")
def calculate(self):
''' "="Display the calculation result when is pressed'''
try:
self.display.text = str(eval(self.display.text)) #Evaluate a single expression Example: eval("5 + 10")Becomes 15
self.clear_bool = True
print('Calculation completed')
except:
#Without entering numbers'=Error countermeasures such as when pressing ’
print('error Typing error')
#class Calculator2(BoxLayout):
# def __init__(self, **kwargs):
# super(Calculator2, self).__init__(**kwargs)
class CalculatorRoot(BoxLayout):
def __init__(self, **kwargs):
super(CalculatorRoot, self).__init__(**kwargs)
def change_calc(self):
self.clear_widgets()
self.add_widget(Calculator1())
def change_calc2(self):
self.clear_widgets()
calc2 = Factory.Calculator2()
#calc2 = Calculator2()
self.add_widget(calc2)
class CalculatorApp(App):
def __init__(self, **kwargs):
super(CalculatorApp, self).__init__(**kwargs)
self.title = 'calculator'
pass
if __name__ == "__main__":
Window.clearcolor = get_color_from_hex('#FFFFFF')
CalculatorApp().run()
The Kv file is as follows.
calculator.kv
#: import get_color_from_hex kivy.utils.get_color_from_hex
CalculatorRoot
<CalculatorRoot>
Calculator1
id: calculator1
<calculator1>
#Layout of the entire screen
orientation: "vertical" #Place objects horizontally
display: display_input
ActionBar:
ActionView:
ActionPrevious:
title: 'calculator'
with_previous: False #If true, the logo on the head becomes a button?
ActionButton:
text: 'switching'
on_press: print("push ");app.root.change_calc2()
# ActionButton:
# text: 'sound'
# icon: 'atlas://data/images/defaulttheme/audio-volume-high'
# ActionGroup: #Group buttons
# text: 'type'
# ActionButton:
# text: 'Scientific calculator'
# ActionButton:
# text: 'programmer'
TextInput: #Number display part
id: display_input
size_hint_y: 1 #Vertical size is 1 for the whole/5.Display at a rate of 5
font_size: 60
hint_text: '0'
Keyboard: #Display part of the taen key
size_hint_y: 3.5 #Vertical size of the whole 3.5/4.Display at a rate of 5
<Keyboard@GridLayout>: # class Keyboard(GridLayout):Same meaning as
#Create a button with 4 columns x 5 rows
cols: 4
rows: 5
spacing: 2
padding: 4
#1st row
ClearButton: #Button type
text: "C" #Button display name
CalcButton:
text: "%"
DelButton:
text: "<x"
OperatorButton:
text: "/"
#2nd row
NumberButton:
text: "7"
NumberButton:
text: "8"
NumberButton:
text: "9"
OperatorButton:
text: "*"
#3rd row
NumberButton:
text: "4"
NumberButton:
text: "5"
NumberButton:
text: "6"
OperatorButton:
text: "-"
#4th row
NumberButton:
text: "1"
NumberButton:
text: "2"
NumberButton:
text: "3"
OperatorButton:
text: "+"
#5th row
CalcButton:
text: "+/-"
NumberButton:
text: "0"
CalcButton:
text: "."
EqualButton:
text: "="
<ButtonFormat@Button>:
font_size: '30dp'
background_normal: ''
background_down: ''
background_color: get_color_from_hex("#83481F")
on_press: self.background_color = get_color_from_hex("#825534")
on_release: self.background_color = get_color_from_hex("#823600")
<NumberButton@ButtonFormat>:
#font: "Roboto"
font_size: '30dp'
bold: True
on_press: app.root.ids['calculator1'].print_number(self.text) #When the button is pressed main.py's Calculator class print_number()Is executed
<OperatorButton@ButtonFormat>:
on_press: app.root.ids['calculator1'].print_operator(self.text)
<ClearButton@ButtonFormat>:
on_press: app.root.ids['calculator1'].clear_display()
<DelButton@ButtonFormat>:
on_press: app.root.ids['calculator1'].del_char()
<EqualButton@ButtonFormat>:
on_press: app.root.ids['calculator1'].calculate()
<CalcButton@ButtonFormat>: #Will display decimal point but not implemented
on_press: app.root.ids['calculator1'].print_point(self.text)
<Calculator2@BoxLayout>
id: calculator2
orientation: "vertical" #Place objects horizontally
display: display_input
ActionBar:
ActionView:
ActionPrevious:
title: 'Calculator 2'
with_previous: False #If true, the logo on the head becomes a button?
ActionOverflow:
ActionButton:
text: 'switching'
on_press: print("push2 ");app.root.change_calc()
TextInput: #Number display part
id: display_input
size_hint_y: 1 #Vertical size is 1 for the whole/4.Display at a rate of 5
font_size: 60
Keyboard2: #Display part of the taen key
size_hint_y: 3.5 #Vertical size of the whole 3.5/4.Display at a rate of 5
<Keyboard2@GridLayout>: # class Keyboard(GridLayout):Same meaning as
#Create a button with 4 columns x 5 rows
cols: 4
rows: 5
spacing: 2
padding: 4
#1st row
ClearButton2: #Button type
text: "Delete" #Button display name
CalcButton2:
text: "Surplus"
DelButton2:
text: "<x"
OperatorButton2:
text: "quotient"
#2nd row
NumberButton2:
text: "Seven"
calc_val: '7'
NumberButton2:
text: "Eight"
calc_val: '8'
NumberButton2:
text: "Nine"
calc_val: '9'
OperatorButton2:
text: "product"
#3rd row
NumberButton2:
text: "four"
NumberButton2:
text: "Five"
NumberButton2:
text: "Six"
OperatorButton2:
text: "difference"
#4th row
NumberButton2:
text: "one"
NumberButton2:
text: "two"
NumberButton2:
text: "three"
OperatorButton2:
text: "sum"
#5th row
CalcButton2:
text: "+/-"
NumberButton2:
text: "zero"
CalcButton2:
text: "."
EqualButton2:
text: "="
<ButtonFormat2@Button>:
font_size: '30dp'
background_normal: ''
background_down: ''
background_color: get_color_from_hex("#398133")
on_press: self.background_color = get_color_from_hex("#73FF66")
on_release: self.background_color = get_color_from_hex("#52824E")
<NumberButton2@ButtonFormat2>:
bold: True
<OperatorButton2@ButtonFormat2>:
<ClearButton2@ButtonFormat2>:
<DelButton2@ButtonFormat2>:
<EqualButton2@ButtonFormat2>:
<CalcButton2@ButtonFormat2>:
The basic explanation of Kv Language was given last time, so I will mainly explain the parts that could not be explained last time. Let's start with the Kv file.
At startup, the Calculator Root of the rootWidget is set to calculator. Among them, calculator1 has the following code.
<calculator1>
#Layout of the entire screen
orientation: "vertical" #Place objects horizontally
display: display_input
ActionBar:
ActionView:
ActionPrevious:
title: 'calculator'
with_previous: False #If true, the logo on the head becomes a button?
ActionOverflow:
ActionButton:
text: 'switching'
on_press: print("push ");app.root.change_calc2()
TextInput: #Number display part
id: display_input
size_hint_y: 1 #Vertical size is 1 for the whole/4.Display at a rate of 5
font_size: 60
hint_text: '0'
Keyboard: #Display part of the taen key
size_hint_y: 3.5 #Vertical size of the whole 3.5/4.Display at a rate of 5
calculator1 is the main layout, of which TextInput is the display area and Keyboard is the keyboard part, which is the main layout. The value of "size_hint_y" of TextInput is "1" and the value of "size_hint_y" of Keyboard is "3.5", and the vertical display area of Keyboard is arranged according to this ratio with TextInput.
TextInput is a wighet that allows you to enter characters. This time we are using the property hint_text: '0'
. hint_text:
can set the display of characters when not entered.
reference
Kewbord is a custom widget created based on GridLayout.
<Keyboard@GridLayout>:
Reprinted from the official website
GirdLayout is a widget that allows you to place widgets on a grid.
<Keyboard@GridLayout>: # class Keyboard(GridLayout):Same meaning as
#Create a button with 4 columns x 5 rows
cols: 4
rows: 5
As a basic usage, "cols" sets the number of cells in the horizontal direction, and "rows" sets the number of cells in the vertical direction. It is not necessary to set both, it is possible to set only "cols". This time I used it to create a keyboard. Also, although not used this time, you can use "size_hint" for each cell, so you can adjust the size of the grid individually. The same layout can be achieved by using BoxLayout in combination, so it depends on your personal preference whether to use GildLayout or BoxLayout.
reference
ActoonBar can display titles and icons, and add buttons, just like Android's Action Bar. Mainly set at the top and bottom of the screen
ActionBar:
ActionView:
ActionPrevious:
title: 'calculator'
with_previous: False #Back button if true
ActionOverflow:
ActionButton:
text: 'switching'
on_press: print("push ");app.root.change_calc2()
ActionButton:
text: 'sound'
icon: 'atlas://data/images/defaulttheme/audio-volume-high'
ActionGroup: #Group buttons
text: 'type'
ActionButton:
text: 'Scientific calculator'
ActionButton:
text: 'programmer'
ActionView is a class based on BoxLayout, and uses it to arrange buttons and so on. ActionPrevious sets the title of the app and the back button. If you set the name to the "title" property and the property of "with_previous" to "True", you can return to the previous page by clicking the kivy icon. However, to use the back function, you need to create multiple screens with slide etc. The icon image can be changed by specifying the "app_icon" parameter. Use "Action Button" to add a button. You can change the image with the "icon" parameter. * "Atlas" is used to specify the icon image. "Atlas" is explained in the next section.
Use Action Group to group buttons. The execution result is as follows.
reference
Atlas is a collection of multiple images into a single image. It is a technology often used in games and CG, and is used to save memory and shorten loading. Detailed explanation can be found by searching for "Unity Atlas" etc.
kivy also uses atlas, and the default buttons and on / off switches all use this mechanism. Kivy's atlas is realized by a file in atlas format (extension .atlas) and a png file specified inside it. The atlas file looks like this in json format:
python
{
"<basename>-<index>.png ": {
"id1": [ <x coordinate>, <y coordinate>, <width>, <height> ],
"id2": [ <x coordinate>, <y coordinate>, <width>, <height> ],
# ...
},
# ...
}
The actual file is as follows.
kivy/data/images/defaulttheme.atlas
defaulttheme.atlas
{
"defaulttheme-0.png ": {
"action_bar": [
158,
133,
36,
36
],
"action_group": [
452,
171,
33,
48
],
"action_group_disabled": [
2,
121,
33,
48
],
~ Omitted ~
"audio-volume-high": [
464,
406,
48,
48
],
~ Omitted ~
}
}
defaulttheme-0.png
Although it is atlas, I think that it is not often prepared and used by myself when creating a desktop application as a personal impression. The reason is that it has the advantage of faster memory and loading, but on the other hand it is troublesome to manage files, and even from the standard specifications of current PCs, it reads from individual image files without using atlas. I think that it is enough for actual operation even if it is used in. Unless you want to load a small image for an RPG map in tiles on the entire screen or display a large number of images like a barrage of a shooting game, you do not need to use it.
reference
ActionButton:
text: 'switching'
on_press: print("push ");app.root.change_calc2()
Looking at the command at the time of on_press, there is a ";" after the print command. You can execute multiple commands by writing ";". You can also align the lines with a line break + space after the ";".
on_press:
app.ans = eval(input.text);
input.text = str(app.ans)
#: import get_color_from_hex kivy.utils.get_color_from_hex
background_color: get_color_from_hex("#83481F")
on_press: self.background_color = get_color_from_hex("#825534")
on_release: self.background_color = get_color_from_hex("#823600")
You can get the color from hexadecimal notation by using get_color_from_hex (). I import from get_color_from_hex () kivy.utils, but not on the Python side.
#: import get_color_from_hex kivy.utils.get_color_from_hex
By writing, you can import on the Kv file side.
reference
Next, I will explain the Python file side. The Python file side can be almost explained with the contents of the previous basic explanation. There is only one new element. It's a widget switch.
The following is an excerpt of the applicable code below.
from kivy.factory import Factory
class Calculator1(BoxLayout):
clear_bool = BooleanProperty(False)
def print_number(self, number):
~ Omitted ~
#class Calculator2(BoxLayout):
# def __init__(self, **kwargs):
# super(Calculator2, self).__init__(**kwargs)
class CalculatorRoot(BoxLayout):
def change_calc(self):
self.clear_widgets()
self.add_widget(Calculator1())
def change_calc2(self):
self.clear_widgets()
calc2 = Factory.Calculator2()
#calc2 = Calculator2()
self.add_widget(calc2)
The kv file is as follows
<Calculator2@BoxLayout>
~ Omitted ~
It is a function in change_calc (), clear_widgets (), which removes all applicable child widgets. Use remove_widget ('widget name') to specify and remove only a specific widget. Conversely, you can add a widget by specifying add_widget ('widget name'). change_calc () once deletes all widgets belonging to CalculatorRoot (clears the screen) and adds a new widget of Calculator1 class.
Next is change_calc2 (). Similarly, the screen is cleared and a new widget for Calculator2 is added, but there is a big difference from change_calc (). That is ** the Calculator1 class is defined in the Python file, but the Calculator2 class is not defined on the Python side. It is defined on the kv file side **.
self.add_widget(Calculator2())
If you execute Calculator2 () directly like Calculator1 (), an error will occur at startup.
from kivy.factory import Factory
calc2 = Factory.Calculator2()
Therefore, we will use the Factory class this time. The Factory class allows you to ** automatically register any class or module and instantiate a class anywhere in your project. Therefore, you can use the widget defined in the kv file. ** **
Using this method, if you want to use only the screen and do not need to implement the function, you do not need to write the widget in the Python file, so you can write the program concisely. In this case, if you do not use the Factory class, you need to declare the Calculator2 () class in the Python file and declare initialization etc. as commented out.
class Calculator2(BoxLayout):
def __init__(self, **kwargs):
super(Calculator2, self).__init__(**kwargs)
reference
What happens when multiple modules each define a widget with the same name in KvLanguage It contains a discussion of Factory and the widgets that are set at startup.
In addition, although it is a method of switching screens, there is a method of using Carousel or Screen Manager after that. As for Carousel, some people have actually done it, so I will list it for reference. I will explain how to use ScreenManager next time.
reference
I think that this time it will be possible to create an application that ends with a simple single screen. Next time, I will explain how to use WebAPI in Kivy and screen transition using ScreenManager.
New content has been added.
Recommended Posts