MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

PythonのGUIライブラリKivyによるクロスプラットフォームGUIアプリ作成入門

目次

はじめに

先日、

PythonのマルチプラットフォームGUIライブラリである

Kivyを紹介しましたが、

myenigma.hatenablog.com

myenigma.hatenablog.com

今回は、Kivyによる基本的なGUIシステムの

作り方を紹介したいと思います。

 

また、この記事で紹介するすべてのサンプルコードは

下記のGitHubリポジトリでも公開されています。

github.com

 

また、下記のそれぞれのサンプルコードを、

Mac, Ubuntu, Windows, iOS(iPhone)で実行した

スクリーンショットも貼りました。

 

Windowを作る

f:id:meison_amsl:20160507210428p:plain

f:id:meison_amsl:20160507215726p:plain

f:id:meison_amsl:20160511184747j:plain

 

もっとシンプルなサンプルとしては、

下記のコードで、上記のような

ウインドウタイトルと一つのラベルのみの

Windowを作ることができます。

from kivy.app import App
from kivy.uix.button import Label
from kivy.config import Config

#Window sizeの設定
Config.set('graphics', 'width', '200')
Config.set('graphics', 'height', '200')

class TestApp(App):
    def build(self):
        self.title = 'Window Sample'
        self.icon = 'images.jpg'
        return Label(text='Hello World')

TestApp().run()

また、iconメンバーに画像データを設定することで、

アプリのアイコンを設定することもできます。

(なぜかubuntuではアイコンがPythonのアイコンのままでした。。)

f:id:meison_amsl:20160507210557p:plain

 

アクションバーを作る

f:id:meison_amsl:20160508093309p:plain

f:id:meison_amsl:20160508095552p:plain

f:id:meison_amsl:20160511212647j:plain

f:id:meison_amsl:20160508095602p:plain

 

下記のコードを実行すると、

PyQtのメニューバーに似た、

アクションバーを追加することができます。

デザインが結構かっこいいなと思います。

よく使う機能などをこのアクションバーに割り当てると便利です。

 

from kivy.app import App
from kivy.uix.button import Label,Button
from kivy.config import Config
from kivy.uix.actionbar import ActionBar, ActionItem, ActionButton, ActionView, ActionPrevious, ActionGroup
from kivy.uix.floatlayout import FloatLayout

Config.set('graphics', 'width', '200')
Config.set('graphics', 'height', '200')

class MyActionBar():
    def __init__(self):

        actionview = ActionView()
        actionview.use_separator=True
        ap = ActionPrevious(title='Action Bar', with_previous=False) 
        actionview.add_widget(ap) 
        self.abtn1=ActionButton(text="Btn1")
        self.abtn1.bind(on_press=self.ActionBtn1Callback)
        actionview.add_widget(self.abtn1) 
        self.abtn2=ActionButton(text="Btn2")
        self.abtn2.bind(on_press=self.ActionBtn2Callback)
        actionview.add_widget(self.abtn2) 

        self.abtn3=ActionButton(text="Btn3",icon="images.jpg")
        self.abtn3.bind(on_press=self.ActionBtn3Callback)
        actionview.add_widget(self.abtn3) 

        group1=ActionGroup()

        self.abtn4=ActionButton(text="Btn4")
        self.abtn4.bind(on_press=self.ActionBtn4Callback)
        group1.add_widget(self.abtn4)

        self.abtn5=ActionButton(text="Press Me!!!!")
        self.abtn5.bind(on_press=self.ActionBtn5Callback)
        group1.add_widget(self.abtn5)

        actionview.add_widget(group1)

        self.actionbar=ActionBar()
        self.actionbar.add_widget(actionview)


    def ActionBtn1Callback(self,instance):
        print("Btn1 press!!")

    def ActionBtn2Callback(self,instance):
        print("Btn2 press!!")

    def ActionBtn3Callback(self,instance):
        print("Btn3 press!!")

    def ActionBtn4Callback(self,instance):
        print("Btn4 press!!")

    def ActionBtn5Callback(self,instance):
        print("Btn5 press!!")


