The past two articles ("Kv Language Basics", "[Create a Calculator](http://qiita.com/dario_okazaki/items/" 910abf09344a60dd7823) ”) I think Kivy has made it possible to create an app with a simple screen. This time, we will create a multi-screen (2-screen) application.
This time, what we will create uses WebAPI to send and receive search conditions and their results. Lists the results so that you can see the detailed contents of the selected item. Below is the actual screen.
From the list view, drag the screen to the right or click the "Details" button on the action bar to slide the details screen from the side. No items are displayed because I have not searched.
This is the result of entering the conditions in the search form and pressing the "Search" button. A list of search results is displayed below the search form in a list of book titles. Click an item to automatically move to the details screen.
The book title and associated information are displayed on the details screen. If you drag the screen to the left or select the "List" button at the top right of the action bar, Return to the list screen
When you return to the list screen, you can see that the item selected from the list is in the selected state (red frame).
The contents to be newly learned are as follows.
However, this time we will use ListView because 1.9.2 has not been released and the specification of RecycleView is not clear. Similarly, Adapters is currently not recommended because it is under development, but we will use it because no alternative API has been presented.
I will give you a reference.
Creating Apps in Kivy - O'Reilly Media An introductory book to Kivy published by O'Reilly in 2014. From the basic usage of Kivy, use OpenWeatherMap (a service that provides weather information with API), skip requests, and display the results using getting and displaying the weather forecast for each city. We even export to mobile apps such as Android and IOS. If this book had been translated into Japanese, I think that there would have been no voice in Japan saying that Kivy was not well understood.
A video explanation of ListView.
This time, we will use the External API of the National Diet Library search. If you assemble and send a search query called CQL, the collection results will be returned. However, it is quite troublesome to assemble CQL from scratch, and this time I want to mainly handle the data sent and received as a result, so I will use this. The contents of the library are to assemble a CQL search query in Python and send and receive search results using the Python library requests to display the results.
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, materials such as fonts and images 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 code on the Python side is as follows.
main.py
#-*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, ListProperty
from kivy.uix.listview import ListItemButton
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.resources import resource_add_path
#National Diet Library Search Externally Provided Interface (API)
from pyndlsearch.client import SRUClient
from pyndlsearch.cql import CQL
#Change the default font
resource_add_path('./fonts')
#resource_add_path('/storage/emulated/0/kivy/calc/fonts')
LabelBase.register(DEFAULT_FONT, 'mplus-2c-regular.ttf') #Specify a Japanese font so that Japanese can be used
class BookButton(ListItemButton):
''' search_Make the result (ListView) item a button'''
book = ListProperty()
class SearchBookForm(BoxLayout):
search_input = ObjectProperty()
search_results = ObjectProperty() #search on the kv file side_Monitor results (ListView)
def __init__(self, **kwargs):
super(SearchBookForm, self).__init__(**kwargs)
def books_args_converter(index, data_item):
'''Convert the search result to dictionary type with book name as key.
It is called and executed for each record of the search result.
'''
title, creater , language, publisher = data_item
return {'book': (title, creater , language, publisher )}
def search_book(self):
'''Search based on search conditions and store the results in ListView'''
print('search_book')
cql = CQL()
#★ Enter search conditions
cql.title = self.search_input.text
year = self.ids['year'].text
month = self.ids['month'].text
day = self.ids['day'].text
cql.fromdate = year + '-' + month + '-' + day #Publication date
#cql.fromdate = '2000-10-10'
#print(cql.payload())
#cql.title = 'Python'
#cql.fromdate = '2000-10-10'
#NDL Search Client Settings
client = SRUClient(cql)
client.set_maximum_records(int(self.ids['number'].text)) #Maximum number of acquisitions
#★ End of search condition input
#client.set_maximum_records(10) #Maximum number of acquisitions
#print(client)
# get_response()Can be obtained in xml format
#res = client.get_response()
#print(res.text)
#Execute SRU (Execute search based on input conditions)
srres = client.get_srresponse()
#Store search results in books list
books = [(d.recordData.title, d.recordData.creator, d.recordData.language, d.recordData.publisher) for d in srres.records]
print(books)
print("----------------")
#Store in search results
self.search_results.adapter.data.clear() #Delete search results data (for detailed display)
self.search_results.adapter.data.extend(books) #Add search results to data
self.search_results._trigger_reset_populate() # search_results(list_view) refresh
class BookInfo(BoxLayout):
'''Details screen information'''
book = ListProperty(['', '','',''])
class BookSearchRoot(BoxLayout):
def __init__(self, **kwargs):
super(BookSearchRoot, self).__init__(**kwargs)
def show_book_info(self, book):
'''Format the selected information and move to the details screen to display'''
print('BookSearchRoot')
print(book) #Book = BookButton()For checking if the value of
#If None is entered in the Text of Label, an error will occur, so conversion is performed.
book_convs = [x if x != None else '' for x in book] #If None is returned""Change to
#Store book information on the details screen
self.bookinfo.book = book_convs
#Move to details screen
self.carousel.load_slide(self.bookinfo)
class BookSearchApp(App):
def __init__(self, **kwargs):
super(BookSearchApp, self).__init__(**kwargs)
self.title = 'National Diet Library Search'
pass
if __name__ == '__main__':
BookSearchApp().run()
The Kv Language is as follows.
bookSearch.kv
#: import main main
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#Widget displayed at startup
BookSearchRoot
<BookSearchRoot>
#List screen
carousel: carousel
booklists: booklists
bookinfo: bookinfo
BoxLayout:
orientation: "vertical"
ActionBar:
ActionView:
ActionPrevious:
title: "National Diet Library Search"
with_previous: False
app_icon: "./icon/32player.png "
ActionButton:
text: "List"
#Move to the list screen
on_press: app.root.carousel.load_slide(app.root.booklists)
ActionButton:
text: "Details"
#Go to the details screen
on_press: app.root.carousel.load_slide(app.root.bookinfo)
Carousel:
id: carousel
SearchBookForm: #List screen
id: booklists
BookInfo: #Details screen
id: bookinfo
<SearchBookForm>
#List screen layout
orientation: "vertical"
search_input: search_box #① Add a class variable. By doing this, self on the Python side.search_input can be taken
search_results: search_results_list
#Search form
BoxLayout:
height: "40dp"
size_hint_y: None
TextInput:
id: search_box #② You can pass the value with "①"
size_hint_x: 70
Button:
text: "Search"
size_hint_x: 30
on_press: root.search_book()
BoxLayout:
size_hint:1,.1
Label:
size_hint: .2,1
text: "Publication date"
Spinner: #List of years
id: year
size_hint: .1,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'2000'
values: [str(y) for y in range(2000, 2018) ]
Label:
size_hint: .05,1
text: "Year"
Spinner:
id: month #List of months
size_hint: .05,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'01'
values: ['{0:02d}'.format(x) for x in range(1,13)]
Label:
size_hint: .05,1
text: "Month"
Spinner: #List of days
id: day
size_hint: .05,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'01'
values: ['{0:02d}'.format(x) for x in range(1,30)] #Processing that changes the day every month is required, but temporarily suspended
Label:
size_hint: .05,1
text: "Day"
Label:
size_hint: .05,1
text: "number"
Spinner: #Monthly size
id: number
size_hint: .05,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'10'
values: ['1','5','10', '15', '20']
ListView:
id: search_results_list
adapter:
#List search results and use items as buttons
# data =Keep a list of searches in a list
# CLS =List display format (This time, it is displayed as a button)
# args_converter =Convert the display result to a list with the book name as the key.
ListAdapter(data=[], cls=main.BookButton, args_converter=main.SearchBookForm.books_args_converter)
<BookButton>
#Layout with search results as buttons
text_size: self.size
halign: 'left'
text: self.book[0] #Make the book name the title of the button
height: "40dp"
size_hint_y: None
on_press: app.root.show_book_info(self.book)
<BookInfo>
#search results
book: ("","","","")
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
size_hint_y: None
height: "40dp"
GridLayout:
cols: 2
rows: 4
Label:
text: "title"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text_size:self.size
halign: 'left'
valign: 'middle'
text:root.book[0]
size_hint_x: 80
Label:
text: "author"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text: root.book[1]
size_hint_x: 80
text_size:self.size
halign: 'left'
valign: 'middle'
Label:
text: "the publisher"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text: root.book[3]
size_hint_x: 80
#text: "the publisher:{} ".format(root.book[3])
text_size:self.size
halign: 'left'
valign: 'middle'
Label:
text: "language"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text: root.book[2]
size_hint_x: 80
text_size: self.size
halign: 'left'
valign: 'middle'
The discussion starts with Kv Language.
At startup, the "Book Search Root" widget is displayed. "Book Search Root" is roughly divided into two widgets.
The corresponding Kv is as follows.
The corresponding code is as follows.
```python
<BookSearchRoot>
#List screen
carousel: carousel
booklists: booklists
bookinfo: bookinfo
BoxLayout:
orientation: "vertical"
ActionBar:
ActionView:
ActionPrevious:
title: "National Diet Library Search"
with_previous: False
app_icon: "./icon/32player.png "
ActionButton:
text: "list"
# Move to the list screen
on_press: app.root.carousel.load_slide(app.root.booklists)
ActionButton:
text: "Details"
# Go to the details screen
on_press: app.root.carousel.load_slide(app.root.bookinfo)
Carousel:
id: carousel
SearchBookForm: # List screen
id: booklists
BookInfo: #Detail screen
id: bookinfo
###About Carousel
Carousel is a widget that switches screens (slides) by swiping. This time, 2 screens (SearchBookForm,BookInfo) is operated. The usage is operated using the widget name and id. You can also swipe the screen you want to show automatically by doing the following.
carousel.load_slide (id of the slide you want to display)
There are other properties such as changing the switching speed, so please refer to the API reference for details.
reference
Last time, "clear" was used to switch screens._widgets()Screen using(widget)Once deleted, "add"_widgets()Screen using(widget)is created. The problem with this method isIt is difficult to return to the previous screen. It is also difficult to keep the value because the widget is deleted. By using Carousel, you can switch between multiple screens while holding the value on each screen.
###Action bar
The action bar at the top of the screen is the following screen.
The corresponding code is as follows.
ActionBar:
ActionView:
use_separator: True
ActionPrevious:
title: "National Diet Library Search"
with_previous: False
app_icon: "./icon/32player.png "
ActionButton:
text: "list"
# Move to the list screen
on_press: app.root.carousel.load_slide(app.root.booklists)
ActionButton:
text: "Details"
# Go to the details screen
on_press: app.root.carousel.load_slide(app.root.bookinfo)
Of these, the title part on the left side of the screen, but this time App_A unique icon is specified for icon and displayed. As for the icon material, the size of the original Kivy icon is 32px x 32px, so I learned the size.
You can also move to each screen by pressing the button.
As you can see by running this program, even if you switch the list display or screen from the list to details, the ActionBar part will continue to be displayed without disappearing from the screen. this isThis is because BoxLayout is used to separate the ActionBar and Carousel parts. Screen switching and list display of search results are displayed on Carousel.
###About SearchBookForm
SearchBookForm can be roughly divided into two. One is the search form part for entering search conditions, and the other is the part for displaying a list of search results.
####About the search form part
The screen is as follows.
The Kv parts that make up the form are as follows.
# Search form
BoxLayout:
height: "40dp"
size_hint_y: None
TextInput:
id: search_box # ② You can pass the value with "①"
size_hint_x: 70
Button:
text: "Search"
size_hint_x: 30
on_press: root.search_book()
BoxLayout:
size_hint:1,.1
Label:
size_hint: .2,1
text: "Publishing date"
Spinner: #List of years
id: year
size_hint: .1,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'2000'
values: [str(y) for y in range(2000, 2018) ]
Label:
size_hint: .05,1
text: "year"
Spinner:
id: month # Month list display
size_hint: .05,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'01'
values: ['{0:02d}'.format(x) for x in range(1,13)]
Label:
size_hint: .05,1
text: "Moon"
Spinner: # List of days
id: day
size_hint: .05,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'01'
values: ['{0: 02d}'. format (x) for x in range (1,30)] # Processing that changes the day every month is required, but temporarily suspended
Label:
size_hint: .05,1
text: "day"
Label:
size_hint: .05,1
text: "Number"
Spinner: #Monthly size
id: number
size_hint: .05,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'10'
values: ['1','5','10', '15', '20']
Of these, TexInput()Was explained up to the last time in the character input part. By the way, it is a bug of Kivy that IME does not open by inputting characters on Windows OS. When searching in Japanese, copy and use the value entered in Notepad.
Next, I'm using "Spinner" this time to select a list of dates. Click on Spinner to display a list from which you can select items.
The corresponding Kv of the year is as follows.
Spinner: #List of years
id: year
size_hint: .1,1
halign: 'center'
valign: 'middle'
text_size: self.size
text:'2000'
values: [str(y) for y in range(2000, 2018) ]
The values you put in Values are listed.
Please refer to the API reference for details.
Spinner
####About the list screen display part
Next, the explanation of the part displaying the list of search results
。
The Kv parts that make up the form are as follows.
ListView:
id: search_results_list
adapter:
#List search results and make items buttons
# data = Keep a list of searches
# CLS = List display format (This time, it is displayed as a button)
# args_converter = Convert the display result to a list with the book name as the key.
ListAdapter(data=[], cls=main.BookButton, args_converter=main.SearchBookForm.books_args_converter)
First, let's talk about ListView.
ListView is a widget for displaying data in list format. For simple usage, see "item"_Enter the list structure in a property called "strings". For example, in the case of the following code, item_The strings will contain consecutive numbers from 0 to 100, and when executed, the items from 0 to 100 will be displayed in Label format.
class MainView(ListView):
def __init__(self, **kwargs):
super(MainView, self).__init__(
item_strings=[str(index) for index in range(100)])
However, since it is only a Label that is displayed in this format, it cannot be selected and operated. So this time,AdaptersUse the method called to change the list to a button display.
ListAdapter(data=[], cls=main.BookButton, args_converter=main.SearchBookForm.books_args_converter)
The explanation of the arguments is as follows.
+data: Keeps search result information in a list
I will explain it by comparing it with the file on the Python side of each item.
data holds the value of the search result This time, when you search, the following tuples will be returned.
The corresponding code is as follows.
def search_book(self):
'''Search based on search conditions and store the results in ListView'''
~ Omitted ~
# SRU execution (search based on input conditions)
srres = client.get_srresponse()
#Store search results in books list
books = [(d.recordData.title, d.recordData.creator, d.recordData.language, d.recordData.publisher) for d in srres.records]
print(books)
print("----------------")
#Store in search results
self.search_results.adapter.data.clear () # Clear search results data (for detailed display)
self.search_results.adapter.data.extend (books) # Add search results to data
Refresh self.search_results._trigger_reset_populate () # search_results (list_view)
The values stored in the books list are:
[('Python introductory class to remember in 10 days','Mikio Hogari, Manabu Terata, Naoki Nakanishi, Naoki Hotta, Takashi Nagai',' jpn','Shoeisha'),
~ Omitted ~
('Operated with a robot-specific tool that controls Bluetooth-connected toys Automated with Python', None,'jpn','')]
The previous result of data is deleted and newly placed in the corresponding place below. afterwards"_trigger_reset_The display is updated with "populate".
self.search_results.adapter.data.clear () # Clear search results data (for detailed display)
self.search_results.adapter.data.extend (books) # Add search results to data
Refresh self.search_results._trigger_reset_populate () # search_results (list_view)
def books_args_converter(index, data_item):
'''Convert search results to dictionary type with book name as key.
It is called and executed for each record of the search result.
''
title, creater , language, publisher = data_item
return {'book': (title, creater , language, publisher )}
args_converter uses the book information of the search result as a key and args_It is stored in the converter.
class BookButton(ListItemButton):
'''Use the search_results (ListView) item as a button'''
book = ListProperty()
cls sets the display format of the list. This time, the book information is converted into a List, stored in the book, and displayed on the Button. In addition, cls is probably an abbreviation of The clear screen and is used to mean screen erase and redisplay.
Regarding the display format, use the following properties on the Kv side.
<BookButton>
#Layout with search results as buttons
text_size: self.size
halign: 'left'
text: self.book [0] # Make the book name the title of the button
height: "40dp"
size_hint_y: None
on_press: app.root.show_book_info(self.book)
The concept of Adpter is difficult to use and I don't fully understand it, but "data",「cls」,「args_Once you know that you can change the list display format setting by setting a value in "converter", it's okay.
reference
###About "Book Info" widget
This is a description of the details screen.
The screen is as follows.
The Kv file is as follows.
<BookInfo>
# search results
book: ("","","","")
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
size_hint_y: None
height: "40dp"
GridLayout:
cols: 2
rows: 4
Label:
text: "Title"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text_size:self.size
halign: 'left'
valign: 'middle'
text:root.book[0]
size_hint_x: 80
Label:
text: "author"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text: root.book[1]
size_hint_x: 80
text_size:self.size
halign: 'left'
valign: 'middle'
Label:
text: "Publisher"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text: root.book[3]
size_hint_x: 80
#text: "Publisher: {}" .format (root.book [3])
text_size:self.size
halign: 'left'
valign: 'middle'
Label:
text: "language"
halign: 'left'
valign: 'middle'
size_hint_x: 20
text_size:self.size
Label:
text: root.book[2]
size_hint_x: 80
text_size: self.size
halign: 'left'
valign: 'middle'
Click the "Book Button" widget to main.show_book_info()Is executed. show_book_info() Formats the book information and displays the "BookInfo" widget (detail screen).
I'm not doing anything particularly difficult here, just displaying the results. The only new thing I'm doing is aligning the characters to the left edge and wrapping them.
Label:
text_size:self.size
halign: 'left'
valign: 'middle'
text:root.book[0]
size_hint_x: 80
If you leave the default text of Label, if it is a long line, it will be displayed on one line beyond Label, so "text"_"size" specifies the size as the size of the label. Then use "halign" to left-justify the horizontal display and "valign" to display the vertical display in the middle.
reference
#Summary Now you know how to display multiple screens, search based on multiple input items, list results, and display details for each item. Until this time, I think you have somehow understood how to make a desktop application that uses buttons.
Next time, I will try to display it on an Android device based on this program.
By the way, it was quite difficult because the program had to be changed significantly from here to display on Android.
#Reference: About file packaging If you want to use Kivy to package your files, you can use PyInstaller to package them. Separately if you want to exe in windowsPyWin32Must be installed.
reference
#Continuation of this content I posted a new article.