class RootWidget(FloatLayout):
    def __init__(self, **kwargs):
        # make sure we aren't overriding any important functionality
        super(RootWidget, self).__init__(**kwargs)

        # let's add a Widget to this layout
        self.add_widget(
            Button(
                text="Hello World",
                size_hint=(.5, .5),
                pos_hint={'center_x': .5, 'center_y': .5}))

        self.myactionbar=MyActionBar()

        self.add_widget(
            self.myactionbar.actionbar
            )

class TestApp(App):
    def build(self):
        self.root = root = RootWidget()
        self.title = 'Window Sample'
        self.icon = 'images.jpg'
        return self.root

TestApp().run()

 

テキスト入力ボックスを作る

f:id:meison_amsl:20160508162858p:plain

f:id:meison_amsl:20160508164804p:plain

 

下記のコードを実行すると、

テキストボックス入力のUIを作ることができます。

 

下記のサンプルでは、

Clock.schedule_intervalを使って、

1秒毎に時刻のデータをテキストボックスに描画するようにしています。

 

残念ながら、

一行だけのテキストボックスを作る方法がわかりませんでした。

 

#!/usr/bin/python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.button import Label,Button
from kivy.config import Config
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from datetime import datetime

#Window sizeの設定
Config.set('graphics', 'width', '400')
Config.set('graphics', 'height', '400')

class RootWidget(BoxLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(padding=30, orientation='vertical')

        self.label=Label(text="Time Display")
        self.add_widget(self.label)

        Clock.schedule_interval(self.TimerCallback, 1.0)

        self.textinput = TextInput(text='Hello world', multiline=False)
        self.add_widget(self.textinput)

    def TimerCallback(self,dt):
        time=datetime.now().strftime("%Y/%m/%d %H:%M:%S")
        self.textinput.text=time

class TestApp(App):
    def build(self):
        self.root = RootWidget()
        self.title = 'Text Input Sample'
        return self.root

TestApp().run()

 

ボタンのUIを作る

f:id:meison_amsl:20160511212502p:plain

 

下記がボタンを複数配置したUIのサンプルコードです。

boxlayoutで4つのAnchorlayoutを設定し、

その真ん中にAnchorlayoutの半分の大きさの

ボタンを配置しました。

 

#!/usr/bin/python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.button import Label,Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.core.window import Window

class RootWidget(GridLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(cols=2)

        self.btn1=Button(text="Btn1",size_hint = (0.5, 0.5))
        layout = AnchorLayout(anchor_x='center', anchor_y='center')
        layout.add_widget(self.btn1)
        self.add_widget(layout)

        self.btn2=Button(text="Btn2",size_hint = (0.5, 0.5))
        layout2 = AnchorLayout(anchor_x='center', anchor_y='center')
        layout2.add_widget(self.btn2)
        self.add_widget(layout2)

        self.btn3=Button(text="Btn3",size_hint = (0.5, 0.5))
        layout3 = AnchorLayout(anchor_x='center', anchor_y='center')
        layout3.add_widget(self.btn3)
        self.add_widget(layout3)

        self.btn4=Button(text="Btn4",size_hint = (0.5, 0.5))
        layout4 = AnchorLayout(anchor_x='center', anchor_y='center')
        layout4.add_widget(self.btn4)
        self.add_widget(layout4)
 
    def on_checkbox_active(self, checkbox, value):
        if value:
            print('The checkbox', checkbox, 'is active')
        else:
            print('The checkbox', checkbox, 'is inactive')
       
class TestApp(App):
    def build(self):
        self.root = RootWidget()
        self.title = 'Text Input Sample'
        return self.root

TestApp().run()

 

スライダーUIを作る

f:id:meison_amsl:20160508170536p:plain

f:id:meison_amsl:20160508170820p:plain

 

下記がスライダーUIを使ったサンプルコードです。

スライダーが移動された時に、

Callback関数を呼んで、

Labelにスライダーの値を表示させています。

 

from kivy.app import App
from kivy.uix.button import Label,Button
from kivy.config import Config
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

#Window sizeの設定
Config.set('graphics', 'width', '200')
Config.set('graphics', 'height', '200')

class RootWidget(BoxLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(padding=50)

        self.label=Label(text="Please slide the slider")
        self.add_widget(self.label)

        self.slider = Slider(min=-100, max=100, value=25)
        self.slider.bind(value=self.callback)
        self.add_widget(self.slider)

    def callback(self,instance,value):
        self.label.text=str(value)

class TestApp(App):
    def build(self):
        self.root = root = RootWidget()
        self.title = 'Slider Sample'
        return self.root

TestApp().run()

 

チェックボックスのUIを作る

f:id:meison_amsl:20160510220923p:plain

f:id:meison_amsl:20160511212619j:plain  

下記はチェックボックスを作るサンプルコードです。

checkbox.valueの値から、

チェックボックスの状態を確認することができます。

 

#!/usr/bin/python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.button import Label,Button
from kivy.config import Config
from kivy.uix.gridlayout import GridLayout
from kivy.uix.checkbox import CheckBox
from kivy.core.window import Window
Window.clearcolor = (0.7, 0.7, 0.7, 0.7)

class RootWidget(GridLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(cols=2)

        self.label=Label(text="Check 1")
        self.add_widget(self.label)

        self.checkbox = CheckBox()
        self.checkbox.bind(active=self.on_checkbox_active)
        self.add_widget(self.checkbox)

        self.label=Label(text="Check 2")
        self.add_widget(self.label)

        checkbox1 = CheckBox()
        self.add_widget(checkbox1)

        self.label=Label(text="Check 3")
        self.add_widget(self.label)

        checkbox3 = CheckBox()
        self.add_widget(checkbox3)
 
    def on_checkbox_active(self, checkbox, value):
        if value:
            print('The checkbox', checkbox, 'is active')
        else:
            print('The checkbox', checkbox, 'is inactive')
       
class TestApp(App):
    def build(self):
        self.root = RootWidget()
        self.title = 'Text Input Sample'
        return self.root

TestApp().run()

 

matplotlibのグラフをkivyの一部として利用する

f:id:meison_amsl:20160508211257p:plain

 

pythonのグラフ作成ライブラリmatplotlibは

グラフ作成に非常に便利です。

myenigma.hatenablog.com

 

このkivyのアドオン管理ツールである、

kivy gardenを使うことにより、

github.com

簡単にmatplotlibのグラフをkivyのwidgetの一部として

GUIツールの一部に組み込むことができます。

 

まず初めに、下記のコマンドでkivy gardenをインストールし、

$ pip install kivy-garden

gardenコマンドでmatplotlibとkivyを繋ぐアドオンをインストールします。

$ garden install matplotlib

あとは、下記のサンプルを起動すると、

冒頭のようなmatplotlibのグラフを

kivyの一部として組み込むことができます。

 

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.config import Config
import numpy as np
import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivy')
import matplotlib.pyplot as pl
import sys, os

class GraphView(BoxLayout):
    def __init__(self, **kwargs):
        super(GraphView, self).__init__(orientation='vertical')
        self.add_widget(Label(text="embeded matplotlib sample"))
        self.add_widget(self.graph_plot_sample())

    def graph_plot_sample(self):
        self.fig, ax = pl.subplots()
        x = np.linspace(-np.pi, np.pi)
        y = np.sin(x)
        ax.set_xlabel("X label")
        ax.set_ylabel("Y label")
        ax.grid(True)
        ax.plot(x, y)
        return self.fig.canvas

class GraphApp(App):
    def build(self):
        height = 300
        Config.set('graphics', 'height', height)
        Config.set('graphics', 'width', height * 2)
        return GraphView()

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

 

下記のコードを参考にさせて頂きました。

https://github.com/Himenon/SampleKivy/tree/master/matplotlibgithub.com

 

二次元の地図データを埋め込む

f:id:meison_amsl:20160513203525p:plain

 

下記のgardenコマンドでmapviewをインストールすると、

$ pip install futures requests

$ garden install mapview

二次元の地図のデータを

kivyアプリに入れることができます。

 

from kivy.garden.mapview import MapView
from kivy.app import App

class MapViewApp(App):
    def build(self):
        mapview = MapView(zoom=15, lat=35.681382, lon=139.766084)
        return mapview

MapViewApp().run()

下記のリンクのように地図にピンを打つこともできます。

qiita.com

 

ちなみにGoogle Earthにピンを打ちたい場合は

こちらのツールが便利だと思います。

myenigma.hatenablog.com

 

参考資料

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

MyEnigma Supporters

もしこの記事が参考になり、

ブログをサポートしたいと思われた方は、

こちらからよろしくお願いします。

https://gumroad.com/l/myenigmasupportersgumroad.com