pybind11ことはじめ

pybind11ことはじめ

目次

1 概要

  • C++11をPythonで利用しやすくするツールの一種
    • ヘッダーファイルのみで構成されている
    • DlibのPythonとの連携で使われていて調べた

2 関連文書

3 環境構築(Dockerを利用して)

  • 色々なOS(Windows,Mac,Linux)で、同じ操作で出来るように、Dockerを利用して環境を作る
  • Docker無しで、必要なツールをインストールして直接やってもOK(その場合この章はスキップ)
    • 必要なツール
      • C++のコンパイラ
      • CMake
      • GNU Make
      • Python とヘッダーファイル、ライブラリファイル
      • Python のライブラリ
        • pip
        • pybind11
    • あった方が良いもの
      • git
      • Python のライブラリ
        • ipython

3.1 以下の操作を行っている動画


3.2 環境構築(Dockerを利用して)の手順

3.2.1 事前準備

  • OSに依存せずに同じ環境作れるようにDockerを利用してこの動画シリーズやっていきます。
  • まずDockerをインストールしてください。
    • 私はUbuntu20.04の環境で行って行きます。DockerはWindowsやMacでも使えるので、他のOSでもDockerを入れれば同じ手順で出来ます。
  • Docker等でUbuntuの環境を用意
    • Python,Python-dev,c++コンパイラー,cmake,git等必要なパッケージインストール
  • 以下のコマンドで、共有用のディレクトリ作成し、user idを1000にしておく
sudo mkdir -p /work/pybind11
sudo chown 1000.1000 /work/pybind11

3.2.2 DockerのUbuntu20.04を利用して毎回必要なパッケージを入れる場合

  • ubuntu20.04を起動する
    • Docker内の/homeを/work/pybind11と共有
docker run -it --rm -v /work/pybind11:/home/ ubuntu:20.04
  1. Docker内で、必要なパッケージのインストール
    apt update
    apt upgrade
    apt -y install python3-dev python3-pip vim git g++ clang cmake make w3m less python3-ipython ipython3 nkf less diffutils patch sudo zlib1g-dev unzip locales 
    locale-gen ja_JP.UTF-8
    
    • pybind11パッケージのインストール
    pip3 install pybind11
    

3.2.3 pybind11を利用するのに必要なパッケージを入れたDockerイメージを作成する場合

  1. Dockerfileの準備
    • 以下の内容でDockerfileを作成
    FROM ubuntu:20.04
    MAINTAINER NM Max
    ENV user_n=user
    ENV user_id=1000
    #ENV DEBIAN_FRONTEND=noninteractive
    RUN apt -y update \
        && apt -y upgrade \
        && echo "6\n79\n" | apt -y install tzdata
    RUN apt -y install python3-dev python3-pip vim git g++ clang cmake make w3m less python3-ipython ipython3 nkf less diffutils patch sudo zlib1g-dev unzip locales \
        && locale-gen ja_JP.UTF-8 \
        && pip3 install pybind11
    ENV LC_ALL ja_JP.UTF-8
    ENV LANG ja_JP.UTF-8
    ENV LANGUAGE LANGUAGE=ja_JP:ja
    RUN echo 'LANG="ja_JP.UTF-8"' >> /etc/default/locale
    RUN echo "${user_n} ALL=NOPASSWD: ALL" >> /etc/sudoers
    WORKDIR /work
    RUN echo "\n\n\n\n\n\n\n" | adduser --uid ${user_id} --disabled-password ${user_n}
    RUN echo "user ALL=NOPASSWD: ALL" >> /etc/sudoers && chown user.user /work
    RUN mkdir -p /home/${user_n}/C && chown  ${user_n}.${user_n} /home/${user_n}/C
    ENTRYPOINT sudo su - ${user_n}
    
    • 以下のコマンドでイメージ作成
    docker build -t mypybind11 .
    
    • 作成したDockerイメージの起動は以下のコマンドで
    docker run -it --rm -v /work/pybind11:/home/user/C mypybind11
    

3.3 この章のまとめ

  • どのOSでも共通で利用できる環境をDockerで作成
  • Dockerの環境の準備が面倒なら、必要なツールをインストールして直接やってもよい

4 本家の文書のFirst stepsをやってみる

4.1 以下の操作を行っている動画


4.2 本家の文書のFirst stepsをやる手順

4.2.1 事前準備

  • OSに依存せずに同じ環境作れるようにDockerを利用してこの動画シリーズやっていきます。
  • まずDockerをインストールしてください。
    • 私はUbuntu20.04の環境で行って行きます。DockerはWindowsやMacでも使えるので、他のOSでもDockerを入れれば同じ手順で出来ます。
  • Docker等でUbuntuの環境を用意
    • Python,Python-dev,c++コンパイラー,cmake,git等必要なパッケージインストール
  1. 前の章で作成したmypybind11イメージを利用する場合
    • 前の章で作成した専用Dockerイメージmypybind11を起動
      • /work/pybind11 を Docker内の/homeと共通にしてます。
    docker run -it --rm -v /work/pybind11:/home/user/C mypybind11
    
    • 起動したDocker環境内で以下を実行
      • ubuntu20.04の場合は cd /home/user/C ではなく cd /home で
    cd /home/user/C
    mkdir test001
    cd test001/
    pwd
    
    • /home/user/C/test001 と表示され、現在の作業しているディレクトリを確認できます。
  2. 前の章で作成したmypybind11イメージを利用せずにUbuntu20.04を利用する場合
    • この場合、必要なものが入ってないので、Dockerイメージ起動あとに、必要なものを入れる必要があります。
    docker run -it --rm -v /work/pybind11:/home Ubuntu20.04
    
    • 以下を実行して必要なパッケージを入れる
    apt update
    apt upgrade
    apt install python3-dev python3-pip vim git g++ clang cmake make w3m less python3-ipython ipython3
    pip3 install pybind11
    
    • 起動したDocker環境内で以下を実行
      • ubuntu20.04の場合は cd /home/user/C ではなく cd /home で
    cd /home/
    mkdir test001
    cd test001/
    pwd
    
    • /home/test001 と表示され、現在の作業しているディレクトリを確認できます。

4.2.2 example.cppの準備

4.2.3 コンパイル

c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

4.2.4 動作確認

  • 対話環境で動作確認した方がやりやすいので、IPythonを起動する
ipython3
  • 作成したモジュールの読み込み
import example                                                                                                                                
  • 1+2をやってみる
example.add(1,2)                                                                                                                              
  • 1+2で3が表示されます
  • 次に5+6をやってみる。
example.add(5,6)   
  • 5+6で11が表示されます

4.3 この章のまとめ

  • 本家文書のFirst stepsをやってみた
  • 使い方の基本の確認
  • C++で作成した関数をPythonで利用する手順の理解

5 CMakeで前章の内容をやってみる

5.1 以下の操作を行っている動画


5.2 cmakeで前章の内容をやってみる手順

5.2.1 事前準備

  • 必要なソフトを入れる
  • 前の章のexample.cppを入れておく
  • example.cppのあるディレクトリに移動
  1. 以下のコマンドでpybind11の最新バージョンをダウンロードして利用しても良い
    git clone https://github.com/pybind/pybind11
    
    • 以下のコマンドでバックアップ取っておいても良いかも
    tar cvzf pybind11_`date +%Y%m%d_%H%M`.tgz pybind11/
    

5.2.2 CMakeLists.txt の準備

cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

5.2.3 コンパイル

mkdir build
cd build
cmake ..
make 
  • 前の章と同じ手順で動作確認すべき

5.3 この章のまとめ

  • 前章の内容をcmakeを利用してやってみた
  • コンパイルがちょい楽に

6 classを利用その1

6.1 以下の操作を行っている動画


6.2 classを利用その1の手順

  • この動画で以前作成したDockerイメージを起動
  • 作業用ディレクトリにてCMakeLists.txtとexample.cppの2つを用意

6.2.1 以前作成したDockerイメージの起動

docker run -it --rm -v /work/pybind11:/home/user/C mypybind11

6.2.2 作業用のディレクトリの作成と移動

cd /home/user/C
mkdir test002
cd test002/
pwd

6.2.3 CMakeLists.txt の作成

cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

6.2.4 example.cppの作成

  • 前の章で利用したものに簡単なクラスTest01を追加
  • Class Test01をPython側で利用出来るようにコード追加
    • def_readwrite でpublicのメンバ変数を参照してPythonから使えるようにした
    • コンストラクタと、helloメンバ関数を参照してPythonから使えるようにした
#include <pybind11/pybind11.h>
#include <string>
namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

class Test01 {
        public:
                Test01(const std::string &s) {
                        name=s;
                }
                std::string name;
                std::string hello() {
                        return "Hello "+name+"!";
                }
};

// https://pybind11.readthedocs.io/en/stable/classes.html#instance-and-static-fields
// https://pybind11.readthedocs.io/en/stable/classes.html#binding-lambda-functions
PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin"; // optional module docstring

        m.def("add", &add, "A function which adds two numbers");
        py::class_<Test01>(m, "Test01")
                .def(py::init<const std::string &>())
                .def_readwrite("name", &Test01::name)
                .def("hello", &Test01::hello);
}

6.2.5 pybind11の最新をgitでゲット

git clone https://github.com/pybind/pybind11

6.2.6 コンパイル

mkdir build
cd build
cmake ..
make 

6.2.7 動作確認

  • ipythonを起動して確認すると対話形式なので色々試しやすい
import example
m=example.Test01("Tom")
m.name
m.hello()
  • 次の章でオブジェクトを文字列化するところをカスタマイズするので、一度表示してみる。
m
  • 内部の情報とかは現状見れない。次の章でここらの表現をカスタマイズします。
  • 次の章で、ダイナミックに属性を追加出来るようにするので、現在出来ないことを確認
m.age=32                                                                                                                                      
  • AttributeError: ‘example.Test01’ object has no attribute ‘age’ が表示される

6.3 この章のまとめ

  • classをやってみた。

7 classを利用その2

7.1 以下の操作を行っている動画


7.2 classを利用その2の手順

  • この動画で以前作成したDockerイメージを起動
  • 作業用ディレクトリにてCMakeLists.txtとexample.cppの2つを用意

7.2.1 以前作成したDockerイメージの起動

docker run -it --rm -v /work/pybind11:/home/user/C mypybind11

7.2.2 作業用のディレクトリの作成と移動

cd /home/user/C
mkdir test003
cd test003/
pwd

7.2.3 CMakeLists.txt の作成

cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

7.2.4 example.cppの作成

  • 前の章で利用したものに多少修正
    • Class Test01のpybind11用記述部分に2箇所追加
      • py::dynamic_attr()追加 これにより 動的に属性追加可能に。 dict でそれらの情報出てくる。
        • .def(“repr“, …. 部分追加 これにより、クラスオブジェクトの表示をカスタマイズ
#include <pybind11/pybind11.h>
#include <string>
namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

class Test01 {
        public:
                Test01(const std::string &s) {
                        name=s;
                }
                std::string name;
                std::string hello() {
                        return "Hello "+name+"!";
                }
};

PYBIND11_MODULE(example, m) {
        m.doc() = "pybind11 example plugin"; // optional module docstring

        m.def("add", &add, "A function which adds two numbers");
        py::class_<Test01>(m, "Test01", py::dynamic_attr())
                .def(py::init<const std::string &>())
                .def_readwrite("name", &Test01::name)
                .def("hello", &Test01::hello)
                .def("__repr__",
                        [](const Test01 &a) {
                            return "<example.Test01 named '" + a.name + "'>";
                        }
                );
}

7.2.5 pybind11の最新をgitでゲット

git clone https://github.com/pybind/pybind11

7.2.6 コンパイル

mkdir build
cd build
cmake ..
make 

7.2.7 動作確認

  • ipythonを起動して確認すると対話形式なので色々試しやすい
import example
m=example.Test01("Tom")
m.name
m.hello()
  • 次の章でオブジェクトを文字列化するところをカスタマイズするので、一度表示してみる。
m
  • 表示がカスタマイズされて、nameの情報が表示されるようになった
  • 動的に属性を追加出来るようになっているので、試す。
m.age=32                                                                                                                                      
m.age  
print(m.__dict__)
  • 内部情報が表示されるようになった。

7.3 さらなる使い方

7.4 この章のまとめ

  • 前章のClassに動的に属性を追加出来るようにした。
  • 前章のClassを文字列化した時の表現のカスタマイズを行った。

8 コンテナを利用(std::vector) 1

8.1 以下の操作を行っている動画


8.2 コンテナを利用(std::vector) 1 の手順

  • この動画で以前作成したDockerイメージを起動
  • 作業用ディレクトリにてCMakeLists.txtとexample.cppの2つを用意

8.2.1 以前作成したDockerイメージの起動

docker run -it --rm -v /work/pybind11:/home/user/C mypybind11

8.2.2 作業用のディレクトリの作成と移動

cd /home/user/C
mkdir test004
cd test004/
pwd

8.2.3 CMakeLists.txt の作成

cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

8.2.4 example.cppの作成

  • pybind11/stl.h をインクルード
  • C++ で2つの関数を定義
    • range関数,最初、最後、差を与えると、等差数列をかえす関数
      • 無限ループ回避の処理をいれたら、ちょい不細工に
    • concatv 2つの整数(int)の数列を結合してかえす関数
  • Pythonに渡す関数の記述
    • 2つの関数の記述
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>
namespace py = pybind11;

std::vector<int> range(int st, int end, int diff) {
  std::vector<int> ans;
  bool flag=(end-st)*diff>=0;
  if(flag && diff!=0) {
    if(end>=st) {
      for(int i=st;i<=end;i+=diff) {
        ans.push_back(i);
      }
    } else {
      for(int i=st;i>=end;i+=diff) {
        ans.push_back(i);
      }
    }
  } else {
    ans.push_back(st);
  }
  return ans;
}       

std::vector<int> concatv(const std::vector<int> &a, const std::vector<int> &b) {
  std::vector<int> ans;
  for(int i=0;i<a.size();++i) {
    ans.push_back(a[i]);
  }
  for(int i=0;i<b.size();++i) {
    ans.push_back(b[i]);
  }
  return ans;
}       

PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin"; // optional module docstring

  m.def("range", &range, "A function which make a arithmetic progression");
  m.def("concatv", &concatv, "A function which concatenate two int vector");
}

8.2.5 pybind11の最新をgitでゲット

git clone https://github.com/pybind/pybind11

8.2.6 コンパイル

mkdir build
cd build
cmake ..
make 

8.2.7 動作確認

  • ipythonを起動して確認すると対話形式なので色々試しやすい
import example
example.range(1,10,1)
example.range(1,10,2)
example.range(10,1,-1)
example.range(1,10,-1)
example.range(1,10,0)
example.concatv([5,6],[1,2,3])
example.concatv([1,2,3],[4,5])

8.3 この章のまとめ

  • std::vectorを使ったC++関数をPython側から使えるようにしてみた。

9 コンテナを利用(std::vector) 2 (Making opaque typesの1 意図したように動かない例)

9.1 以下の操作を行っている動画


9.2 コンテナを利用(std::vector) 2 (Making opaque typesの1 意図したように動かない例) の手順

9.2.1 以前作成したDockerイメージの起動

docker run -it --rm -v /work/pybind11:/home/user/C mypybind11

9.2.2 作業用のディレクトリの作成と移動

cd /home/user/C
mkdir test005
cd test005/
pwd

9.2.3 CMakeLists.txt の作成

cmake_minimum_required(VERSION 3.4...3.18)
project(example LANGUAGES CXX)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

9.2.4 example.cppの作成

 - このままコピペじゃ動かないので、不足分を追加して実際に動かしてみる  - この例は意図した動作になっていない。自分でも同じことする時は気をつけるべき

  • 意図する動作にする手順は次の章で
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>

namespace py = pybind11;

void append_1(std::vector<int> &v) {
  v.push_back(1);
}

class MyClass {
  public:
    std::vector<int> contents;
};

/* ... binding code ... */


PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin"; // optional module docstring

  m.def("append_1", &append_1);
  py::class_<MyClass>(m, "MyClass")
      .def(py::init<>())
      .def_readwrite("contents", &MyClass::contents);
}

9.2.5 pybind11の最新をgitでゲット

git clone https://github.com/pybind/pybind11

9.2.6 コンパイル

mkdir build
cd build
cmake ..
make 

9.2.7 動作確認

  • ipythonを起動して確認すると対話形式なので色々試しやすい
import example
v=[1,2,3]
example.append_1(v)
v
  • [1,2,3,1]ではなく[1,2,3]がかえってくる
  • クラスの方もやってみる
m = example.MyClass()
m.contents = [5, 6]
m.contents
m.contents.append(7)
m.contents
  • これも[5,6,7]ではなく[5,6]がかえってくる

9.3 この章のまとめ

10 コンテナを利用(std::vector) 3 (Making opaque typesの2 意図したように動くようにOPAQUEを使う)

10.1 以下の操作を行っている動画


10.2 コンテナを利用(std::vector) 3 (Making opaque typesの2 意図したように動くようにOPAQUEを使う)手順

  • 手順は前の章と同じになるので、前の章と異る部分、example.cppのみここに記載します。

10.2.1 example.cppの作成

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>
#include <string>

namespace py = pybind11;

void append_1(std::vector<int> &v) {
  v.push_back(1);
}

class MyClass {
  public:
    std::vector<int> contents;
};

/* ... binding code ... */

PYBIND11_MAKE_OPAQUE(std::vector<int>);

PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin"; // optional module docstring

  py::class_<std::vector<int>>(m, "IntVector")
    .def(py::init<>())
    .def("clear", &std::vector<int>::clear)
    .def("pop_back", &std::vector<int>::pop_back)
    .def("push_back", (void (std::vector<int>::*)(const int &)) &std::vector<int>::push_back)
    .def("__len__", [](const std::vector<int> &v) { return v.size(); })
    .def("__iter__", [](std::vector<int> &v) {
       return py::make_iterator(v.begin(), v.end());
    }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */

  m.def("print_opaque_list", [](const std::vector<int> &l) {
        std::string ret = "Opaque list: [";
        bool first = true;
        for (auto entry : l) {
            if (!first)
                ret += ", ";
            ret += std::to_string(entry);
            first = false;
        }
        return ret + "]";
    });


  m.def("append_1", &append_1);
  py::class_<MyClass>(m, "MyClass")
      .def(py::init<>())
      .def_readwrite("contents", &MyClass::contents);
}

10.2.2 動作確認

import example
m=example.MyClass()
example.print_opaque_list(m.contents)
m.contents.push_back(1)
m.contents.push_back(2)
example.print_opaque_list(m.contents)
example.append_1(m.contents)
example.print_opaque_list(m.contents)
example.append_1(m.contents)
example.print_opaque_list(m.contents)

10.3 この章のまとめ

11 コンテナを利用(std::vector) 4 (Making opaque typesの3 意図したように動くようにOPAQUEと、Binding STL containersを使う)

11.1 以下の操作を行っている動画


11.2 コンテナを利用(std::vector) 4 (Making opaque typesの3 意図したように動くようにOPAQUEと、Binding STL containersを使う) 手順

  • 手順は以前の章と同じになるので、前の章と異る部分、example.cppのみここに記載します。

11.2.1 example.cppの作成

 - このままコピペじゃ動かないので、不足分を追加して実際に動かしてみる  - この例は意図した動作になっていない。自分でも同じことする時は気をつけるべき

  • 意図する動作にする手順は次の章で
  • ../pybind11/tests/test_opaque_types.cpp を参考に
    • push_backを追加(std::vector<int>用に修正)
    • print_opaque_list(std::vector<int>用に修正)
#include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>
#include <vector>
#include <string>

namespace py = pybind11;

void append_1(std::vector<int> &v) {
  v.push_back(1);
}

class MyClass {
  public:
    std::vector<int> contents;
};

/* ... binding code ... */

PYBIND11_MAKE_OPAQUE(std::vector<int>);

PYBIND11_MODULE(example, m) {
  m.doc() = "pybind11 example plugin"; // optional module docstring

  py::bind_vector<std::vector<int>>(m, "VectorInt");

  m.def("append_1", &append_1);
  py::class_<MyClass>(m, "MyClass")
      .def(py::init<>())
      .def_readwrite("contents", &MyClass::contents);
}

11.2.2 動作確認

import example
m=example.MyClass()
m.contents
m.contents=example.VectorInt([1,2,3])
example.append_1(m.contents)
m.contents
m.contents.append(5)
m.contents

11.3 この章のまとめ

12 引数に名前をつける

12.1 以下の操作を行っている動画


12.2 引数に名前をつける手順

  • 手順は以前の章と同じになるので、前の章と異る部分、example.cppのみここに記載します。

12.2.1 example.cppの作成

#include <pybind11/pybind11.h>
namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

 // m.def("add", &add, "A function which adds two numbers"); // org
    m.def("add", &add, "A function which adds two numbers", py::arg("i"), py::arg("j"));
}

12.2.2 動作確認

import example
example.add(i=1,j=2)

12.3 この章のまとめ

13 引数にデフォルト値を設定

13.1 以下の操作を行っている動画


13.2 引数にデフォルト値を設定する手順

  • 手順は以前の章と同じになるので、前の章と異る部分、example.cppのみここに記載します。

13.2.1 example.cppの作成

#include <pybind11/pybind11.h>
namespace py = pybind11;

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

 // m.def("add", &add, "A function which adds two numbers"); // org
 // m.def("add", &add, "A function which adds two numbers", py::arg("i"), py::arg("j")); // 前の章
    m.def("add", &add, "A function which adds two numbers", py::arg("i")=1, py::arg("j")=2);
}

13.2.2 動作確認

import example
example.add(i=2)
example.add(j=3)
example.add()

13.3 この章のまとめ

14 変数のエクスポート

14.1 以下の操作を行っている動画


14.2 変数のエクスポート 手順

  • 手順は以前の章と同じになるので、前の章と異る部分、example.cppのみここに記載します。

14.2.1 example.cppの作成

#include <pybind11/pybind11.h>
namespace py = pybind11;

PYBIND11_MODULE(example, m) {
    m.attr("the_answer") = 42;
    py::object world = py::cast("World");
    m.attr("what") = world;
}

14.2.2 動作確認

import example
example.the_answer
example.the_answer=53
example.the_answer
example.what
example.what="hoho"
example.what

14.3 この章のまとめ

15 オーバーロードされたメソッド

15.1 以下の操作を行っている動画


15.2 オーバーロードされたメソッドをPythonで利用する手順

  • C++14以前の方法と、C++14以降とでやり方2つある
  • C++14以降の方が記述が楽なので、こっちだけ動画で説明します。(もう一つの方法は Overloaded methods https://pybind11.readthedocs.io/en/stable/classes.html#overloaded-methodsをご覧ください)
  • pybind11では自動で、それらのメソッドを自動で選択してくれない、なので、ひとつひとつ引数を記述して記述していく
  • Dockerイメージの起動からコンパイルまでの手順は以前の動画 https://www.youtube.com/embed/fBW69-x4n6U を参考にやってください。

15.2.1 C++ソースファイルの作成

#include <pybind11/pybind11.h>
namespace py = pybind11;

struct Widget {
  int foo(int x, float y) {
    return x+y;
  }
  int foo(int x, float y) const {
    return x-y;
  }
};

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring
    py::class_<Widget>(m, "Widget")
       .def(py::init<>())
       .def("foo_mutable", py::overload_cast<int, float>(&Widget::foo))
       .def("foo_const",   py::overload_cast<int, float>(&Widget::foo, py::const_));
}

15.2.2 CMakeLists.txt の作成

cmake_minimum_required(VERSION 3.4...3.18)

set(CMAKE_CXX_STANDARD 14)          # C++14以上に設定する
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 設定のC++のバージョン条件を満たせないならエラーにする
set(CMAKE_CXX_EXTENSIONS OFF)       # 互換性を高める

project(example LANGUAGES CXX)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

15.2.3 pybind11の最新をgitでゲット

git clone https://github.com/pybind/pybind11

15.2.4 コンパイル

  • 以前の章と同じコマンド
mkdir build
cd build
cmake ..
make 

15.2.5 動作確認

  • ipythonを起動して確認すると対話形式なので色々試しやすい
import example
w=example.Widget()
w.foo_mutable(1,2)
w.foo_const(1,2)

15.3 この章のまとめ

  • オーバーロードされたメソッドの取扱方法の1つを行なった。

16 モジュール名に注意

  • CMakeLists.txt内のモジュール名とc++ソースのモジュール名が異ると、コンパイルは可能なのですが、モジュール読み込み時にエラーが発生
  • Dockerイメージの起動からコンパイルまでの手順は以前の動画 https://www.youtube.com/embed/fBW69-x4n6U を参考にやってください。

16.1 以下の操作を行っている動画


16.2 モジュール名に注意 の確認手順

  • 前のソースのCMakeLists.txtの pybind11_add_module(example example.cpp) を pybind11_add_module(widget example.cpp) とか、別名にしてみる
  • コンパイルしなおして、モジュール読み込むと以下のエラーが発生
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-64ca027457b3> in <module>
----> 1 import widget

ImportError: dynamic module does not define module export function (PyInit_widget)
  • ということで、 CMakeLists.txt内のモジュール名と、C++ソースのモジュール名同じにする必要があります。(こないだこれでハマり、時間ロスしました)

16.3 この章のまとめ

  • CMakeLists.txt のモジュール名と、C++ソースで記述したモジュール名は同じにしましょう。そうしないとエラー発生。

17 今後

  • 今後も文書追加していきます。

18 この文書のチェンジログ

  • 2021/05/08 初版
  • 2021/05/10 classを利用その1 の章, classを利用その2 の章追加
  • 2021/05/10 Dockerイメージの起動のコマンドに誤りがあったので修正
  • 2021/05/11 コンテナを利用(std::vector) 1 の章追加
  • 2021/05/12 コンテナを利用(std::vector) 2 (Making opaque typesの1 意図したように動かない例) の章、コンテナを利用(std::vector) 3 (Making opaque typesの2 意図したように動くようにOPAQUEを使う) の章、 * コンテナを利用(std::vector) 4 (Making opaque typesの3 意図したように動くようにOPAQUEと、Binding STL containersを使う) の章追加
  • 2021/05/13 引数に名前をつける の章、 引数にデフォルト値を設定 の章、 変数のエクスポート の章追加
  • 2021/05/21 オーバーロードされたメソッド の章追加, モジュール名に注意 の章追加
  • 2021/05/24 BlogへのアップロードをMarkDownに変換して登録するように変更

著者: NM Max

Created: 2021-05-24 月 23:42

Validate

cppyyを利用して外部ライブラリを簡単にPythonで利用

cppyy 入門

1 概要

  • PythonからC++やCと連携するのが非常に楽に出来るパッケージ
  • C++やCのインタプリタ、CERNのROOTの技術を利用しているようだ
  • 色々あった、既存のPythonとの連携ツールは、ソースを改造したり、追加のソースが必要となったが、これはほぼ無しで動く

3 インストールと、基本的使い方

3.1 以下の操作を行っている動画

3.2 ローカルにインストールする手順

  • Ubuntuじゃなければpip3のかわりにpipを利用して pip install cppyy でローカルにインストール可能
  • 必要なパッケージ、ツールがあると思われる
  • ipythonか、Jupyter Notebookをいれとくと、試しやすいし、プログラムする時に便利(私は軽いIpythonをいれてます)
pip3 install cppyy

3.3 テスト

import cppyy
# include
cppyy.include("string")
# std::vector 利用
v=cppyy.gbl.std.vector['std::string']()
v.push_back("hello")
# vのタイプ表示
print(type(v))
# std::vectorの要素表示
print(v.size())                                                                                                                               
print(v[0])  
# 独自関数定義
cppyy.cppdef('void myprint(std::string s) {std::cout << s << std::endl;};')   
# 独自関数利用
cppyy.gbl.myprint(v[0])
cppyy.gbl.myprint(cppyy.gbl.std.string("World"))         
cppyy.gbl.myprint("World")         
# lowlevel ライブラリ
import cppyy.ll

3.4 この章のまとめ

  • 基本的な使い方の説明を行った
    • include
    • std::vectorの利用
    • std::stringの利用
    • 独自関数の定義と利用
    • タイプの表示
    • クラスの利用

4 外部ライブラリの利用

4.1 以下の操作を行っている動画

4.2 外部ライブラリの利用手順

4.2.1 自作のライブラリ作成

  1. 以下の内容でmyadd.hpp作成
    #ifndef MYADD_HPP 
    #define MYADD_HPP
    class MyCalc{
      public:
        int myadd(int a,int b);
        int mysub(int a,int b);
    };
    #endif
    
  2. 以下の内容でmyadd.cpp作成
    #include "myadd.hpp"
    int MyCalc::myadd(int a, int b){
      return a+b;
    }
    
    int MyCalc::mysub(int a, int b){
      return a-b;
    }
    
  3. 以下のコマンドでライブラリファイル作成
    clang++ --shared -fPIC myadd.cpp -o libmyadd.so
    

4.2.2 cppyyを利用して、Pythonで自作C++ライブラリの利用

import cppyy
cppyy.include("myadd.hpp")
cppyy.load_library("./libmyadd.so")
mc=cppyy.gbl.MyCalc()
print(mc.myadd(1,2))
print(mc.mysub(1,2))

4.3 この章のまとめ

  • 自作C++ライブラリを作成し、それをPythonでcppyyライブラリを利用して使ってみた
  • 外部ライブラリも同じ手順でcppyyで利用可能
  • cppyyを利用することで、色々なCやC++の豊富なライブラリを簡単にPythonで利用できるようになった

5 今後

  • 今後も文書追加していきます。

6 この文書のチェンジログ

  • 2020/09/02 初版

著者: NM Max

Created: 2020-09-02 水 19:48

Validate

Kivy入門(version1系)

Kivy入門(version1系)

目次

1 概要

  • Pythonのライブラリ
  • クロスプラットフォームの開発環境
  • Kivy + buildozer で Androidアプリを作ってみる
  • 環境は Ubuntu version 20.04ベースで行う(Dockerを利用すれば、Dockerを利用できる他のプラットフォームでも同じ手順で行える)

2 関連リンク

2.1 使うかもしれないパッケージ

3 インストール

3.1 以下の操作を行っている動画

3.2 kivyのUbuntu20.04へのインストール手順

  • 手順の確認はDockerのUbuntu 20.04を利用した
  • 以下の手順で共有ディレクトリを作成し、Dockerは1000のuidのユーザーで行った
sudo mkdir /work/kivy
sudo chown 1000.1000 /work/kivy/
  • 起動は以下のコマンドで行った
docker run -it --rm -v /work/kivy:/home/ ubuntu:20.04 

3.3 手順

3.3.1 必要パッケージのインストール

apt update
apt install python3-kivy 
  • 一般ユーザーで以下を行う場合は 以下のように先頭に sudoをつける
sudo apt update
sudo apt install python3-kivy 

4 空のApp作成

4.1 以下の説明を行っている動画

4.2 空のApp作成手順

  • tutorial001.py というファイル名で以下内容のファイル作成
from kivy.app import App

class HelloWorldApp(App):
  pass

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

4.2.1 実行

python3 tutorial002_helloworld.py 

4.3 この章のまとめ

  • 空のアプリ作成

5 サンプル用のHello Worldプログラム作成(Label利用)

5.1 以下の説明を行っている動画

5.2 HelloWorld

  • tutorial002_helloworld.py というファイル名で以下内容のファイル作成
from kivy.app import App
from kivy.uix.label import Label

class HelloWorldApp(App):

    def build(self):
        return Label(text='Hello World!')

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

5.2.1 実行

python3 tutorial002_helloworld.py 

5.3 この章のまとめ

  • Labelを利用して、Hello World作成

6 BoxLayout

6.1 以下の説明を行っている動画

6.3 BoxLayout利用

  • tutorial003_boxlayout.py というファイル名で以下内容のファイル作成
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout

class HelloWorldApp(App):
    def build(self):
        b = BoxLayout(orientation="vertical")
        l1 = Label(text='Hello',font_size=150)
        l2 = Label(text='World',font_size=150)

        b.add_widget(l1)
        b.add_widget(l2)
        return b

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

6.3.1 実行

python3 tutorial003_boxlayout.py

6.4 この章のまとめ

  • 部品を良い感じで並べてくれるツールの内BoxLayoutを使ってみた

7 Module: kivy.lang.builder

7.1 以下の説明を行っている動画

7.3 Builder 利用

  • tutorial004_builder.py というファイル名で以下内容のファイル作成
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder

kv='''
BoxLayout:
    orientation: 'vertical'
    Label:
        text: 'Hello'
        font_size: 150
    Label:
        text: 'World'
        font_size: 150
'''

class HelloWorldApp(App):
    def build(self):
        return Builder.load_string(kv)

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

7.3.1 実行

python3 tutorial004_builder.py

7.4 この章のまとめ

  • builderを利用して、kivy言語を利用

8 Kivy言語の利用(外部ファイル)

8.1 以下の説明を行っている動画

8.3 Kivy言語外部ファイルを利用

8.3.1 tutorial005_kivy.py というファイル名で以下内容のファイル作成

from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
#from kivy.uix.widget import Widget
#from kivy.lang.builder import Builder

class HelloWorld(BoxLayout):
  pass

class Tutorial005App(App):
    def build(self):
        return HelloWorld()

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

8.3.2 tutorial005.kv ファイルを以下の内容で作成

  • ファイル名はAppを継承したクラス名の最後のAppを除いた物が自動で検索される
  • 例 今回だと Tutorial005App から tutorial005.kv
  • 例 TutorialApp なら tutorial.kv
<HelloWorld>:
    orientation: 'vertical'
    Label:
	text: 'Hello'
	font_size: 150
    Label: 
	text: 'World'
	font_size: 150

8.3.3 実行

python3 tutorial005_builder.py

8.4 この章のまとめ

  • 外部ファイルで、kivy言語を利用

9 Module: kivy.uix.button

9.1 以下の説明を行っている動画

9.3 buttonを利用する手順例

9.3.1 tutorial006.py というファイル名で以下内容のファイル作成

  • on_release でボタン押して離した時にイベント発生する記述を行う
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder

import random

kv='''
<HelloWorld>
    orientation: 'vertical'
    Label:
        id: label1
        text: 'Hello'
        font_size: 150
    Button:
        text: 'Change Color'
        font_size: 80
        on_release: root.changeColor()
''' 

Builder.load_string(kv)

class HelloWorld(BoxLayout):
    def changeColor(self):
        print("changeColor")
        for key, val in self.ids.items():
            print("key={0}, val={1}".format(key, val))
        print(self.ids)
        mylabel=self.ids['label1']
        mylabel.color=[random.random(),random.random(),random.random(),1]

class HelloWorldApp(App):
    def build(self):
        return HelloWorld()
        #return Builder.load_string(kv)

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

9.3.2 実行

  • Ubuntuでは pythonではなくpython3の必要あるケースあり
python tutorial006.py 

9.3.3 tutorial006-2.py というファイル名で以下内容のファイル作成

  • kivy言語側で、クリックイベントの処理記述
  • kivy言語側で、randomモジュール読み込み
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder

import random

kv='''
#:import random random
<HelloWorld>
    orientation: 'vertical'
    Label:
        id: label1
        text: 'Hello'
        font_size: 150
    Button:
        text: 'Change Color'
        font_size: 80
        on_release: root.ids['label1'].color=[random.random(),random.random(),random.random(),1]
''' 

Builder.load_string(kv)

class HelloWorld(BoxLayout):
    pass

class HelloWorldApp(App):
    def build(self):
        return HelloWorld()
        #return Builder.load_string(kv)

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

9.3.4 実行

  • Ubuntuでは pythonではなくpython3の必要あるケースあり
python tutorial006-2.py 

9.4 この章のまとめ

  • Module: kivy.uix.buttonを使ってみた
  • ボタンを押して離した時にイベント発生する処理を書いた

10 Module: kivy.uix.textinputを利用して足し算アプリ作成

10.1 以下の説明を行っている動画

10.3 Module: kivy.uix.textinputを利用して足し算アプリ作成 手順

10.3.1 tutorial007.py というファイル名で以下内容のファイル作成

  • input_filterで整数のみ入力可能にしてある
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.lang.builder import Builder

import random

# https://kivy.org/doc/stable/api-kivy.uix.textinput.html

kv='''
#:import random random
<HelloWorld>
    orientation: 'vertical'
    TextInput:
        id: textinput1
        font_size: 150
        input_filter: 'int'
        text: '1'
    TextInput:
        id: textinput2
        font_size: 150
        input_filter: 'int'
        text: '1'
    Label:
        id: label1
        text: ''
        font_size: 150
    Button:
        text: 'calc add'
        font_size: 80
        on_release: root.ids['label1'].text=str(int(root.ids['textinput1'].text)+int(root.ids['textinput2'].text))

''' 

Builder.load_string(kv)

class HelloWorld(BoxLayout):
    pass

class HelloWorldApp(App):
    def build(self):
        return HelloWorld()
        #return Builder.load_string(kv)

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

10.3.2 実行

python3 tutorial007.py 

10.4 この章のまとめ

  • Module: kivy.uix.textinputの基本的な使い方紹介
  • Module: kivy.uix.textinputを利用して足し算アプリ作成

11 設定を行う機能

11.1 以下の説明を行っている動画

11.3 設定を行う機能の利用手順

11.3.1 tutorial008.py というファイル名で以下内容のファイル作成

  • ホームディレクトリの .kivy/config.ini というファイルが設定を変更すると更新される
  • app.open_settings() でセッティング画面が表示される
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.lang.builder import Builder

import random

kv='''
#:import random random
<MyAdd>
    orientation: 'vertical'
    TextInput:
        id: textinput1
        font_size: 100
        input_filter: 'int'
        text: '1'
    TextInput:
        id: textinput2
        font_size: 100
        input_filter: 'int'
        text: '1'
    Label:
        id: label1
        text: ''
        font_size: 100
    Button:
        text: 'calc add'
        font_size: 80
        on_release: root.ids['label1'].text=str(int(root.ids['textinput1'].text)+int(root.ids['textinput2'].text))
    Button:
        text: 'setting'
        font_size: 80
        on_release: app.open_settings()
''' 

Builder.load_string(kv)

class MyAdd(BoxLayout):
    pass

class HelloWorldApp(App):
    def build(self):
        return MyAdd()
        #return Builder.load_string(kv)

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

11.3.2 実行

python3 tutorial008.py

11.3.3 tutorial008-2.py というファイル名で以下内容のファイル作成

  • ホームディレクトリの .kivy/config.ini というファイルが設定を変更すると更新される
  • app.open_settings() でセッティング画面が表示される
  • self.settings_cls=SettingsWithSidebar を追加すると横にメニューが追加
  • self.use_kivy_settings=False の行の#を外して有効化すると、元々のメニューは選択できなくなる
  • 独自メニュー追加
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.settings import SettingsWithSidebar
from kivy.lang.builder import Builder

import json
import random

Builder.load_string('''
#:import random random
<MyAdd>
    orientation: 'vertical'
    TextInput:
        id: textinput1
        font_size: 100
        input_filter: 'int'
        text: '1'
    TextInput:
        id: textinput2
        font_size: 100
        input_filter: 'int'
        text: '1'
    Label:
        id: label1
        text: ''
        font_size: 100
    Button:
        text: 'calc add'
        font_size: 80
        on_release: root.ids['label1'].text=str(int(root.ids['textinput1'].text)+int(root.ids['textinput2'].text))
    Button:
        text: 'setting'
        font_size: 80
        on_release: app.open_settings()
''')

settings_json=json.dumps([
    {'type': 'title', 'title': 'example title'},
    {'type': 'bool', 'title': 'a boolean setting', 'desc': 'Boolean description text', 'section':'example', 'key':'boolexample'},
    {'type': 'numeric', 'title': 'a numerical setting', 'desc': 'Numeric description text', 'section':'example', 'key':'numericalexample'},
    {'type': 'options', 'title': 'a options setting', 'desc': 'Option description text', 'section':'example', 'key':'optionsexample', 'options':['1','2','3']},
    {'type': 'string', 'title': 'a string setting', 'desc': 'String description text', 'section':'example', 'key':'stringexample'},
    {'type': 'path', 'title': 'a path setting', 'desc': 'Path description text', 'section':'example', 'key':'pathexample'},
    ])

class MyAdd(BoxLayout):
    pass

class HelloWorldApp(App):
    def build(self):
        self.settings_cls=SettingsWithSidebar
        # self.use_kivy_settings=False
        setting1 = self.config.get('example', 'optionsexample')
        print(self.config.items('example'))
        return MyAdd()
        #return Builder.load_string(kv)

    def build_config(self, config):
        config.setdefaults('example', {
            'boolexample':True,
            'numericalexample':1,
            'optionsexample':'3',
            'stringexample':'abc',
            'pathexample':'./',
            })

    def build_settings(self, settings):
        settings.add_json_panel('test', self.config, data=settings_json)

    def on_config_change(self, config, section, key, value):
        print([config, section, key, value])

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

11.3.4 実行

python3 tutorial008-2.py

11.4 この章のまとめ

  • ベースはCrash Courseの13番目の動画を参考に作成
  • 設定関係の説明を行った
  • 独自メニューの追加方法も紹介

12 スクリーンマネジャー(画面切替)

12.1 以下の説明を行っている動画

12.3 スクリーンマネジャー(画面切替) 手順

12.3.1 tutorial009.py というファイル名で以下内容のファイル作成

  • スクリーンマネジャーはScreenManagerを継承
  • 切り替える画面はScreenを継承
  • クラスの定義はkivy言語の読み込み前に行わないとエラーになっていた
  • kivy.uix.screenmanager.FadeTransitionをkivy言語側でインポート
  • transition: FadeTransition() の 先頭の#をはずすと、画面切替の様子が変更になる
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.settings import SettingsWithSidebar
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition

import json
import random

class MyScreenManager(ScreenManager):
    pass

class MyAdd(Screen):
    pass

class MySub(Screen):
    pass

root_widget = Builder.load_string('''
#:import random random
#:import FadeTransition kivy.uix.screenmanager.FadeTransition

MyScreenManager:
    #transition: FadeTransition()
    MyAdd:
    MySub:

<MyAdd>
    name : 'MyAdd'
    BoxLayout:
        orientation: 'vertical'
        TextInput:
            id: textinput1
            font_size: 100
            input_filter: 'int'
            text: '1'
        TextInput:
            id: textinput2
            font_size: 100
            input_filter: 'int'
            text: '1'
        Label:
            id: label1
            text: ''
            font_size: 100
        Button:
            text: 'calc add'
            font_size: 80
            on_release: root.ids['label1'].text=str(int(root.ids['textinput1'].text)+int(root.ids['textinput2'].text))
        Button:
            text: 'setting'
            font_size: 80
            on_release: app.open_settings()
        Button:
            text: 'go to sub'
            font_size: 80
            on_release: app.root.current = 'MySub'
<MySub>
    name : 'MySub'
    BoxLayout:
        orientation: 'vertical'
        TextInput:
            id: textinput1
            font_size: 100
            input_filter: 'int'
            text: '1'
        TextInput:
            id: textinput2
            font_size: 100
            input_filter: 'int'
            text: '1'
        Label:
            id: label1
            text: ''
            font_size: 100
        Button:
            text: 'calc sub'
            font_size: 80
            on_release: root.ids['label1'].text=str(int(root.ids['textinput1'].text)-int(root.ids['textinput2'].text))
        Button:
            text: 'setting'
            font_size: 80
            on_release: app.open_settings()
        Button:
            text: 'go to add'
            font_size: 80
            on_release: app.root.current = 'MyAdd'
''')

# ここで定義するとエラーに
#class MyScreenManager(ScreenManager):
#    pass
#
#class MyAdd(Screen):
#    pass
#
#class MySub(Screen):
#    pass
#

settings_json=json.dumps([
    {'type': 'title', 'title': 'example title'},
    {'type': 'bool', 'title': 'a boolean setting', 'desc': 'Boolean description text', 'section':'example', 'key':'boolexample'},
    {'type': 'numeric', 'title': 'a numerical setting', 'desc': 'Numeric description text', 'section':'example', 'key':'numericalexample'},
    {'type': 'options', 'title': 'a options setting', 'desc': 'Option description text', 'section':'example', 'key':'optionsexample', 'options':['1','2','3']},
    {'type': 'string', 'title': 'a string setting', 'desc': 'String description text', 'section':'example', 'key':'stringexample'},
    {'type': 'path', 'title': 'a path setting', 'desc': 'Path description text', 'section':'example', 'key':'pathexample'},
    ])


class HelloWorldApp(App):
    def build(self):
        self.settings_cls=SettingsWithSidebar
        self.use_kivy_settings=False
        setting1 = self.config.get('example', 'optionsexample')
        print(self.config.items('example'))
        return root_widget
        #return Builder.load_string(kv)

    def build_config(self, config):
        config.setdefaults('example', {
            'boolexample':True,
            'numericalexample':1,
            'optionsexample':'3',
            'stringexample':'abc',
            'pathexample':'./',
            })

    def build_settings(self, settings):
        settings.add_json_panel('test', self.config, data=settings_json)

    def on_config_change(self, config, section, key, value):
        print([config, section, key, value])

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

12.3.2 実行

python3 tutorial009.py

12.4 この章のまとめ

13 今後

  • 今後も文書追加していきます。

14 この文書のチェンジログ

  • 2020/08/13 初版
  • 2020/08/14 Module: kivy.lang.builder の章, Module: kivy.lang.builderの章, Kivy言語の利用(外部ファイル) の章追加
  • 2020/08/14 Module: kivy.uix.buttonの章, Module: kivy.uix.textinputを利用して足し算アプリ作成 の章, 設定を行う機能 の章, スクリーンマネジャー(画面切替) の章追加

著者: NM Max

Created: 2020-08-14 金 19:25

Validate

Kivy と buildozer で スマホアプリ作成

Kivy と buildozer で スマホアプリ作成

目次

1 概要

  • Android純正の開発環境重すぎ
  • クロスプラットフォームの開発環境で作りたい(ソースの使い回し)
  • ということで、これができる開発環境の一つである、Kivy + buildozer で Androidアプリを作ってみる
  • 環境は Ubuntu version 20.04ベースで行う(Dockerを利用すれば、Dockerを利用できる他のプラットフォームでも同じ手順で行える)

2 リンク

3 Ubuntuでコンパイル

3.1 以下の操作を行っている動画



3.2 buildozerのUbuntu20.04へのインストール手順

  • 手順の確認はDockerのUbuntu 20.04を利用した
  • 以下の手順で共有ディレクトリを作成し、Dockerは1000のuidのユーザーで行った
sudo mkdir /work/kivy
sudo chown 1000.1000 /work/kivy/
  • 起動は以下のコマンドで行った
docker run -it --rm -v /work/kivy:/home/ ubuntu:20.04 

3.3 手順

3.3.1 必要パッケージのインストール

apt update
apt install sudo python3-kivy python3-pip python3-virtualenv vim default-jdk zlib1g-dev
apt install -y sudo vim git aidl ffmpeg python3 automake autoconf libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev libltdl-dev libssl-dev lld clang lldb zip unar unzip
  • 一般ユーザーで以下を行う場合は 以下のように先頭に sudoをつける
sudo apt update
sudo apt install sudo python3-kivy python3-pip python3-virtualenv vim default-jdk zlib1g-dev
sudo apt install -y sudo vim git aidl ffmpeg python3 automake autoconf libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev libltdl-dev libssl-dev lld clang lldb zip unar unzip

3.3.2 一般ユーザー追加

  • これは通常のUbuntuでは不要、Dockerで行っていて、操作を一般ユーザーで行いたいときに、以下の手順を実行
  • この例ではユーザー名はyoutubeとしている
  • 通常のUbuntu 20.04の場合はこの手順は恐らく不要
adduser youtube
  1. 追加した一般ユーザーがsudo使えるようにする(これは特に必要ない手順)
    • これは通常のUbuntuでは不要、Dockerで行っていて、操作を一般ユーザーで行いたいときに、以下の手順を実行
    • この例ではユーザー名はyoutubeとしている
    adduser youtube sudo
    

3.3.3 virtual環境で実行

  • ここでは kivy_buildozer ディレクトリで行う
virtualenv  --no-packaging  kivy_buildozer
cd kivy_buildozer
source bin/activate

3.3.4 git で管理できるようにしとく

  • ユーザー名と、メールアドレスは、自分にあわせてください。ここでは”Your Name”, “you@example.com”としてます。
git init
git add -A
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git commit -m "first"

3.3.5 必要なPythonパッケージのインストール

pip3 install Cython
pip3 install buildozer

3.3.6 自分のプロジェクト用のディレクトリ作成

mkdir app
cd app
mkdir test001
cd test001/

3.3.7 サンプル用のHello Worldプログラム作成

  • main.py というファイル名で以下内容のファイル作成
import kivy

from kivy.app import App
from kivy.uix.label import Label

class HelloWorldApp(App):

    def build(self):
        return Label(text='Hello World!')

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

3.3.8 Android用のapkファイル作成

  • bin ディレクトリにエラーがなければ、apkファイルが作成されます
buildozer init
buildozer android debug deploy run
  • 実行中にAndroid用のライセンス表示されるので、yを入力しエンターを押す必要があるケースあり

3.4 この章のまとめ

  • Ubuntu 20.04に kivy+buildozerを利用したスマホアプリ開発環境インストールし、Androidアプリを作成
  • Dockerを利用すれば、同じ手順で環境構築と、Androidアプリ作成可能

4 Kivy+buildozer関連リンク

  • 開発ツール関係文書

4.1 以下の説明を行っている動画

4.3 使うかもしれないパッケージ

5 Kivy Launcherの使い方

5.1 以下の説明を行っている動画

5.3 概要

  • Androidにkivy Luncherをインストール
  • adbコマンドで db push kivy/ storage/sdcard0 で kivyディレクトリ以下のプロジェクトファイルをコピー(storage/emulated/0/kivy 以下にプロジェクトディレクトリを準備)
  • プロジェクトはディレクトリにまとめて、android.txtファイルを準備(以下が例)
    • title にアプリの名前
    • author に作者名
    • orientation に画面の方向
title=accelerometer_basic
author=plyer example
orientation=portrait

5.4 実際の手順

  • Android端末(スマホ)に、kivy Launcherをインストール(Google Playストア)
  • Androidの端末でUSBデバッグ可能に設定(ファイル転送にadbコマンドを使う場合 Ubuntuでadbパッケージ)
    • Androidの設定を起動
      • システム→端末情報を開き、一番下の「ビルド番号」を何度もタップ(7から8回)すると、開発者むけオプションが有効になる
      • システムに戻ると、「開発者向けオプション」ができてるので、それをタップ、「USBデバッグ」を有効に変更
  • adbコマンド等で、/storage/emulated/0/kivy/ディレクトリ以下にプロジェクトファイルをコピー

5.4.1 kivyソースに含まれるサンプルをコピー

  • コピーするプロジェクトがはいっているディレクトリを用意
mkdir kivy
  • ソースをゲットするディレクトリを作成し、そのディレクトリに移動(使い慣れたツールでやってもOK)
mkdir github
cd github
  • kivy,plyerのソースをgitコマンドでコピー(ダウンロードして展開してもOK)
git clone https://github.com/kivy/kivy.git
git clone https://github.com/kivy/plyer.git
  • ソースに含まれているAndroid用サンプルを、一つしたのディレクトリのkivyディレクトリにコピー
cp -r kivy/examples/settings/ ../kivy/
cp -r kivy/examples/demo/showcase/ ../kivy/
cp -r kivy/examples/demo/pictures/ ../kivy/
cp -r kivy/examples/demo/touchtracer/ ../kivy/
cp -r kivy/examples/widgets/sequenced_images/ ../kivy/
cp -r kivy/examples/keyboard/ ../kivy/
cp -r kivy/examples/android/takepicture/ ../kivy/
cp -r kivy/examples/android/compass/ ../kivy/
cp -r plyer/examples/accelerometer/basic/ ../kivy/accelerometer_basic/
  • accelerometer_basic用のandroid.txtがないので追加 ( ../kivy/accelerometer_basic/android.txt )
title=accelerometer_basic
author=plyer example
orientation=portrait
  • PCとAndroidをUSBケーブルで接続
  • adbコマンドでAndroidにコピー
adb push ../kivy/ /storage/emulated/0/
  • android側でKivy Launcher起動
  • 動かしたいのを選択して起動

5.5 Kivy Launcherの使い方のまとめ

  • Kivy Launcher でapk作成せずに、手軽に効率良く、android本体で動作テストする手順
  • temuxとか、sl4aとか、QPython等があるが、色々なパッケージとの連携を行いながら、しかもapkパッケージ化が難しく、kivy Launcherが良さげ
  • 次の章で、Google Playストアで配布されているKivy Launcherには含まれていないplyerを含んだ、Kivy Launcherをビルドする

6 plyer入りのKivy Launcherの作成とインストール(好きなパッケージ入りのKivy Launcherの作り方)

6.1 以下の説明を行っている動画



6.2 関連文書

6.3 カスタマイズした、Kivy Launcherの作成手順

6.3.1 ソースゲット

git clone https://github.com/kivy/kivy-launcher.git

6.3.2 buildozer.specの requirements = kivy, androidの後に追加したパッケージを記述

  • plyer, python3, pyjniusを追加
...省略
requirements = kivy, android, plyer, python3, pyjnius
...省略

6.3.3 ビルドインストール

  • Androidを開発モードにして、USBデバッグ可能にしてから、PCとUSB接続
  • 以下のコマンドで、「Ubuntuでコンパイル」の章の https://www.youtube.com/embed/-xUmhSMK4Jk で作成した環境にはいる
source ~/kivy_buildozer/bin/activate
  • それから、以下のコマンドを実行
buildozer android debug deploy

6.4 この章のまとめ

  • Kivy Launcherをカスタマイズして、plyerのパッケージが必要なソースを動くようにしてみた

7 plyer関係の機能の利用方法の調べ方

  • exampleをメインで、関係文書を元に調べると、使い方を効率的に調べれそう

7.1 以下の説明を行っている動画

7.2 関係文書

7.3 手順

  • ソースに含まれるサンプルコードを調べれば使い方だいたいわかる。(あと公式文書、ソースも)
  • plyerのソースをgitコマンドでコピー(ダウンロードして展開してもOK)
git clone https://github.com/kivy/plyer.git
  • plyer/examples/ディレクトリ内に多数サンプルある
  • android用のバーチャル環境にはいり、apk生成出来る環境に
source ~/kivy_buildozer/bin/activate
  • buildozer.specファイルあるものは、そのディレクトリに移動し、以下のコマンドで、コンパイル、転送、実行
buildozer android debug deploy run
  • 一部のサンプルは動かないものもあった(Androidの機種によって、そのセンサーはいってないとか)
  • gpsサンプルは、simがはいっていて、位置情報の詳細がオン設定になっていないと動作しなかった(GPSじゃなくて、ネットで位置を調べているように感じた)

7.4 サンプルディレクトリ

  • accelerometer/basic/ 加速度計 こっちは正常動作
  • accelerometer/using_graph/ 加速度計 こっちは動かなかった
  • audio/ ファイルのパーミッション変更が必要
  • barometer/ 気圧計(F1 Playには気圧計ないみたい、Pixcel3aXLでは正常動作)
  • battery/ バッテリー関係
  • bluetooth/ 未チェック
  • brightness/ 起動できず
  • call/ 未チェック
  • camera/ 動作せず
  • compass/ コンパス(方位磁石) 起動できず
  • email/ eメイル,未チェック
  • filechooser/ ファイル選択
  • flash/ 未チェック
  • gps/ gps関係、gpsの電波がないところでも動作しており、simカードがないとエラーになっていた、基地局利用の位置情報?
  • gravity/ 重力
  • gyroscope/ ジャイロスコープ
  • humidity/ 湿度,未チェックorコンパイル時にエラー
  • light/ 明るさセンサー
  • notification/ 通知
  • orientation/ 向き?
  • proximity/ 近接?,enableでダウン
  • screenshot/ スクショ? buildozer.specがないため未チェック
  • sms/ SMS関係
  • spatialorientation/ 空間定位
  • speech2text/ 動作させたがスタートおしてもすぐに止まる、マイクの権限与えてもダメ
  • storagepath/ ストレージのパス
  • temperature/ 気温?、動作せず
  • text2speech/ 文字を発音、文字化けしてた
  • uniqueid/ ユニークなID取得
  • vibrator/ バイブ
  • wifi/ 未チェック

7.4.1 コンパイルして、次々とインストールする場合

  • Android端末をUSB接続して、plyer/exampleディレクトリにはいって、以下のコマンドを実行
cd accelerometer/basic/  ; buildozer android debug deploy run; cd ../..
cd accelerometer/using_graph/ ; buildozer android debug deploy run; cd ../..
cd audio/ ; buildozer android debug deploy run; cd ..
cd barometer/ ; buildozer android debug deploy run; cd ..
cd battery/ ; buildozer android debug deploy run; cd ..
cd bluetooth/ ; buildozer android debug deploy run; cd ..
cd brightness/ ; buildozer android debug deploy run; cd ..
cd call/ ; buildozer android debug deploy run; cd ..
cd compass/ ; buildozer android debug deploy run; cd ..
cd email/ ; buildozer android debug deploy run; cd ..
cd flash/ ; buildozer android debug deploy run; cd ..
cd gps/ ; buildozer android debug deploy run; cd ..
cd gravity/ ; buildozer android debug deploy run; cd ..
cd gyroscope/ ; buildozer android debug deploy run; cd ..
cd humidity/ ; buildozer android debug deploy run; cd ..
cd light/ ; buildozer android debug deploy run; cd ..
cd notification/ ; buildozer android debug deploy run; cd ..
cd orientation/ ; buildozer android debug deploy run; cd ..
cd proximity/ ; buildozer android debug deploy run; cd ..
cd sms/ ; buildozer android debug deploy run; cd ..
cd spatialorientation/ ; buildozer android debug deploy run; cd ..
cd speech2text/ ; buildozer android debug deploy run; cd ..
cd storagepath/ ; buildozer android debug deploy run; cd ..
cd temperature/ ; buildozer android debug deploy run; cd ..
cd text2speech/ ; buildozer android debug deploy run; cd ..
cd uniqueid/ ; buildozer android debug deploy run; cd ..
cd vibrator/ ; buildozer android debug deploy run; cd ..

7.5 Androidの特殊機能を利用する方法

7.5.1 pyler利用

  • クロスプラットフォームのソースになる
  • しかしiOSでサポートされていない機能多い

7.5.2 Pyjnius等を利用して、PythonからJavaを利用

  • Androidの開発ツールのライブラリを利用できる
  • 情報少ない

7.5.3 import Androidで利用

  • 情報少ない(あまり文書をみつけれてない、みつけたらまたリンクはっときます)

7.6 この章のまとめ

  • スマホ固有の機能の動作させるライブラリplyer関係の使い方の調べ方紹介
  • かたっぱしからコンパイルしてインストールすると相当時間かかるが、ソースみるまえに動作させた方がわかりやすい
  • exampleをメインで、関係文書を元に調べると、使い方を効率的に調べれそう
  • 全部コンパイルすると、30-40Gの容量が必要

8 広告

8.1 以下の説明を行っている動画



8.2 使えそうなパッケージ

8.2.2 手順

  • 以下の手順で(Ubuntuでコンパイルhttps://www.youtube.com/embed/EF0PNSlblYYの手順をDocker内のUbuntuではなく、通常のUbuntuで行って構築した環境)apkを作成できる環境に移行
source ~/kivy_buildozer/bin/activate
  • KivMod Github https://github.com/MichaelStott/KivMobの記述どうりの手順で行う(INTERSTITIALタイプ、全画面タイプで広告表示)
  • package.domain部分がorg.testのままだと、広告が表示されないので、org.kivyなどorg.test以外に変更
  • 以下のコマンドでインストール実行
buildozer android debug deploy run
  • KivMob Tutorial http://kivmob.com/tutorials.htmlにあるBannerタイプとか、Rewarded Videoタイプ(広告のかわりに、なにかユーザーにプレゼントするタイプ)とかもコンパイルして実行する

8.3 この章のまとめ

  • KivModを利用すると、GoogleのAdMobを利用して簡単に色々なタイプの広告を表示可能

9 課金

9.1 以下の説明を行っている動画

10 KivyMD

  • ナイスなルックスで、豊富な部品があるライブラリ
  • Kivy純正の部品よりも、KivyMDの部品で作成した方がルックス良いアプリ作成簡単にできそう

10.1 以下の説明を行っている動画

10.3 install (Ubuntu ユーザースペース)

  • pip install kivymd==0.104.1 をしろとGitHubに書いてあるが、以下でも同じバージョンがはいった(2020/08/21)
pip3 install kivymd
  • その時のアウトプット
Collecting kivymd
  Using cached kivymd-0.104.1-py3-none-any.whl (2.4 MB)
Requirement already satisfied: kivy in /usr/lib/python3/dist-packages (from kivymd) (1.10.1)
Requirement already satisfied: pillow in /usr/lib/python3/dist-packages (from kivymd) (7.0.0)
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from kivymd) (2.22.0)
Installing collected packages: kivymd
Successfully installed kivymd-0.104.1

10.4 pkg作成時にKivyMDを利用する

  • buildozer.specのrequestsにkivymdを追加

10.5 デモのコンパイルと実行

  • KivyMDのソースゲット
git clone https://github.com/kivymd/KivyMD.git
  • 取ったソースのバックアップ(20200821にゲットしたので、その日付をつけた、KivyMD.20200821.tgz を作成)
tar cvzf KivyMD.20200821.tgz KivyMD/
  • 前作成したapk作成環境に移行(virtualenvで作成した環境にはいる)
source ~/kivy_buildozer/bin/activate
  • AndroidをUSBケーブルでPCと接続
  • デモのあるディレクトリに移動し、コンパイル実行
cd KivyMD/demos/kitchen_sink/
buildozer android debug deploy run

10.6 各部品の使い方の調べ方

  • 上の文書のリンクと、動かしたサンプルソースのソースを調べれば、簡単に使い方を調べることが可能
  • 使う部品は

10.7 この章のまとめ

  • ルックスの良いKivyMDを紹介
  • デフォルトのKivyの部品よりもKivyMDの部品を使うべき
  • 動画では、サンプルの動作の様子も紹介

11 デバッグ手法いくつか紹介

11.1 以下の説明を行っている動画

11.3 手順

  • USBデバッグを可能にしたAndroid実機とPC接続
  • ソースにprint文とかlogging文をいれる
  • 特殊な文字列をいれとくと後で検索しやすくなる
  • 確認したい行の前後にいれるとかも良い
  • そこを通過してるかの確認にも使える
  • 変数の値のチェックにも便利
  • 以下はLoggerを使っているが、print文でもログに出る
from kivy.logger import Logger
... 省略
Logger.info('#####情報#####')
  • テストしたい動作の手前で以下を実行し、ログを適当なファイル名で保存(下の例だとlog001.txt)
adb logcat > log001.txt 2>&1
  • 以下にしとくと画面にも表示しながらファイルに保存
adb logcat 2>&1 | tee log001.txt

11.4 バグの例

  • org.renpy.android.PythonActivity’がみつからない例
08-22 21:10:02.938 30206 30239 I python  :  jnius.jnius.JavaException: Class not found b'org/renpy/android/PythonActivity'
  • 特殊文字をいれてわかりやすくした例
........... 省略
08-23 15:20:12.353 15129 15161 I python  : [INFO   ] #####before vibrate#####
08-23 15:20:12.353 15129 15161 I python  : [INFO   ] [Base        ] Leaving application in progress...
........... 省略
08-23 15:20:12.357 15129 15161 I python  :    File "/storage/emulated/0/kivy/vibrate02/main.py", line 56, in vibrate
08-23 15:20:12.357 15129 15161 I python  :      jvibrator.vibrate(1000)
08-23 15:20:12.358 15129 15161 I python  :    File "jnius/jnius_export_class.pxi", line 745, in jnius.jnius.JavaMethod.__call__
08-23 15:20:12.358 15129 15161 I python  :  jnius.jnius.JavaException: Invalid call, number of argument mismatch, got 1 need 4
08-23 15:20:12.358 15129 15161 I python  : Python for android ended.
........... 省略

11.5 この章について

  • 低次元のデバッグ方法の一種を紹介

12 Kivy Launcherの拡張その2

12.1 以下の説明を行っている動画

12.2 関係動画

12.3 関係ソース

12.4 以下のようにKivy Launcherのbuildozer.specのrequestsを追加

  • opencv関係や、sympy関係もいれたかったが、現状のバージョンだと、エラーになる、配布元でのデバッグ終わったら入れるべき
requirements = kivy, android, plyer, python3, pyjnius, kivmob, kivymd, cymunk, pymunk, pandas, libcurl, Pillow, websocket-client, vispy, regex, numpy, pandas

12.5 KivMobhttps://github.com/MichaelStott/KivMobのサンプル用の修正を行う(buildozer.spec)

12.6 この章のまとめ

  • これによってかなりのものを実際にapkファイルを作成することなく、Kivy Launcherで動作確認可能になる
  • 広告関係のサンプルも、Kivy Launcherでテスト可能に

13 pyjniusを使って、java経由でAndroid特有の機能の利用

13.1 以下の説明を行っている動画

13.3 バイブを行ってみた

13.3.1 まずは、pyjniusをつかわず、plyerを使って、バイブできるのを作った

from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from plyer import vibrator
Builder.load_string('''
#:import vibrator plyer.vibrator
<My>:
    orientation: 'vertical'
    Label:
        id: label1
        text: 'Hello'
        font_size: 150
    Button:
        text: 'vibrate'
        font_size: 80
        on_release: vibrator.vibrate(5)
''')

class My(BoxLayout):
    pass

class HelloWorldApp(App):
    def build(self):
        return My()

if __name__ == '__main__':
    HelloWorldApp().run()
  • android.txtが以下(3行にすること)
title=Vibrate 001 android.vibrate
author=NM Max
orientation=portrait

13.3.2 pyjniusを使うバージョン

  • https://kivy.org/doc/stable/guide/android.html の断片ソースを利用してやろうとしたら酷い目にあった
    • バージョンが新しいとこのソース動かない
    • org.renpy.android.PythonActivity は org.kivy.android.PythonActivity になってた
      • 前の前の章のデバッグ手法で気づいた org.renpy.android.PythonActivity でつまづいていることに
    • そこらもプログラムで取得可能なようで、plyerのソースを参考に変更した
    • Androidの開発環境のVibrator関係も変更になっていて、vibrate関数の使用が変更となり、VibrationEffectを利用するようになってた
  • plyerのソースの plyer/platforms/android/__init__.py と、plyer/platforms/android/vibrator.pyを参考に作成
  • そうして改造したのが以下 main.py
from os import environ
from jnius import autoclass, cast

try:
    from android import config
    ns = config.JAVA_NAMESPACE
except (ImportError, AttributeError):
    ns = 'org.renpy.android'
PythonActivity = autoclass(ns + '.PythonActivity')
activity = PythonActivity.mActivity

ANDROID_VERSION = autoclass('android.os.Build$VERSION')
SDK_INT = ANDROID_VERSION.SDK_INT

Context = autoclass("android.content.Context")
vibrator_service = activity.getSystemService(Context.VIBRATOR_SERVICE)
vibrator = cast("android.os.Vibrator", vibrator_service)
if SDK_INT >= 26:
    VibrationEffect = autoclass("android.os.VibrationEffect")

from kivy.logger import Logger
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder

Builder.load_string('''
<My>:
    orientation: 'vertical'
    Label:
        id: label1
        text: 'Hello'
        font_size: 150
    Button:
        text: 'vibrate'
        font_size: 80
        on_release: root.vibrate(5)
''')

class My(BoxLayout):
    def vibrate(self,x):
        Logger.info('#####before vibrate#####')
        if vibrator:
            if SDK_INT >= 26:
                vibrator.vibrate(VibrationEffect.createOneShot(int(1000 * x), VibrationEffect.DEFAULT_AMPLITUDE))
            else:
                vibrator.vibrate(int(1000 * x))
        Logger.info('#####after vibrate#####')

class HelloWorldApp(App):
    def build(self):
        return My()

if __name__ == '__main__':
    HelloWorldApp().run()
  • android.txtが以下(3行にすること) Kivy Launcher用
title=Vibrate 002 pyjnius.vibrate
author=NM Max
orientation=portrait
  • ( buildozer initしたのち)buildozer.specの変更内容
    • title
      • title = My Application を title = Pyjnius vibrator test に修正
    • requirements
      • requirements = python3,kivy を requirements = python3,kivy,pyjnius,plyer に修正
    • android.permissions = INTERNET, VIBRATE を追加
    • buildozer android debug deploy run すれば、USB接続してあるUSBデバッグオンになっているandroidにインストールされ、実行される(binディレクト以下にapkファイルも生成される)

13.4 この章のまとめ

  • 公式の文書に古い記述があり、新しいバージョンでは動かないものも多い
  • plyerやpython-for-androidのソースを参考にすると効率良く動くものをつくれる
  • Kivy用に用意されていないもので、Android用に用意されているJavaライブラリを、pyjniusを使えるようになることで、利用可能になった

14 Kivy Launcherの拡張その3

14.1 以下の説明を行っている動画

14.2 関係文書

14.3 関係動画

14.4 関係ソース

14.5 Kivy Launcherカスタマイズ3の手順

14.5.1 現状(2020/08/28時点)でbuildozerでKivy Gardenを入れる為の手順の説明ありhttps://stackoverflow.com/questions/62260608/kivy-garden-mapview-deploying-error-with-buildozer

  • ~/kivy_buildozer/にvirturlenvで環境作った場合、~/kivy_buildozer/lib/python3.8/site-packages/buildozer/__init__.pyを修正
    • Kivy Gardenのバージョンを0.1.1から0.1.4に変更
self.cmd('pip install Kivy-Garden==0.1.1', env=self.env_venv)
を
self.cmd('pip install Kivy-Garden==0.1.4', env=self.env_venv)
に修正

14.5.2 Kivy Launcherのbuildozer.specのrequirementsを以下に修正

  • ベースは Kivy Launcherのカスタマイズ手順2 https://www.youtube.com/watch?v=Fd4jX-RJ9es の動画でおこなった修正後のファイルに以下の修正を行う
  • pygame, matplotlib, websocket-client, snappy,requests,certifi,urllib3,chardet,idna を追加している
    • pygame はPython用のゲームエンジンの一つ
    • matplotlib はプロット用
    • websocket-client は多分ウェッブ系の部品だと思われる
    • snappy は Python bindings for the snappy compression library from Google.らしい
    • requests,certifi,urllib3,chardet,idna
      • requestsは adb logcat で確認したら無いってエラーが出てた、それ以外にも必要なパッケージあるかもしれないので、以下を参考にそれ以下も入れた
      • FarmersMarketFinderTutorial動画のソースhttps://github.com/Dirk-Sandberg/FarmersMarketFinderTutorialで入れられていたパッケージ
requirements = kivy, android, plyer, python3, pyjnius, kivmob, kivymd, cymunk, pymunk, pandas, libcurl, Pillow, websocket-client, vispy, regex, numpy, pandas, pygame, matplotlib, websocket-client, snappy,requests,certifi,urllib3,chardet,idna
  • 本当はOpenCV関係や、Sympy関係も入れたいが、まだ入れれてない

14.5.3 Kivy Launcherのbuildozer.specのgarden_requirementsを以下に修正

  • 沢山あるが、とりあえず、2つ入れた、他にも試したいのあれば、入れてOKう
garden_requirements = matplotlib, mapview

14.5.4 Kivy Launcher ビルドインストール

  • Kivy Launcherのカスタマイズ手順 https://www.youtube.com/watch?v=YyYNBL0VtdM の手順のビルドインストールコマンド実行部分と同じ(設定ファイルの変更はこのリンクの動画ではなく、上の修正を行う)
  • Androidを開発モードにして、USBデバッグ可能にしてから、PCとUSB接続
  • 以下のコマンドで、「Ubuntuでコンパイル」の章の https://www.youtube.com/embed/-xUmhSMK4Jk で作成した環境にはいる
source ~/kivy_buildozer/bin/activate
  • それから、以下のコマンドを実行
buildozer android debug deploy

14.5.5 mapviewEx02ディレクトリを作成し、必要なandroid.txtとmain.pyをいれる

./mapviewEx02/
├── android.txt
└── main.py

14.5.6 android.txtの中身

  • Kivy Launcher用のファイル
title=MapViewEx02
author=NM Max
orientation=portrait

14.5.7 Kivy Gardenのmapviewのexampleのmap_browser.pyを改造し、main.pyとして以下のようにmapviewEx02ディレクトリに入れる

  • 修正箇所は、Pythonの検索パスに、gardenのmapviewを入れるように設定(ファイルの前半部分)
  • 東京駅の座標を入れ、東京のボタンも増やした
  • スマホだとちょっと文字が大きすぎて崩れていたが、とりあえず放置
  • mainのファイルの変数を、kv言語側にもっていくには、#:import MapSource main.MapSource こういう感じでやれば良いらしい
    • ネットをうろうろしていた時に見つけたサイトで知ったのだが、URLをメモしておらず、URLわからない、英語のサイトだった
import sys
import os
print("########")
print(sys.path)
print("########")
#sys.path.append(os.path.join(os.path.dirname(__file__), 'libs/garden/garden.mapview'))
sys.path.append('./libs/garden/garden.mapview')
print("########")
print(sys.path)
print("########")
print(os.listdir(path='.'))
print("########")
from kivy.base import runTouchApp
from kivy.lang import Builder
from mapview import MapSource

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))

#:import MapSource __main__.MapSource
root = Builder.load_string("""
#:import MapSource mapview.MapSource

<Toolbar@BoxLayout>:
    size_hint_y: None
    height: '48dp'
    padding: '4dp'
    spacing: '4dp'

    canvas:
        Color:
            rgba: .2, .2, .2, .6
        Rectangle:
            pos: self.pos
            size: self.size

<ShadedLabel@Label>:
    size: self.texture_size
    canvas.before:
        Color:
            rgba: .2, .2, .2, .6
        Rectangle:
            pos: self.pos
            size: self.size

RelativeLayout:

    MapView:
        id: mapview
        lat: 50.6394
        lon: 3.057
        zoom: 8
        #size_hint: .5, .5
        #pos_hint: {"x": .25, "y": .25}

        #on_map_relocated: mapview2.sync_to(self)
        #on_map_relocated: mapview3.sync_to(self)

        MapMarker:
            lat: 50.6394
            lon: 3.057

        MapMarker
            lat: -33.867
            lon: 151.206

        MapMarker
            lat: 35.6809591
            lon: 139.7673068

    Toolbar:
        top: root.top
        Button:
            text: "Move to Lille, France"
            on_release: mapview.center_on(50.6394, 3.057)
        Button:
            text: "Move to Sydney, Autralia"
            on_release: mapview.center_on(-33.867, 151.206)
        Button:
            text: "Move to Tokyo, Japan"
            on_release: mapview.center_on(35.6809591, 139.7673068)
        Spinner:
            text: "mapnik"
            values: MapSource.providers.keys()
            on_text: mapview.map_source = self.text

    Toolbar:
        Label:
            text: "Longitude: {}".format(mapview.lon)
        Label:
            text: "Latitude: {}".format(mapview.lat)
    """)

runTouchApp(root)

14.5.8 Kivy Launcher が読み取れるディレクトリに、作ったファイルをコピー(adbコマンドを利用してPCからAndroidにコピー, AndroidとPCはUSB接続し、AndroidはUSBデバッグ可能な設定済み)

  • mapviewEx02/ディレクトリ内にいるなら、以下のコマンドでOK
adb push ../mapviewEx02/ /storage/emulated/0/kivy/
  • 後は起動して、MapViewEx02を選択肢から選択して、起動すればOK

14.6 この章のまとめ

  • Kivy Gardenを使いたくて、非常に苦労して成功した時の手順説明
  • Kivy Garden mapview 素晴らしすぎる
  • 地図を表示するアプリを簡単に作れるようになった
  • Pythonの検索パスの変更方法や、Androidパッケージ内部での検索パスの調べ方を知った
    • Kivy Launcher のビルドディレクトリの .buildozer/android/app/ 内にパッケージ化するファイル群がある
    • 本来検索パスの設定変更無しにいけるはずだが、その方法まだわかっておらず。知ってる方いたら、教えて下さい。

15 WebView(ウェブブラウザの部品への埋め込みandroid)

15.1 以下の説明を行っている動画

15.2 関係文書

from kivy.utils import platform
....省略
 if platform == 'android':
   ...android用の処理...

15.3 WebViewを使ってみる手順

from kivy.app import App
from jnius import autoclass
from kivy.clock import Clock
from android.runnable import run_on_ui_thread
from kivy.uix.widget import Widget

WebView = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
activity = autoclass('org.kivy.android.PythonActivity').mActivity

@run_on_ui_thread
def create_webview(*args):
        webview = WebView(activity)
        webview.getSettings().setJavaScriptEnabled(True)
        wvc = WebViewClient();
        webview.setWebViewClient(wvc);
        activity.setContentView(webview)
        webview.loadUrl('https://blog.123abcsoft.com/2020/07/31/kivy-%E3%81%A8-buildozer-%E3%81%A7-%E3%82%B9%E3%83%9E%E3%83%9B%E3%82%A2%E3%83%97%E3%83%AA%E4%BD%9C%E6%88%90')

class Wv(Widget):
        def __init__(self, **kwargs):
                super().__init__(**kwargs)
                self.__functionstable__ = {}
                Clock.schedule_once(create_webview, 0)


class ServiceApp(App):
        def build(self):
                return Wv()

# https://github.com/kivy/python-for-android/issues/1908

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

15.4 この章のまとめ

  • 部品として、WebViewを使う方法を調べて、動作することを確認した
  • リンク先をクリックするとリンク先に飛ぶ
  • 戻るが効かない、戻るで1つ前に戻るには更にプログラムが必要らしい

16 Kivy Launcherの拡張その4

  • Sympyを使ったアプリを作りたかったが、p4aにSympyのインストール手順が用意されているが、古い為か、現状はbuilder.specのrequirementsにsympyを追加しても、途中でエラーになって入れれなかった
  • KivyMDを利用する時に発生することが多い意味不明のエラー回避の為にKivyのバージョンを2.0.0rc3に
  • pyopenssl追加
  • 日本語フォントを追加
  • KivyMD関連画像を追加

16.1 関連動画、関連文書

16.2 以下の説明を行っている動画

16.3 手順

  • Kivy Launcherのソースのbuildozer.specのrequirementsを以下に修正
  • kivyのバージョンを2.0.0rc3指定、pyopenssl追加
requirements = kivy==2.0.0rc3, android, plyer, python3, pyjnius, kivmob, kivymd, cymunk, pymunk, pandas, libcurl, Pillow, websocket-client, vispy, regex, numpy, pandas, pygame, matplotlib, websocket-client, snappy,requests,certifi,urllib3,chardet,idna,pyopenssl
  • buildozer.specのsource.include_exts を以下に変更
source.include_exts = py,png,jpg,kv,atlas,ttf
  • buildozer.specのsource.include_patterns を以下に変更
source.include_patterns = images/*,fonts/*

16.3.1 日本語フォント追加

  • Kivy Launcherのソースディレクトリにfontsディレクトリを作成し、IPAフォントファイルをそのディレクトリ内にコピーした
  • Ubuntuなら以下のコマンドでOK(ipaフォントのパッケージイスんトール後、 fonts-ipaexfont-gothic fonts-ipaexfont-mincho fonts-ipafont-gothic fonts-ipafont-mincho )
sudo apt install fonts-ipaexfont-gothic fonts-ipaexfont-mincho fonts-ipafont-gothic fonts-ipafont-mincho 
mkdir fonts
cp /usr/share/texlive/texmf-dist/fonts/truetype/public/ipaex/* fonts/
  • ファイルは、私の環境では以下になった
fonts
├── ipaexg.ttf
├── ipaexm.ttf
├── ipag.ttf
├── ipagp.ttf
├── ipam.ttf
└── ipamp.ttf

16.3.2 KivyMDのロゴ画像を入れた

  • 必要性は低いけど
  • Kivy Launcherのソースディレクトリにimagesディレクトリを作成し、kivyMDのロゴ画像を入れた。画像はKivyMDのGitHubから得た
  • ファイルは、私の環境では以下になった
images/
└── kivymd.png

16.3.3 Kivy LauncherにSympy追加

  • https://github.com/kivy/python-for-android/issues/2303の情報で、Sympy(数式処理パッケージ、前から入れたかった)を入れる方法2つ紹介されている。そのうちの一つ、Pythonで書かれているので、それらのライブラリを、ディレクトリにコピーする手法を選択した
  1. buildozer用のvirturlenvにはいる
    • 私の場合は ~/kivy_buildozer/ に作成したので、以下のコマンドを実行
    cd ~/kivy_buildozer/
    source bin/activate
    
  2. Sympyをインストール
    pip3 install sympy
    
  3. Kivy Launcher のソースディレクトリに移動
    • その後、取ってきてあるkivy Launcherのソースディレクトリに行き,sympyと、それに必要なmpmathをコピーしてくる
    • ~/kivy_buildozer/ の部分は作成したvirturlenvのディレクトリに依存するので、適時変更してください
    cp -r ~/kivy_buildozer/lib/python3.8/site-packages/mpmath .
    cp -r ~/kivy_buildozer/lib/python3.8/site-packages/sympy .
    
  4. ビルドインストール
    • Android端末をPCに接続して以下のコマンドを実行
    • Android端末側でポップアップがでたら、リモートでバッグ可能なように操作
    buildozer android debug deploy run
    
  5. テスト用のプログラムを作成して動かしてみる
    • SympyTest ディレクトリ内に用意する
    mdkir SympyTest
    cd SympyTest
    
    • android.txtを以下で用意
    title=SympyTest
    author=NM Max
    orientation=portrait
    
    • main.pyを以下で作成
    from kivy.app import App
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.label import Label
    import sympy
    
    
    x=sympy.Symbol('x')
    y=sympy.Symbol('y')
    
    
    class MyApp(App):
    
        def build(self):
            box = BoxLayout()
            box.orientation = 'vertical'
            box.add_widget(Label(text=str(sympy.expand((x+y)**3)),font_size="20sp"))
            box.add_widget(Label(text=str(sympy.integrate(1/x**3,x)),font_size="30sp"))
            box.add_widget(Label(text=str(sympy.diff(x**2,x)),font_size="30sp"))
            box.add_widget(Label(text=str(sympy.Or(x,y)),font_size="30sp"))
            return box
    
    MyApp().run()
    
  6. ファイルを転送
    • 以下のコマンドで転送する
    adb push ../SympyTest/ /sdcard/kivy/
    
  7. Android端末で動かしてみる
    • 上手く動作した

16.4 この章のまとめ

  • Sympyを入れたバージョンを作成
  • KivyMDと相性のあんま良くないkKivy1系ではなく、最新のKivy2系を入れた
  • 日本語フォントや、KivyMDのロゴも入れておいた

17 Kivyでmatplotlibでグラフ

  • Kivy + Kivy Garden matplotlib + matplotlibでグラフ描いてみた

17.1 関連動画、関連文書

17.2 以下の説明を行っている動画

17.3 手順

  • virtualenvにはいる(私の場合は ~/kivy_buildozer/bin 以下に構築してある)
source ~/kivy_buildozer/bin/activate
  • ディレクトリ matplotlib01 用意
  • コマンドラインでやるなら以下
mkdir matplotlib01
cd matplotlib01
buildozer init
  • buildozer.specのrequestsと,garden_requirementsの行を以下に修正
requirements = python3, kivy==2.0.0rc3, android, jnius, kivmob, kivymd, pandas, numpy, matplotlib
garden_requirements = mapview, matplotlib
  • 以下の内容でmain.pyを用意
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
import matplotlib.pyplot as plt
import numpy as np
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg

fig, ax = plt.subplots()  
xx=range(5)
ax.plot(xx, list(map(lambda x:x**2,xx)), label='x**2', linestyle='--', marker='o')  
ax.plot(xx, list(map(lambda x:x+3,xx)), label='x+3', linestyle='--', marker='o')  
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("title")
ax.legend()
ax.grid()

class MyApp(App):
    def build(self):
        box = BoxLayout()
        box.orientation = 'vertical'
        box.add_widget(FigureCanvasKivyAgg(fig))
        return box

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

17.3.1 Ubuntu側でテストするようにvirturlenvにmatplotlibをインストール、2020/10/01で、最新のmatplotlibをUbuntu20.04で動かそうとするとエラーになるため、2.2系をインストール

  • まずバーチャル環境にはいる(私は~/kivy_buildozer/に準備してある)
source ~/kivy_buildozer/bin/activate
  • それから3.2系最新のmatplotlibをインストール
pip3 install matplotlib==3.2.2
  • main.pyを動かしてみる
python3 main.py

17.3.2 Androidで実行

  • AndroidとUSB接続して以下を実行
buildozer android debug deploy run

17.4 この章のまとめ

  • Kivy で matplotlib を利用して、グラフ描画(Kiby Gardenのmatplotlibライブラリも利用)できた
  • Kivy Launcherでも動かそうとしたが、なぜかKivy Gardenのmatplotlibが読み込めなかった、そのため、新規パッケージ方式でやった。今後解決方法みつけたら、また文書と動画にします。

18 Kivy+Buildozerで作成したAndroidアプリをGoogle Play Storeに出品する手順

18.1 関連動画、関連文書

18.2 手順

  1. AdMobへの登録とアップロード
    • 登録料不要だった。
    • アプリ名と、広告タイプを登録し、アプリのkeyと、広告のkeyをゲット
  2. buildoze.specとソース修正
    • AdMobのkeyがテスト用になっていたので、それを修正しこのアプリ用のKeyに置き換えるようにbuildozer.specとソース修正
    • 64bitCPU用にする必要があった(32bit用だと審査さえしてもらえなくなってる2020/10/23時点)) buildozer.specの armeabi-v7a を arm64-v8a に変更
    • 来年くらいに32bit用はGoogle Playストアから駆逐されるらしい。2年前くらい前発売のAndroidはみな64bitになってて、その前数年が32bitCPUと64bitCPUが混在。その前は32bitCPUだったらしい。
    android.arch = arm64-v8a
    
    • Androidのバージョンが29対応じゃないと審査してもらえない(2020/10/23時点)。buildozer.specの 27を29に。将来もっと上のバージョンの必要がでてくることが予想される
    android.api = 29
    

18.2.1 リリースバージョンの作成

  • .buildozer/ 以下を削除してから、以下のコマンドで作成しなおした 
buildozer android release
  • 今回のSympyCalculatorだと sympycalclator-0.0.2-arm64-v8a-release-unsigned.apk が出来た。(アプリ名とバージョン番号はbuildozer.specの設定によってかわる)
  • Google Play Storeに出品する為には署名が必要だった。

18.2.2 サイン

  1. key作成
    • alies を test1 にした
    • 以下のコマンドでkeyを生成
    • 姓名、県、市、等を聞かれるから、ちゃんと回答、組織単位名は何も入力せずエンターした(私の場合、ある場合は入れて下さい)国のコードはJAにした。
    • 10,000日間有効な2,048ビットのRSAの鍵ペアと自己署名型証明書が生成された、これらを利用して署名した
    mkdir ./keystores
    keytool -genkey -v -keystore ./keystores/sympycalculator-release-key.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias test1
    
  2. サイン作業
    • これやると良いらしい
    zipalign -v -p 4 ./bin/sympycalclator-0.0.2-arm64-v8a-release-unsigned.apk ./bin/sympycalclator-0.0.2-arm64-v8a-release-unsigned-aligned.apk
    
    • サインする
    apksigner sign --ks ./keystores/sympycalculator-release-key.keystore --out ./bin/sympycalclator-0.0.2-arm64-v8a-release.apk  ./bin/sympycalclator-0.0.2-arm64-v8a-release-unsigned-aligned.apk
    
    • 以下で確認
    apksigner verify ./bin/sympycalclator-0.0.2-arm64-v8a-release.apk
    
    • これで Google Playに登録可能な sympycalclator-0.0.2-arm64-v8a-release.apk が作成できた
  3. GooglePlayへの登録と、アップロード
    • 登録料で3000円弱払った(為替レートで変化するはず,25ドル)
    • xxxxxxxxxxxxxxxxxxxはGoogle Play Storeのサイトに書いてある方法。色々サインの方法があったけど、この記述があるのを選んだ
    • これでoutput.zipを生成して、Google Play Storeにkeyを登録。その後apkをアップロード出来るようになるので、上の手順でサインしたリリースバージョンのapkをアップロードする。私は最初はオープンテストを選択した。審査に3日ほど必要だった。審査後に公開された。
    • foo.keystore と foo はkey生成時のファイルとワードに変更した
    java -jar pepk.jar --keystore=foo.keystore --alias=foo --output=output.zip --include-cert --encryptionkey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    

18.3 この章のまとめ

  • 64bitCPU用にビルドする必要があった
  • サインするのが結構面倒だった
  • apiを29以上にする必要あった(2020/10/23時点で)今後これはもっと上のバージョンになって行く

19 Googleのツールのbuildozerによる自動ダウンロードが出来なくなっています。(恐らく一時的?次のバージョンアップで解消される? 2021/02/01)

  • いつ出来なくなったのかは、2020/10/25〜2021/2/1のいつか(この間でパッケージビルドしてない為正確な日付不明)

19.1 以下の説明を行っている動画

19.2 手順

  • https://youtu.be/3U5_330aySk のようにして、あらかじめ、コマンドラインにて、必要な開発環境を用意
  • 上の手順でANDROID_SDK_ROOT にセットしたディレクトリ直下に、cmdline-tools/バージョン を tools にシンボリックリンク (最新バージョンインストールしてるなら SDKをいれようとしているディレクトリで、 ln -s cmdline-tools/latest tools)
  • 19cのndkを追加インストール(19cより新しいバージョンいれて、buildozerを動かすと、p4aが推奨してないとエラーが出る。 19bは sdkmanager ではインストール出来ない為, 19bは19cをインストールした時に表示されるURL名のcをbに変更すればダウンロード可能なので、それを ndk/android-ndk-r19b/ に移動)
  • v24のビルドツールを追加インストール(最新バージョンでエラーが出るかも知れないので追加インストールした。最新バージョンでもいけるかも、試してない)
  • buildozer.specのandroid.ndk_path, android.sdk_path, android.ant_pathを設定し

19.3 この章のまとめ

  • 今までの手順でパッケージ作成が出来なくなっているので、可能になる手順の一つを紹介
  • 今後のbuildozerのバージョンアップにて、この問題は恐らく解消?(個人的な予想)

20 opencvのコンパイル方法(邪道2021/02/05)

  • buildozerでopencvをインストールすることが出来なかった
  • buildozerでコンパイルに成功したので、その手順を紹介

20.1 以下の説明を行っている動画

20.2 手順

20.2.1 前準備

  • https://youtu.be/3U5_330aySk のようにして、あらかじめ、コマンドラインにて、必要な開発環境を用意(ndk,sdk,cmake等, Android Studioで用意してもよいかも)
  • 上の手順でANDROID_SDK_ROOT にセットしたディレクトリ直下に、cmdline-tools/バージョン を tools にシンボリックリンク (最新バージョンインストールしてるなら SDKをいれようとしているディレクトリで、 ln -s cmdline-tools/latest tools)
  • 19cのndkを追加インストール(19cより新しいバージョンいれて、buildozerを動かすと、p4aが推奨してないとエラーが出る。 19bは sdkmanager ではインストール出来ない為, 19bは19cをインストールした時に表示されるURL名のcをbに変更すればダウンロード可能なので、それを ndk/android-ndk-r19b/ に移動)
  • v24のビルドツールを追加インストール(最新バージョンでエラーが出るかも知れないので追加インストールした。最新バージョンでもいけるかも、試してない)
sdkmanager --list_installed
  • 上のコマンド実行時の出力が以下
    • cmakeは2種類は不要(試行錯誤したので2種類いれた,新しい方だけでOK)
Path                 | Version      | Description                             | Location             
-------              | -------      | -------                                 | -------              
build-tools;24.0.3   | 24.0.3       | Android SDK Build-Tools 24.0.3          | build-tools/24.0.3/  
build-tools;30.0.3   | 30.0.3       | Android SDK Build-Tools 30.0.3          | build-tools/30.0.3/  
cmake;3.10.2.4988404 | 3.10.2       | CMake 3.10.2.4988404                    | cmake/3.10.2.4988404/
cmake;3.6.4111459    | 3.6.4111459  | CMake 3.6.4111459                       | cmake/3.6.4111459/   
cmdline-tools;latest | 3.0          | Android SDK Command-line Tools (latest) | cmdline-tools/latest/
emulator             | 30.3.5       | Android Emulator                        | emulator/            
ndk-bundle           | 22.0.7026061 | NDK                                     | ndk-bundle/          
ndk;19.2.5345600     | 19.2.5345600 | NDK (Side by side) 19.2.5345600         | ndk/19.2.5345600/    
patcher;v4           | 1            | SDK Patch Applier v4                    | patcher/v4/          
platform-tools       | 30.0.5       | Android SDK Platform-Tools              | platform-tools/      
platforms;android-27 | 3            | Android SDK Platform 27                 | platforms/android-27/
platforms;android-29 | 5            | Android SDK Platform 29                 | platforms/android-29/
platforms;android-30 | 3            | Android SDK Platform 30                 | platforms/android-30/
Path                                     | Version      | Description                             | Location                                 
-------                                  | -------      | -------                                 | -------                                  
build-tools;30.0.3                       | 30.0.3       | Android SDK Build-Tools 30.0.3          | build-tools/30.0.3/                      
cmake;3.18.1                             | 3.18.1       | CMake 3.18.1                            | cmake/3.18.1/                            
cmdline-tools;latest                     | 5.0.0 rc1    | Android SDK Command-line Tools (latest) | cmdline-tools/latest/                    
emulator                                 | 30.4.3       | Android Emulator                        | emulator/                                
ndk;22.0.7026061                         | 22.0.7026061 | NDK (Side by side) 22.0.7026061         | ndk/22.0.7026061/                        
patcher;v4                               | 1            | SDK Patch Applier v4                    | patcher/v4/                              
platform-tools                           | 30.0.5       | Android SDK Platform-Tools              | platform-tools/                          
platforms;android-30                     | 3            | Android SDK Platform 30                 | platforms/android-30/                    
sources;android-30                       | 1            | Sources for Android 30                  | sources/android-30/                      
system-images;android-30;google_apis;x86 | 9            | Google APIs Intel x86 Atom System Image | system-images/android-30/google_apis/x86/

20.2.2 buildozerでパッケージ作成

  • 今までのbuildozerの手順でパッケージ作成
  • 前の章の内容のように、現状Googleの自動ダウンロードできなくなっている(2021/2/5現在、将来のバージョンで解決するとは思います)対応
    • buildozer.specのandroid.ndk_path, android.sdk_path, android.ant_pathを設定(上記用意した、NDKやSDKなどにパスを設定する。ndkは新しすぎるとエラーになるので、19系の最新のもので行った)
  • 必要パッケージにopencv追加
  • 作成しようとするとOpenCVのビルド関係でエラーがでるので以下を行った。(これはAndroidの開発環境をホームディレクトリの/Android/SDK/ (${HOME}/Android/SDK/)に入れた場合、他の場所にAndroid環境を入れた場合は、そのパスに設定)
cp ${HOME}/Android/SDK/ndk/19.2.5345600/build/cmake/android.toolchain.cmake ./.buildozer/android/platform/build-arm64-v8a/build/other_builds/opencv/arm64-v8a__ndk_target_21/opencv/cmake/android/OpenCVDetectAndroidSDK.cmake
chmod -w ./.buildozer/android/platform/build-arm64-v8a/build/other_builds/opencv/arm64-v8a__ndk_target_21/opencv/cmake/android/OpenCVDetectAndroidSDK.cmake
  • これを行ってから再度ビルドすると成功した
  • OpenCVの一部の機能を利用してAndroid端末で動作確認したところ、その機能は正常に動作していた(imread,cvtColor,flip)

20.3 この章のまとめ

  • opencvをbuildozer.specに設定しただけではインストールできなくなっていた不具合をさけて、ビルドすることに成功したので、その手順を紹介した。
  • 今後のbuildozer あるいは python-for-android のバージョンアップにて、この問題は恐らく解消?(個人的な予想)

21 Dlibのクロスコンパイル方法(邪道2021/02/14)

  • クロスコンパイルは、Ubuntu20.04で行った。
  • python-for-androidにDlib用のレシピがなかった。
  • クロスコンパイルもなかなか成功しなかったが、2021年にはいって、ライブラリのクロスコンパイルに成功、しかし、Python用のモジュールに必要な、動的ライブラリの生成に失敗していた。
  • Python用のAndroid用のモジュール、termux環境でpipコマンドにてインストールに成功した、そのファイルを、buildozerのプロジェクトディレクトリにコピーしたが、必要なライブラリが不足していて、termux環境からもってくる必要がありそうだった。Pythonの細かいコンパイル条件も同じではなさそうだったので、この方法は断念。
  • Python用のモジュールに必要な、動的ライブラリのクロスコンパイル、2021/2/13についに成功
  • 途中でエラーがでたときに、操作したりする邪道な手順となっているけど、コンパイルして、buildozerでAndroidのパッケージ化し、インストール動作させて、顔画像のパーツ認識まで成功している

21.2 以下の説明を行っている動画




21.3 手順

21.3.1 前準備

  • コマンドラインにて、AndroidのSDKやNDKをインストール。 https://youtu.be/3U5_330aySk
    • インストールするパッケージは以下で行った。
    • buildozerの現状のバージョンだと、NDKのバージョンは19c以下しか可能ではないので、19の一番新しいバージョンを入れてある
Path                 | Version      | Description                             | Location             
-------              | -------      | -------                                 | -------              
build-tools;24.0.3   | 24.0.3       | Android SDK Build-Tools 24.0.3          | build-tools/24.0.3/  
build-tools;30.0.3   | 30.0.3       | Android SDK Build-Tools 30.0.3          | build-tools/30.0.3/  
cmake;3.10.2.4988404 | 3.10.2       | CMake 3.10.2.4988404                    | cmake/3.10.2.4988404/
cmake;3.6.4111459    | 3.6.4111459  | CMake 3.6.4111459                       | cmake/3.6.4111459/   
cmdline-tools;latest | 3.0          | Android SDK Command-line Tools (latest) | cmdline-tools/latest/
emulator             | 30.3.5       | Android Emulator                        | emulator/            
ndk-bundle           | 22.0.7026061 | NDK                                     | ndk-bundle/          
ndk;19.2.5345600     | 19.2.5345600 | NDK (Side by side) 19.2.5345600         | ndk/19.2.5345600/    
patcher;v4           | 1            | SDK Patch Applier v4                    | patcher/v4/          
platform-tools       | 30.0.5       | Android SDK Platform-Tools              | platform-tools/      
platforms;android-27 | 3            | Android SDK Platform 27                 | platforms/android-27/
platforms;android-29 | 5            | Android SDK Platform 29                 | platforms/android-29/
platforms;android-30 | 3            | Android SDK Platform 30                 | platforms/android-30/
  • パッケージ環境を、作業ディレクトリを作成し、buildozer initで作成したあと、以下の変更を行う
    • arm64-v8a に buildozer.specのターゲット変更
    • 必要なパッケージをインストールするように修正
      • imutilsがあるとDlibで顔認識するとき便利なので、これも追加しておくことをオススメします。後から追加でも良いけど。
      • DlibのGUIでの表示コマンドはAndroid+Kivy+buildozerでは動作しなかったので、OpenCVあるいは、Pillow も入れておくことをオススメします。後から追加でも良いけど。OpenCVを入れる場合は現状はエラーになるので、https://www.youtube.com/embed/V0Z5cgzEsRg の手順でやれば回避可能(将来のバージョンでは、こんなややこしい手順不要になると思われる)
      • 追加情報
        • OpenCVの場合、画像表示は、Kivyのcanvasに、上下反転、カラーマップを変更して流し込めばいけた。カラーマップはrgbaにしとかないと、エラーになった。バージョンによって挙動ちがうかも。(kivy 2.0.0, Python3.8.5, OpenCV 4.5.1で試した。OpenCVのバージョンを4.5.1にするには、python-for-androidのOpenCV用のレシピを変更する必要あり)
    • android.ndk_path, android.sdk_path, android.ant_path を、この作業ディレクトリからの相対パスにて設定。
      • シンボリックリンクをはって、そこに設定した方が楽かも。私はこれは行ってないけど
    • その他必要な変更を行う
mkdir ${HOME}/mydlib
cd ${HOME}/mydlib
# source ~/kivy_buildozer/bin/activate # buildozer 用のvirturlenvが ~/kivy_buildozer にある場合
buildozer init
touch main.py # からのmain.pyを作成。何か動くものにした方が良いけど、ここでは空ファイル用意します。
# その後buildozer.specをテキストエディタで変更
  • main.pyを用意
    • dlibのバージョンも表示するようにしてあるので、dlibを入れる前だとエラーになる
    • dlibのPythonを上手くインストールできれば、Kivy, Python, Dlib, OpenCV, imutils のバージョンが表示される
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
import kivy
import sys 
import cv2
import dlib
import imutils

class HelloWorldApp(App):
 def build(self):
  b = BoxLayout(orientation="vertical")
  l2 = Label(text="Kivy:"+kivy.__version__,font_size=50)
  b.add_widget(l2)
  l4 = Label(text="Py:"+sys.version,font_size=20)
  b.add_widget(l4)
  l5 = Label(text="dlib:"+dlib.__version__,font_size=20)
  b.add_widget(l5)
  l6 = Label(text="opencv:"+cv2.__version__,font_size=20)
  b.add_widget(l6)
  l11 = Label(text="imutils:"+imutils.__version__,font_size=20)
  b.add_widget(l11)
  return b

if __name__ == '__main__':
 HelloWorldApp().run()
  • buildozer.specの変更箇所例
    • “… 省略” はbuildozer.specにコピペしない。変更しない箇所という意味
    • ndk,sdk,antのパスは実際用意してある、AndroidのNDKやSDKをあらかじめインストールしてあるパスに設定、この例では、ホームディレクトリのAndroid/SDK以下にインストールしている https://youtu.be/3U5_330aySk
    • android.arch は 現状Googleストアでうけつけてくれる64bitCPU用のものに変更(buildozer initした直後は32bit用のarmeabi-v7aになっている)
    • tiltleとパッケージネームは適当に変更してある
... 省略
title = MyDlibTest001
... 省略
package.name = mydlibtest001
... 省略
source.include_exts = py,png,jpg,kv,atlas,so
... 省略
requirements = python3,kivy==2.0.0,imutils,opencv
... 省略
android.ndk_path = ../Android/SDK/ndk/19.2.5345600
... 省略
android.sdk_path = ../Android/SDK
... 省略
android.ant_path = ../Android/SDK/cmdline-tools/latest/proguard/apache-ant-1.9.4/bin/ant
... 省略
android.arch = arm64-v8a
... 省略
  • Dlibの無い状態で、一旦Android用のバイナリを生成。
    • これによって、作業ディレクトリの .buildozer/ ディレクトリに、Dlibのクロスコンパイルに必要なファイルが生成される
    • OpenCVをrequirementsに追加してるなら、エラー発生現状はするので、前の章の手順で回避https://www.youtube.com/embed/V0Z5cgzEsRg。この手順は将来のバージョンでは不要になると思われる
buildozer android debug deploy run
  • 下が実行例 tee コマンド等でログをファイルに出しておくと良い
buildozer android debug deploy run 2>&1 | tee log001.txt

21.3.2 Dlibのソースゲット

  • 上のパッケージ作成とは別作業ディレクトリに移動。(例えば${HOME}/mydlib_dlibとか)
  • 最新安定バージョンは、コンパイル時にワーニング出まくります。なので今回はGithubの最新ソースをコンパイルします。
  • 最新安定バージョンでも同じ手順で多分クロスコンパイル出来るはず
# mkdir ${HOME}/mydlib_dlib  # 作業用のディレクトリ作成
# cd ${HOME}/mydlib_dlib     # 作業用のディレクトリに移動
git clone https://github.com/davisking/dlib

21.3.3 Dlibソースのバックアップ

  • dateコマンドを組み合わせた方だと、実行時の日付や日時入りのファイル名でバックアップ取れます。
  • 1行目のコマンドなら、dlib.org.tgz でバックアップ取れる
tar cvzf dlib.org.tgz dlib
# or
tar cvzf dlib.`date +%Y%m%d_%H%M`.tgz dlib

21.3.4 Dlibライブラリ(Python用は次の手順)のクロスコンパイル

  • Python用のクロスコンパイルするなら、ここの手順は不要
  • 作業用のディレクトリを作成し、そこに移動
    • これはコマンドラインで行わなくてもOK(使い慣れたツールでやってください)
cd dlib
mkdir build
cd build
  • 作業用のディレクトリを作成し、そこに移動し、以下を実行
    • Android用の開発環境はホームディレクトリの、Android/SDK ディレクトリ以下にインストール済み https://youtu.be/3U5_330aySk
    • ndkは19系の作業時点で一番新しいバージョンを利用した。
    • ターゲットは’arm64-v8a’にしている
    • 動的ライブラリを生成しようとしている -DANDROID_STL=c++_shared を無くせば、静的ライブラリを生成
    • ANDROID_PLATFORM=”android-16″の場合、他のバージョンにするなら、そこを変更
${HOME}/Android/SDK/cmake/3.10.2.4988404/bin/cmake -DBUILD_SHARED_LIBS=1 -DANDROID_NDK="${HOME}/Android/SDK/ndk/19.2.5345600" -DCMAKE_TOOLCHAIN_FILE="${HOME}/Android/SDK/ndk/19.2.5345600/build/cmake/android.toolchain.cmake" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-std=c++11 -frtti -fexceptions" -DCMAKE_C_FLAGS=-O3 -DANDROID_ABI='arm64-v8a' -DANDROID_PLATFORM="android-16" -DANDROID_TOOLCHAIN=clang -DANDROID_STL=c++_shared -DANDROID_CPP_FEATURES=rtti exceptions .. 2>&1 | tee log001.txt

21.3.5 DlibライブラリPython用のクロスコンパイル

  • 作業用のディレクトリを作成し、そこに移動し、以下を実行
cd dlib
mkdir tools/python/build
cd tools/python/build
  • 設定のファイルの位置確認1
    • もし無いなら、find ${HOME}/mydlib/.buildozer/ -iname python -type f で探し、cmakeするときのオプションの該当箇所を変更
ls ${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/hostpython3/desktop/hostpython3/native-build/python
  • 設定のファイルの位置確認2
    • もし無いなら、find ${HOME}/mydlib/.buildozer/ -iname Python.h -type f で探し、そのディレクトリを調べておく、cmakeするときのオプションの該当箇所を変更
ls ${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/Include 
  • 設定のファイルの位置確認3
    • もし無いなら、find ${HOME}/mydlib/.buildozer/ -iname libpython3.8.so -type f で探し、そのディレクトリを調べておく、cmakeするときのオプションの該当箇所を変更
ls ${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build/libpython3.8.so 
  • 以下のコマンドを実行,ただし、環境にあわせて、コマンド変更する必要あります。
    • 前準備で用意した、Androidパッケージの開発ディレクトリが、下の例だと ${HOME}/mydlib/ になっています。このディレクトリじゃない場合は、前準備で行ったディレクトリに変更してください。
    • Android用の開発環境はホームディレクトリの、Android/SDK ディレクトリ以下にインストール済み https://youtu.be/3U5_330aySk 。他のバージョンを利用する場合は、そこを変更してください。
  • その他の設定について
    • ndkは19系の作業時点で一番新しいバージョンを利用した。
    • ターゲットは’arm64-v8a’にしている
    • android.ndk_apiが21の場合。21以外なら、以下のコマンドの21をその番号に変更
    • lsコマンドで事前に調べたオプションが環境にあってないなら、該当箇所を変更
    • 2行目のコメントアウトしているコマンドは、2021/2/15現在で、python-for-android develp(githubで最新)を使う時のコマンド、Python関係のファイルの位置がちょっと違う
${HOME}/Android/SDK/cmake/3.10.2.4988404/bin/cmake -DANDROID_NDK="${HOME}/Android/SDK/ndk/19.2.5345600" -DCMAKE_TOOLCHAIN_FILE="${HOME}/Android/SDK/ndk/19.2.5345600/build/cmake/android.toolchain.cmake" -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI='arm64-v8a' -DANDROID_PLATFORM="android-21" -DANDROID_TOOLCHAIN=clang -DANDROID_CPP_FEATURES=rtti exceptions -DPYTHON_EXECUTABLE=${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/hostpython3/desktop/hostpython3/native-build/python -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=build/lib.arm64-v8a -DPYTHON_INCLUDE_DIRS=${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/Include -DPYTHON_LIBRARY=${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build/libpython3.8.so .. 2>&1 | tee log001.txt
# ${HOME}/Android/SDK/cmake/3.10.2.4988404/bin/cmake -DANDROID_NDK="${HOME}/Android/SDK/ndk/19.2.5345600" -DCMAKE_TOOLCHAIN_FILE="${HOME}/Android/SDK/ndk/19.2.5345600/build/cmake/android.toolchain.cmake" -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI='arm64-v8a' -DANDROID_PLATFORM="android-21" -DANDROID_TOOLCHAIN=clang -DANDROID_CPP_FEATURES=rtti exceptions -DPYTHON_EXECUTABLE=${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/hostpython3/desktop/hostpython3/native-build/python -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=build/lib.arm64-v8a -DPYTHON_INCLUDE_DIRS=${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3-libbz2-liblzma/arm64-v8a__ndk_target_21/python3/Include -DPYTHON_LIBRARY=${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3-libbz2-liblzma/arm64-v8a__ndk_target_21/python3/android-build/libpython3.8.so .. 2>&1 | tee log001.txt # python-for-android develの場合こっちだった
  • 上のコマンドでエラーがなければ
make 2>&1 | tee log002.txt
  • これを実行すると、コンパイルには成功しますが、リンクの時にエラーが出ます。以下のようなエラーならOK。100%までコンパイルできずにエラーになってたら、上のcmakeのオプションがまずいので、修正
... 省略
[100%] Linking CXX shared module build/lib.arm64-v8a/_dlib_pybind11.cpython-38-x86_64-linux-gnu.so
... 膨大なエラーメッセージ
  • リンクで失敗しているので、そこの命令をマニュアルで実行します。
  • CMakeFiles/_dlib_pybind11.dir/link.txt にリンク時のコマンドが生成されてるので、-L${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build -lpython3.8 を追加します。私は-latomic の前に挿入しました。
  • ユーザー名がyoutubeの時こんな感じ
/home/youtube/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android21 --gcc-toolchain=/home/youtube/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/youtube/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-addrsig -Wa,--noexecstack -Wformat -Werror=format-security -stdlib=libc++ -frtti  -O2 -DNDEBUG  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now  -shared  -o build/lib.arm64-v8a/_dlib_pybind11.cpython-38-x86_64-linux-gnu.so CMakeFiles/_dlib_pybind11.dir/src/dlib.cpp.o CMakeFiles/_dlib_pybind11.dir/src/matrix.cpp.o CMakeFiles/_dlib_pybind11.dir/src/vector.cpp.o CMakeFiles/_dlib_pybind11.dir/src/svm_c_trainer.cpp.o CMakeFiles/_dlib_pybind11.dir/src/svm_rank_trainer.cpp.o CMakeFiles/_dlib_pybind11.dir/src/decision_functions.cpp.o CMakeFiles/_dlib_pybind11.dir/src/other.cpp.o CMakeFiles/_dlib_pybind11.dir/src/basic.cpp.o CMakeFiles/_dlib_pybind11.dir/src/cca.cpp.o CMakeFiles/_dlib_pybind11.dir/src/sequence_segmenter.cpp.o CMakeFiles/_dlib_pybind11.dir/src/svm_struct.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image2.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image3.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image4.cpp.o CMakeFiles/_dlib_pybind11.dir/src/rectangles.cpp.o CMakeFiles/_dlib_pybind11.dir/src/object_detection.cpp.o CMakeFiles/_dlib_pybind11.dir/src/shape_predictor.cpp.o CMakeFiles/_dlib_pybind11.dir/src/correlation_tracker.cpp.o CMakeFiles/_dlib_pybind11.dir/src/face_recognition.cpp.o CMakeFiles/_dlib_pybind11.dir/src/cnn_face_detector.cpp.o CMakeFiles/_dlib_pybind11.dir/src/global_optimization.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image_dataset_metadata.cpp.o CMakeFiles/_dlib_pybind11.dir/src/numpy_returns.cpp.o CMakeFiles/_dlib_pybind11.dir/src/line.cpp.o dlib_build/libdlib.a -latomic -lm
  • -L${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build -lpython3.8 を追加例が以下
/home/youtube/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android21 --gcc-toolchain=/home/youtube/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/youtube/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-addrsig -Wa,--noexecstack -Wformat -Werror=format-security -stdlib=libc++ -frtti  -O2 -DNDEBUG  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now  -shared  -o build/lib.arm64-v8a/_dlib_pybind11.cpython-38-x86_64-linux-gnu.so CMakeFiles/_dlib_pybind11.dir/src/dlib.cpp.o CMakeFiles/_dlib_pybind11.dir/src/matrix.cpp.o CMakeFiles/_dlib_pybind11.dir/src/vector.cpp.o CMakeFiles/_dlib_pybind11.dir/src/svm_c_trainer.cpp.o CMakeFiles/_dlib_pybind11.dir/src/svm_rank_trainer.cpp.o CMakeFiles/_dlib_pybind11.dir/src/decision_functions.cpp.o CMakeFiles/_dlib_pybind11.dir/src/other.cpp.o CMakeFiles/_dlib_pybind11.dir/src/basic.cpp.o CMakeFiles/_dlib_pybind11.dir/src/cca.cpp.o CMakeFiles/_dlib_pybind11.dir/src/sequence_segmenter.cpp.o CMakeFiles/_dlib_pybind11.dir/src/svm_struct.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image2.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image3.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image4.cpp.o CMakeFiles/_dlib_pybind11.dir/src/rectangles.cpp.o CMakeFiles/_dlib_pybind11.dir/src/object_detection.cpp.o CMakeFiles/_dlib_pybind11.dir/src/shape_predictor.cpp.o CMakeFiles/_dlib_pybind11.dir/src/correlation_tracker.cpp.o CMakeFiles/_dlib_pybind11.dir/src/face_recognition.cpp.o CMakeFiles/_dlib_pybind11.dir/src/cnn_face_detector.cpp.o CMakeFiles/_dlib_pybind11.dir/src/global_optimization.cpp.o CMakeFiles/_dlib_pybind11.dir/src/image_dataset_metadata.cpp.o CMakeFiles/_dlib_pybind11.dir/src/numpy_returns.cpp.o CMakeFiles/_dlib_pybind11.dir/src/line.cpp.o dlib_build/libdlib.a -L${HOME}/mydlib/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/android-build -lpython3.8 -latomic -lm
  • このコマンドを実行すると、リンクして build/lib.arm64-v8a/_dlib_pybind11.cpython-38-x86_64-linux-gnu.so が生成されます。ファイル名はlinux用になっていますが、中身は arm64-v8a用(Android64bit用)のバイナリになっています。
  • build/lib.arm64-v8a/以下のファイルが、必要ファイルになります。
  • ファイルサイズが240M程度で大きいので、以下を行うと11M程度までファイルが小さくなります。
${HOME}/Android/SDK/ndk/19.2.5345600/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/strip ./build/lib.arm64-v8a/_dlib_pybind11.cpython-38-x86_64-linux-gnu.so
  • あとは./build/lib.arm64-v8a/dlib/ディレクトリを、Dlibを使いたいbuildozerプロジェクトディレクトリにコピーします。
    • コピー先のディレクトリ名は、各自変更してください。下の例だと ${HOME}/mydlib/ にインストールする場合
cp -r ./build/lib.arm64-v8a/dlib/ ${HOME}/mydlib/
  • 必要な動的ライブラリをコピーします
    • コピーする時にファイル名を変更しました。元のままでも動作するかどうかは未確認。ファイル名をもしかしたら、変更しなくても大丈夫かもしれない
    • コピー先のディレクトリ名は、各自変更してください。下の例だと ${HOME}/mydlib/ にインストールする場合
cp ./build/lib.arm64-v8a/_dlib_pybind11.cpython-38-x86_64-linux-gnu.so ${HOME}/mydlib/_dlib_pybind11.cpython-38.so
  • この例だと ${HOME}/mydlib/ の Dlibを使いたいbuildozerプロジェクトを、再度コンパイルしなおせば、そのパッケージでDlibをPythonから使えます。

21.3.6 DlibライブラリPythonを含めたAndroidパッケージ作成とインストール

  • Android端末にUSB等でPCを接続し、以下のコマンドを実行して、Android端末にインストール、実行する
cd ${HOME}/mydlib/
buildozer android debug deploy run 2>&1 | tee log010.txt

21.4 その他

21.5 この章のまとめ

  • python-for-androidにレシピが用意されていなくて、Androidで使いにくかった、Dlibをbuildozerで利用するのに成功した邪道な作業手順を説明した。
  • もっとスマートな方法もあるとは思う
  • 今後もっと楽になる可能性大(buildozerや、python-for-androidのバージョンあがったら)。Dlibは人気のライブラリだし、レシピ用意されると思う。

22 今後

  • 今後も文書追加していきます。

23 この文書のチェンジログ

  • 2020/07/31 初版
  • 2020/08/13 Kivy+buildozer関連リンク の章追加
  • 2020/08/18 Kivy Launcherの使い方の章追加, plyer入りのKivy Launcherの作成とインストール(好きなパッケージ入りのKivy Launcherの作り方)の章追加
  • 2020/08/19 plyer関係の機能の利用方法の調べ方の章追加、広告の章、 課金の章追加
  • 2020/08/19 sl4a関係はkivyとは別物であったことと、kivy launcherカスタマイズしたが、GooglePlayにあるものと同名のものはソースが異ることを訂正,Kivy Launcherの使い方へのリンク追加
  • 2020/08/20 広告の章にKivModの詳細情報と、実際にサンプルソースを動かす手順を記述, 課金に例のリンクを追加
  • 2020/08/23 デバッグ手法いくつか紹介 の章追加, * pyjniusを使って、java経由でAndroid特有の機能の利用 の章追加, * Kivy Launcherの拡張その2 の章追加, 課金の情報にandroidモジュールの情報,公式Android開発環境の情報も追加
  • 2020/08/29 Kivy Launcherの拡張その3 の章追加
  • 2020/08/31 WebView(ウェブブラウザの部品への埋め込みandroid) の章追加
  • 2020/10/01 Kivy Launcherの拡張その4 の章追加
  • 2020/10/01 Kivyでmatplotlibでグラフ の章追加
  • 2020/10/23 Kivy+Buildozerで作成したAndroidアプリをGoogle Play Storeに出品する手順 の章追加
  • 2021/02/02 Googleのツールのbuildozerによる自動ダウンロードが出来なくなっています の章追加
  • 2021/02/05 opencvのコンパイル方法(邪道2021/02/05) の章追加
  • 2021/02/15 Dlibのクロスコンパイル方法(邪道2021/02/14) の章追加

著者: NM Max

Created: 2021-02-15 月 20:23

Validate

django入門

django入門

目次

1 概要

1.1 djangoの概要

  • djangoは2019/11でPythonで最もユーザー数が多いとされているWebアプリケーションフレームワーク
  • 最新のdjangoはpython3系をサポートらしい

1.2 この文書の概要

  • django関係の入門者用文書
  • Ubuntu19系をを利用して最新のdjangoを使って確認してますが、他のOSでもほぼ同じ手順で作成可能
  • 実際の作業部分を真似しやすいように作成
  • 学習手順として、実際やってみてから、修正したり調べた方が効率良いケースが多い為
  • ベースはdjangoの本家の文書なので、この文書のライセンスもdjangoの本家と同じとします。

2 リンク

2.1 本家

Django ドキュメント (version 2.2)
https://docs.djangoproject.com/ja/2.2/
入門文書(version 2.2)
https://docs.djangoproject.com/ja/2.2/intro/
本家ドキュメント目次
https://docs.djangoproject.com/ja/2.2/contents/
本家ドキュメント索引
https://docs.djangoproject.com/ja/2.2/genindex/
モジュール索引
https://docs.djangoproject.com/ja/2.2/py-modindex/

3 インストール

  • 2019/10月頃、Ubuntuのパッケージのdjangoは1系統であるため、Ubuntuのパッケージによるインストールはお勧めしません。
  • WindowsやMacの場合、Ubuntuの仮想環境や模擬環境であれば、同じ操作で可能だと思います。

3.1 以下の操作を行っている動画


3.2 pipによるローカルへのインストール手順

3.2.1 必要パッケージのインストール

sudo apt install pip3

3.2.2 ローカルディレクトリへのインストール

  • ホームディレクトリの python3/dist-packages ディレクトリにインストールする場合
  • 下のコマンドを実行しパッケージ関係のファイルをインストール
pip3 install Django -t ~/python3/dist-packages
  • インストールしたファイルを検索パスで検索できるように以下の設定を ~/.bashrcに追加し、再起動するか、端末を開きなおし、設定を読み込んだ状態にする
export PYTHONPATH="$HOME/python3/dist-packages:$PYTHONPATH"
export PATH="$HOME/python3/dist-packages/bin:$PATH"
alias python=python3

4 ローカルディレクトリへのインストールしたものを最新のバージョンにアップグレード

4.1 以下の操作を行っている動画


4.2 アップグレードするコマンド

pip3 install --upgrade Django -t ~/python3/dist-packages

5 最初のdjangoアプリ

5.1 以下の操作を行っている動画


5.2 現状のdjangoのバージョン確認

python -m django --version
  • 2019/11/10現在で 2.2.7 の出力が得られています

5.3 python自体のバージョン確認

python --version
  • 2019/11/10現在で Python 3.7.5rc1 の出力が得られています

5.4 プロジェクトの作成

  • 以下のコマンドで mysite という名前のプロジェクトを作成できます。
  • mysiteというディレクトリが作成され、その中にディレクトリや各種ファイルが生成されます
  • プロジェクト名は mysite である必用はなく別の名前でもOK
django-admin startproject mysite

5.5 生成した空プロジェクトを動かしてブラウザで確認する方法

  • 以下のコマンドでプロジェクトのルートにカレントディレクトリを移動
cd mysite
  • 以下のコマンドでサーバーを起動
  • デフォルトで http://127.0.0.1:8000/ で動きます
  • ポート番号とかはオプションで変更可能
  • http://127.0.0.1:8000/ をブラウザで開くとデフォルト画面が表示されます。
python manage.py runserver

6 djangoアプリ2(Hello World)

  • https://docs.djangoproject.com/ja/2.2/intro/ がお勧め
  • 上の本家の文書の手順をベースとして簡略化し、多少アレンジした内容になってます。
  • 指定のurlにアクセスすると、指定の文字が表示されるものを作ります

6.1 以下の操作を行っている動画


6.2 指定のurlで指定の文字が表示されるウェッブアプリを作成する手順

6.2.1 mytest001というアプリを作成し、その中にmypageというアプリケーションを作成

django-admin startproject mytest001
cd mytest001
python manage.py startapp mypage

6.2.2 mypage/views.py を以下に変更します。

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world!")

6.2.3 mypage/urls.py には以下を記述します

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

6.2.4 mytest001/urls.py に 以下の変更を行う

from django.contrib import admin
from django.urls import include, path

from django.views.generic.base import RedirectView

urlpatterns = [
    path('mypage/', include('mypage.urls')),
    path('', RedirectView.as_view(url='/mypage/')),
    path('admin/', admin.site.urls),
]

6.2.5 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

7 djangoアプリ3(テンプレートの利用)

7.1 以下の操作を行っている動画


7.2 テンプレートファイルを利用して表示させるための手順

7.2.1 テンプレートファイルを入れるディレクトリの準備

  • プロジェクトのルート(manage.py があるディレクトリにあるmypageディレクトリ)に templates/mypage ディレクトリを作成します。
  • 以下のコマンド実行前に プロジェクトのルート(manage.py があるディレクトリ)に移動してから実行
mkdir -p mypage/templates/mypage

7.2.2 プロジェクトのセッティング修正

  • INSTALLED_APPS 以外の部分は変更しないでください。
  • mytest001/settings.py の INSTALLED_APPS に ‘mypage.apps.MypageConfig’, を追加
  • これを行うことで、テンプレートファイルの検索パスにmypageが含まれるようになります。
... 省略
# Application definition

INSTALLED_APPS = [
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
... 省略

7.2.3 テンプレートファイル用意

  • 今回はemacsのorgモードを利用して自動生成したhtmlをベースに改造して以下を作成しました。
  • 以下の内容をmypage/templates/mypage/index.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>djangoの始め方</title>
</head>
<body>
<h1 class="title">djangoの始め方</h1>
</body>
</html>

7.2.4 表示部分の改造

  • mypage/views.py を以下の内容に変更します
from django.http import HttpResponse
from django.template import loader

def index(request):
    template = loader.get_template('mypage/index.html')
    context = {}
    return HttpResponse(template.render(context, request))

7.2.5 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

7.3 この章まとめ

  • テンプレートファイルを利用して表示できる手順を説明

8 djangoアプリ4(計算アプリ)

8.1 以下の操作を行っている動画


8.2 djangoアプリ4(計算アプリ)作成手順

8.2.1 プロジェクトとmycalcアプリのベース作成

  • 今回はmytest002という名前で新規に作っていきます。
django-admin startproject mytest002
cd mytest002
python manage.py startapp mycalc

8.2.2 プロジェクトのセッティング修正

  • INSTALLED_APPS 以外の部分は変更しないでください。
  • mytest002/settings.py の INSTALLED_APPS に ‘mycalc.apps.MypageConfig’, を追加
  • これを行うことで、テンプレートファイルの検索パスにmycalcが含まれるようになります。
... 省略
# Application definition

INSTALLED_APPS = [
    'mycalc.apps.MycalcConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
... 省略

8.2.3 テンプレートファイルを入れるディレクトリの準備

mkdir -p mycalc/templates/mycalc

8.2.4 テンプレートファイル用意

  • 以下の内容をmycalc/templates/mycalc/index.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ</title>
</head>
<body>
<form action="" method="POST">
  <table>
    {{ form.as_table }}
  </table>
  {% csrf_token %}
  <button type="submit">計算</button>
</form>
{% if ans != "" %}
  {{ ans }} </br>
{% endif %}
</body>
</html>

8.2.5 mycalc/form.py を作成

from django import forms

class numForm(forms.Form):
  num1 = forms.DecimalField(label='num1', initial=2, required=True)
  num2 = forms.DecimalField(label='num2', initial=1, required=True)

8.2.6 mycalc/views.py を以下に変更します。

from django.shortcuts import render
from .form import numForm

def index(request):
  if request.method == 'POST':
    form = numForm(request.POST)
    if form.is_valid():
      # print(request.POST)
      # print(form.cleaned_data['num1'])
      # print(form.cleaned_data['num2'])
      # return HttpResponseRedirect('/answer/')
      # print([form.cleaned_data['num1'],form.cleaned_data['num2']])
      n1=int(form.cleaned_data['num1'])
      n2=int(form.cleaned_data['num2'])
      ans=str(n1)+" + "+str(n2)+" = "+str(n1+n2)
      # print(ans)
  else:
    form=numForm()
    ans=""
  return render(request,'mycalc/index.html',{'form':form, 'ans' : ans})

8.2.7 mycalc/urls.py には以下を記述します

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

8.2.8 mytest002/urls.py に 以下の変更を行う

from django.contrib import admin
from django.urls import include, path

from django.views.generic.base import RedirectView

urlpatterns = [
    path('mycalc/', include('mycalc.urls')),
    path('', RedirectView.as_view(url='/mycalc/')),
    path('admin/', admin.site.urls),
]

8.2.9 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

8.3 この章まとめ

  • 整数を2つ入力して、足し算結果を返すアプリの作成(JavaScriptで作った方が楽っぽいけど)
  • formの使い方の基礎を確認

9 djangoアプリ5(ファイルアップロード)

9.1 以下の操作を行っている動画



9.2 djangoアプリ5(ファイルアップロード)作成手順

9.2.1 プロジェクトとmyfile_uploadアプリのベース作成

  • 今回はmytest003という名前で新規に作っていきます。
django-admin startproject mytest003
cd mytest003
python manage.py startapp myfile_upload

9.2.2 プロジェクトのセッティング修正

  • INSTALLED_APPS 以外の部分は変更しないでください。
  • mytest003/settings.py の INSTALLED_APPS に ‘myfileUpload.apps.MypageConfig’, を追加
  • これを行うことで、テンプレートファイルの検索パスにmyfile_uploadが含まれるようになります。
... 省略
# Application definition

INSTALLED_APPS = [
    'myfile_upload.apps.MyfileUploadConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
... 省略

9.2.3 myfile_upload/urls.py には以下を記述します

from django.urls import path

from . import views

urlpatterns = [
    path('', views.upload_file, name='index'),
]

9.2.4 mytest003/urls.py に 以下の変更を行う

from django.contrib import admin
from django.urls import include, path

from django.views.generic.base import RedirectView

import myfile_upload.views as myfile_upload

urlpatterns = [
    path('success/url/',myfile_upload.success),
    path('myfile_upload/', include('myfile_upload.urls')),
    path('', RedirectView.as_view(url='/myfile_upload/')),
    path('admin/', admin.site.urls),
]

9.2.5 テンプレートファイルを入れるディレクトリの準備

mkdir -p myfile_upload/templates/myfile_upload

9.2.6 テンプレートファイル1用意

  • 以下の内容をmyfile_upload/templates/myfile_upload/index.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>ファイルアップロードアプリ</title>
</head>
<body>
<form action="" method="POST" enctype="multipart/form-data">
  <table>
    {{ form.as_table }}
  </table>
  {% csrf_token %}
  <button type="submit">ファイルアップロード実行</button>
</form>
</body>
</html>

9.2.7 テンプレートファイル2(成功時用)用意

  • 以下の内容をmyfile_upload/templates/myfile_upload/success.html として保存します。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>ファイルアップロードアプリ</title>
</head>
<body>
<h1>ファイルのアップロードに成功しました!</h1>
</body>
</html>

9.2.8 myfile_upload/forms.py を作成

from django import forms

class UploadFileForm(forms.Form):
    # title = forms.CharField(max_length=50)
    file = forms.FileField()

9.2.9 myfile_upload/views.py を以下に変更します。

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm

# Imaginary function to handle an uploaded file.
# from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render(request, 'myfile_upload/index.html', {'form': form})

def handle_uploaded_file(f):
    fname='files/'+f.name
    print("fname:"+fname)
    with open(fname, 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

def success(request):
    return render(request, 'myfile_upload/success.html', {})

9.2.10 ファイル保管用のディレクトリ作成

mkdir -p files

9.2.11 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

9.3 この章まとめ

  • ファイルのアップロードを行う方法を確認

10 urlでのパラメータ渡し

10.1 以下の操作を行っている動画


10.2 djangoアプリ4(計算アプリ)の改造手順

10.2.1 mycalc/urls.py を以下に修正

  • <int: だとマイナスの数値にマッチしてくれないため、re_pathで正規表現マッチを利用 (?P<name>pattern)
from django.urls import path, re_path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    # ex: /mycalc/5/6/results/
    # path('<int:num1>/<int:num2>/results/', views.results, name='results'),
    # ex: /mycalc/5/-1/4/results/
    re_path(r'^(?P<num1>-?\d+)/(?P<num2>-?\d+)/(?P<num3>-?\d+)/results/$', views.results, name='results'),
]

10.2.2 mycalc/views.py を以下に変更します。

from django.shortcuts import render, redirect
from .form import numForm
from django.views.generic.base import RedirectView

def index(request):
  if request.method == 'POST':
    form = numForm(request.POST)
    if form.is_valid():
      n1=int(form.cleaned_data['num1'])
      n2=int(form.cleaned_data['num2'])
      return redirect(str(n1)+'/'+str(n2)+'/'+str(n1+n2)+'/results/')
  else:
    form=numForm()
  return render(request,'mycalc/index.html',{'form':form})

def results(request, num1, num2, num3):
  return render(request,'mycalc/results.html',{'num1':num1, 'num2':num2, 'num3':num3})

10.2.3 元のテンプレートファイルを修正

  • mycalc/templates/mycalc/index.html を以下に修正
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ</title>
</head>
<body>
<form action="" method="POST">
  <table>
    {{ form.as_table }}
  </table>
  {% csrf_token %}
  <button type="submit">計算</button>
</form>
</body>
</html>

10.2.4 結果表示用のテンプレートファイル作成

  • mycalc/templates/mycalc/results.html を以下の内容で作る
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ 計算結果</title>
</head>
<body>
    {{ num1 }} + {{ num2 }} = {{ num3 }} </br>
    {{ num1 }} + {{ num2 }} = {{ num1|add:num2 }} </br>
<a href="/mycalc/">計算設定画面へ</a>
</form>
</body>
</html>

10.2.5 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

10.3 この章まとめ

  • urlでのパラメーター渡し
  • 正規表現を利用したマッチも利用
  • 組み込みタグとフィルタの活用

11 カスタムフィルタ

11.1 以下の操作を行っている動画


11.2 カスタムフィルタの作成手順

11.2.1 カスタムフィルタ保存用のフォルダ作成

mkdir -p mycalc/templatetags

11.2.2 カスタムフィルタ用ファイルの作成

  • touchコマンドで空の__init__.pyを作成してます。エディタで空の__init__.pyを作成してもOK
touch mycalc/templatetags/__init__.py
  • mycalc/templatetags/mysub.py を以下の内容で作成
from django import template

register = template.Library()

@register.filter("mysub")
def mysub(value, arg):
    return int(value)-int(arg)

@register.filter("mymul")
def mymul(value, arg):
    return int(value)*int(arg)

@register.filter("mydiv")
def mydiv(value, arg):
    return int(value)/int(arg)

@register.filter("mymod")
def mymod(value, arg):
    return int(value)%int(arg)

11.2.3 結果表示用のテンプレートファイル修正

  • mycalc/templates/mycalc/results.html を以下の内容で作る
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>計算アプリ 計算結果</title>
</head>
<body>
    {{ num1 }} + {{ num2 }} = {{ num3 }} </br>
    {{ num1 }} + {{ num2 }} = {{ num1|add:num2 }} </br>
    {% load mysub %}
    {{ num1 }} - {{ num2 }} = {{ num1|mysub:num2 }} </br>
    {{ num1 }} * {{ num2 }} = {{ num1|mymul:num2 }} </br>
    {{ num1 }} / {{ num2 }} = {{ num1|mydiv:num2 }} </br>
    {{ num1 }} % {{ num2 }} = {{ num1|mymod:num2 }} </br>
<a href="/mycalc/">計算設定画面へ</a>
</form>
</body>
</html>

11.2.4 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

11.3 この章まとめ

  • カスタムフィルタを作成し利用してみた

12 セッション(session)関係の機能を試してみる

12.1 以下の操作を行っている動画


12.2 djangoアプリ5(ファイルアップロード mytest003)の改造手順

12.2.1 マイグレートを行う

  • セッション機能を利用するのに必用(デフォルトのセッションの設定の場合、データベースを利用するため)
python manage.py migrate

12.2.2 テンプレートファイル2(成功時用)を改造

  • myfile_upload/templates/myfile_upload/success.html を以下の内容に変更
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>ファイルアップロードアプリ</title>
</head>
<body>
<h1>ファイル {{ orgfilename }} を {{ dfilename }} として、アップロードすることに成功しました!</h1>
</body>
</html>

12.2.3 myfile_upload/views.py を以下に変更します。

  • テンポラリファイルを利用するように変更
  • success画面で、セッションに保存したファイル名を利用する様に変更
  • セッションでの値の保存は request.session[‘名前’]=値
  • 保存していたセッションでの値の取り出し request.session[‘名前’]
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
import tempfile

# Imaginary function to handle an uploaded file.
# from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            tfname=handle_uploaded_file(request.FILES['file'])
            request.session['org_fname']=request.FILES['file'].name
            request.session['tmp_fname']=tfname
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render(request, 'myfile_upload/index.html', {'form': form})

def handle_uploaded_file(f):
    # fname='files/'+f.name
    tfname=""
    # print("fname:"+fname)
    # with open(fname, 'wb+') as destination:
    with tempfile.NamedTemporaryFile(mode='wb+',delete=False) as destination:
        tfname=destination.name
        print(tfname)
        for chunk in f.chunks():
            destination.write(chunk)
    return tfname

def success(request):
    return render(request, 'myfile_upload/success.html', {'orgfilename':request.session['org_fname'], 'dfilename':request.session['tmp_fname']})

12.2.4 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver

12.3 この章まとめ

  • セッション関係の機能を利用
  • 一時ファイル(tempfile)を利用

13 カスタムユーザーの作成(AbstractUserを継承タイプ)

13.1 以下の操作を行っている動画


13.2 カスタムユーザー設定(AbstractUserを継承タイプ)を行ってプロジェクトを始める手順

13.2.1 mytest004というアプリを作成し、その中にmyuserというアプリケーションを作成

django-admin startproject mytest004
cd mytest004
python manage.py startapp myuser

13.2.2 mytest004/settings.py 修正

  • INSTALLED_APPS に ‘myuser’, を追加
  • AUTH_USER_MODEL = ‘myuser.User’ を追加
... 省略
# Application definition

INSTALLED_APPS = [
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myuser',
]

AUTH_USER_MODEL = 'myuser.User'
... 省略

13.2.3 myuser/models.py 修正

  • 「pass はヌル操作 (null operation) です — pass が実行されても、何も起きません。 pass は、構文法的には文が必要だが、コードとしては何も実行したくない場合のプレースホルダとして有用です。」 https://docs.python.org/ja/3/reference/simple_stmts.html#pass から引用
  • 後でUserを修正してカスタマイズ出来ます
# from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

13.2.4 myuser/admin.py 修正

rom django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

13.2.5 migration作成、適用

python manage.py makemigrations
python manage.py migrate

13.2.6 superuserを作成

python manage.py createsuperuser
  • 以下の感じで、ユーザーネーム、メールアドレスを入れて(ここでメールアドレスは適当なもにしてます、わたしのメールアドレスではありません)
Username: admin
Email address: xxx@xxx.xxx
Password: 
Password (again): 
  • パスワードが簡単すぎると以下のようなメッセージが表示されます、以下の例では強引にそれを認めさせてます
This password is too short. It must contain at least 8 characters.
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
  • パスワード2つ入力したものが異なると以下のメッセージが出ます。そのときは再度2回同じパスワードを入れてください
Error: Your passwords didn't match.

13.2.7 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver
  • http://localhost:8000/admin/ にアクセスして、設定した管理者権限のユーザー名とパスワードでログインします

13.3 この章まとめ

  • プロジェクト開始時にカスタムユーザー作成

14 カスタムユーザーの作成2(AbstractBaseUserを継承タイプ)

  • 前章はAbstractUserを継承していましたが、今回はより自由度が高くなるAbstractBaseUserを継承
  • myuser/models.py 修正する箇所が異なるのみで、他はほぼ一緒
  • 本家で関係する文書 は前章と同じになります。
  • 関係するインストールしたpythonファイル (ローカルの~/python3/にインストールした場合) ~/python3/dist-packages/django/contrib/auth/models.py

14.1 以下の操作を行っている動画


14.2 カスタムユーザー設定(AbstractBaseUserを継承タイプ)を行ってプロジェクトを始める手順

14.2.1 mytest005というアプリを作成し、その中にmyuserというアプリケーションを作成

django-admin startproject mytest005
cd mytest005
python manage.py startapp myuser

14.2.2 mytest005/settings.py 修正

  • INSTALLED_APPS に ‘myuser’, を追加
  • AUTH_USER_MODEL = ‘myuser.User’ を追加
... 省略
# Application definition

INSTALLED_APPS = [
    'myuser',
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

AUTH_USER_MODEL = 'myuser.User'
... 省略

14.2.3 mytest005/settings.py 修正2

  • ローカライズの修正も行います。(今回特に必用ないけど)
  • LANGUAGE_CODE を ‘en-us’ から ‘ja’に変更
  • TIME_ZONE を ‘UTC’ から ‘Asia/Tokyo’ に変更
... 省略
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
... 省略

14.2.4 myuser/models.py 修正

  • 関係するインストールしたpythonファイル (ローカルの~/python3/にインストールした場合) ~/python3/dist-packages/django/contrib/auth/models.py をベースに削って作成。修正内容は以下の感じ
    • 関係しそうなimportのみを残す
    • from django.contrib.auth.models import PermissionsMixin, UserManager を追加(オリジナルの PermissionsMixin, UserManagerを利用するため)
    • from django.contrib.auth.validators import UnicodeUsernameValidator を追加
    • AbstractUser 部分をコピペ
      • クラス名をUserに変更
      • abstract = True をコメントアウト
  • 後で修正してカスタマイズ可能
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from django.contrib.auth.models import PermissionsMixin, UserManager

from django.contrib.auth.validators import UnicodeUsernameValidator


class User(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        # abstract = True

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

14.2.5 myuser/admin.py 修正

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

14.2.6 migration作成、適用

python manage.py makemigrations
python manage.py migrate

14.2.7 superuserを作成

  • 質問に答えていって、作成
  • 実際の途中の様子は前章参照
python manage.py createsuperuser

14.2.8 サーバーを起動しブラウザで確認

  • 以下でサーバー起動
python manage.py runserver
  • http://localhost:8000/admin/ にアクセスして、設定した管理者権限のユーザー名とパスワードでログインします

14.3 この章まとめ

  • プロジェクト開始時にカスタムユーザー作成

15 郵便番号検索アプリの作成

15.1 以下の操作を行っている動画


15.2 郵便番号csvデータの取得と準備

15.2.1 データの取得が面倒な方は ken_all_utf_wh.json として以下を保存

  • 3つのデータのみ入っているデータ
[
  {
    "model": "zipcodes.zipcode",
    "pk": 1,
    "fields": {
      "jisx": "01101",
      "zipold": "060  ",
      "zip": "0600000",
      "prefecture": "ホッカイドウ",
      "city": "サッポロシチュウオウク",
      "street": "イカニケイサイガナイバアイ",
      "kprefecture": "北海道",
      "kcity": "札幌市中央区",
      "kstreet": "以下に掲載がない場合",
      "c1": "0",
      "c2": "0",
      "c3": "0",
      "c4": "0",
      "c5": "0",
      "c6": "0"
    }
  },
  {
    "model": "zipcodes.zipcode",
    "pk": 79393,
    "fields": {
      "jisx": "26104",
      "zipold": "604  ",
      "zip": "6048182",
      "prefecture": "キョウトフ",
      "city": "キョウトシナカギョウク",
      "street": "オオサカザイモクチョウ",
      "kprefecture": "京都府",
      "kcity": "京都市中京区",
      "kstreet": "大阪材木町",
      "c1": "0",
      "c2": "0",
      "c3": "0",
      "c4": "0",
      "c5": "0",
      "c6": "0"
    }
  },
  {
    "model": "zipcodes.zipcode",
    "pk": 124340,
    "fields": {
      "jisx": "47382",
      "zipold": "90718",
      "zip": "9071801",
      "prefecture": "オキナワケン",
      "city": "ヤエヤマグンヨナグニチョウ",
      "street": "ヨナグニ",
      "kprefecture": "沖縄県",
      "kcity": "八重山郡与那国町",
      "kstreet": "与那国",
      "c1": "0",
      "c2": "0",
      "c3": "0",
      "c4": "0",
      "c5": "0",
      "c6": "0"
    }
  }
]

15.2.2 データの取得

  • 郵便番号と住所のデータの準備が面倒な方は上のken_all_utf_wh.jsonを準備してスキップ可
  1. ブラウザでダウンロードしに行く場合
  2. wgetを利用してのダウンロードする場合
    • データと、データの説明ページをダウンロードしてます
    wget "https://www.post.japanpost.jp/zipcode/dl/kogaki/zip/ken_all.zip"
    wget "https://www.post.japanpost.jp/zipcode/dl/readme.html"
    
  3. unarを利用してzipファイルを解凍する場合
    • unar以外のツールでzipを解凍してもOK
    • KEN_ALL.CSV を取り出せます
    unar ken_all.zip
    
  4. 文字コードをユニコードに変換(nkfを利用する場合)
    • 複数の文字コードを変換できるエディタや、ツールを利用してもOK
    • ここではnkfコマンドでやってみます。
    • 環境がユニコードなら以下でOK
    • 作成したファイル名は ken_all_utf.csv
    nkf KEN_ALL.CSV > ken_all_utf.csv
    

    **

  5. 1行目に各列のデータ名をつける
    jisx,zipold,zip,prefecture,city,street,kprefecture,kcity,kstreet,c1,c2,c3,c4,c5,c6
    
    • echo と cat コマンドで行うと以下の感じ、普通のエディタで上の1行をデータの頭に入れてもOK
    echo "jisx,zipold,zip,prefecture,city,street,kprefecture,kcity,kstreet,c1,c2,c3,c4,c5,c6" > ken_all_head.csv
    cat ken_all_head.csv ken_all_utf.csv > ken_all_utf_wh.csv
    
  6. csvをjsonデータに変換
    • 他のツールを使ってもOK
    • 以下の内容のpythonファイルを作成し、convert.pyという名前で保存
    import csv
    import json
    
    with open('ken_all_utf_wh.csv', 'r') as f:
      r = csv.DictReader(f)
      c = 1
      a=[]
      for row in r:
        a.append({"model" : "zipcodes.zipcode", "pk" : c, 'fields' : row })
        c=c+1
      print(json.dumps(a, ensure_ascii=False, indent=2))
    
    • 以下を実行すると ken_all_utf_wh.csv(ヘッダー追加してユニコードにしたcsvファイル)をjson形式に変換し ken_all_utf_wh.json として保存
    python convert.py > ken_all_utf_wh.json
    

15.3 郵便番号検索アプリの作成手順

15.3.1 myzipcodes というアプリを作成し、その中にmyappというアプリケーションを作成

  • カスタムユーザーを最初に追加した方が後々カスタムユーザーを使う時に便利になりますが、説明上カスタムユーザーを追加せずに作って行きます。(前章を参考にカスタムユーザーを作っておく方が良い。)
django-admin startproject myzipcodes
cd myzipcodes
python manage.py startapp zipcodes

15.3.2 myzipcodes/settings.py 修正

  • INSTALLED_APPS に ‘zipcodes.apps.ZipcodesConfig’, を追加
... 省略
# Application definition

INSTALLED_APPS = [
    'zipcodes.apps.ZipcodesConfig',
    'mypage.apps.MypageConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

... 省略

15.3.3 myzipcodes/settings.py 修正2

  • ローカライズの修正も行います。(今回特に必用ないけど)
  • LANGUAGE_CODE を ‘en-us’ から ‘ja’に変更
  • TIME_ZONE を ‘UTC’ から ‘Asia/Tokyo’ に変更
... 省略
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
... 省略

15.3.4 zipcodes/models.py 修正

from django.db import models

class Zipcode(models.Model):
    jisx = models.CharField(max_length=20)
    zipold = models.CharField(max_length=10)
    zip = models.CharField(max_length=10)
    prefecture = models.CharField(max_length=200)
    city = models.CharField(max_length=200)
    street = models.CharField(max_length=200)
    kprefecture = models.CharField(max_length=200)
    kcity = models.CharField(max_length=200)
    kstreet = models.CharField(max_length=200)
    c1 = models.CharField(max_length=10)
    c2 = models.CharField(max_length=10)
    c3 = models.CharField(max_length=10)
    c4 = models.CharField(max_length=10)
    c5 = models.CharField(max_length=10)
    c6 = models.CharField(max_length=10)

15.3.5 zipcodes/admin.py 修正

  • Zipcodeを管理サイトに登録(これで管理メニューでzipcodeをいじれます)
from django.contrib import admin

from .models import Zipcode

admin.site.register(Zipcode)

15.3.6 migration作成、適用

python manage.py makemigrations
python manage.py migrate

15.3.7 郵便局から得たcsvファイルを前節で加工して生成した ken_all_utf_wh.json ファイルをデータベースに読み込み

python manage.py loaddata ../ken_all_utf_wh.json

15.3.8 zipcodes/views.py を以下に修正

from django.shortcuts import render, get_object_or_404, get_list_or_404
from .models import Zipcode
from django.db.models import Q

def getAddress(request,zipcode):
  d=get_list_or_404(Zipcode, zip__regex=zipcode)
  return render(request, 'zipcodes/getAddress.html', {'zipcode':zipcode, 'd':d})

def getZipcodesStreet(request,s):
  d=get_list_or_404(Zipcode, kstreet__regex=s)
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"町域名", 'd':d})

def getZipcodesCity(request,s):
  d=get_list_or_404(Zipcode, kcity__regex=s)
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"市区町村名", 'd':d})

def getZipcodesPrefecture(request,s):
  d=get_list_or_404(Zipcode, kprefecture__regex=s)
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"都道府県名", 'd':d})

def getZipcodesOr(request,s):
  d=get_list_or_404(Zipcode, Q(kprefecture__regex=s)|Q(kcity__regex=s)|Q(kstreet__regex=s))
  return render(request, 'zipcodes/getZipcodes.html', {'s':s, 'x':"都道>府県名あるいは市区町村名あるいは町域名", 'd':d})

15.3.9 zipcodes/templates/zipcodes/getAddress.html を作成

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>郵便番号{{zipcode}}を含む住所</title>
</head>
<body>
<h1>郵便番号{{zipcode}}を含む住所</h1>
{% for a in d %}
    <li>{{ a.zip }} {{ a.kprefecture}} {{ a.kcity }} {{ a.kstreet }}</a></li>
{% endfor %}
</body>
</html>

15.3.10 zipcodes/templates/zipcodes/getZipcodes.html を作成

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>"{{s}}"を"{{x}}"に含む住所</title>
</head>
<body>
<h1>"{{s}}"を"{{x}}"に含む住所</h1>
{% for a in d %}
    <li>{{ a.zip }} {{ a.kprefecture}} {{ a.kcity }} {{ a.kstreet }}</a></li>
{% endfor %}
</body>
</html>

15.3.11 myzipcodes/urls.py を以下に修正

from django.contrib import admin
from django.urls import path, re_path
from zipcodes import views as zviews

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^zip/(?P<zipcode>\d{1,7})$', zviews.getAddress, name="getAddress"),
    re_path(r'^street/(?P<s>.+)$', zviews.getZipcodesStreet, name="getZipcodesStreet"),
    re_path(r'^city/(?P<s>.+)$', zviews.getZipcodesCity, name="getZipcodesCity"),
    re_path(r'^prefecture/(?P<s>.+)$', zviews.getZipcodesPrefecture, name="getZipcodesPrefecture"),
    re_path(r'^address/(?P<s>.+)$', zviews.getZipcodesOr, name="getZipcodesPrefecture"),
]

15.4 この章でのまとめ

  • データベースとの連携機能の利用
  • データベースへのデータのインポート
  • 今までの章でやってきたテクの利用

16 djangoのshellの活用

16.1 以下の操作を行っている動画


16.2 動画で出てくる操作

  • 操作は一つ前の章で使った郵便番号検索アプリで行ってます

16.2.1 dangoのシェルの起動

  • 郵便番号アプリのプロジェクトのベースディレクトリ(manage.pyがあるディレクトリ)にはいってから
python manage.py shell
  • 現状やると以下の画面に
$ python manage.py shell
Python 3.7.5 (default, Nov 20 2019, 09:21:52) 
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: 

16.2.2 モデルクラスのimport

  • 郵便番号検索アプリの場合は Zipcode クラスをimport
  • import のfromの部分は、アプリ名とmodelsを . で結合し、郵便番号アプリのZipcodeの場合は zipcodes.models になる
from zipcodes.models import Zipcode

16.2.3 モデルクラスで検索

  1. 全部のデータを取得し、個数を表示
    • dに検索結果を入れる
    d=Zipcode.objects.all()
    len(d)
    
  2. pk=1のデータっを取得し、zipcodeや色々なデータを表示
    • dに検索結果を入れる
    d=Zipcode.objects.filter(pk=1)
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
  3. 県名が”大阪府”で、市名が”大阪市”のデータを取得し数を数え、一番最初のデータの表示
    • And条件
    d=Zipcode.objects.filter(kprefecture="大阪府",kcity__startswith="大阪市")
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
  4. Qオブジェクトを利用してORとかAnd
    • Q()で条件をくるんで
    • ANDは&で結合
    • ORは|で結合
    • “府”で終わる都道府県名で、市区町村名に”区”が含まれる And条件だと
    from django.db.models import Q
    d=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity__contains="区"))
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
    • Qオブジェクトを利用しないANDケースだと
    d=Zipcode.objects.filter(kprefecture__endswith="府",kcity__contains="区")
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    
    • “府”で終わる都道府県名か、市区町村名に”区”が含まれる OR条件だと
    d=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity__contains="区"))
    print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kstreet])
    

16.2.4 ここまでの操作の画面のコピー

$ python manage.py shell
Python 3.7.5 (default, Nov 20 2019, 09:21:52) 
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from zipcodes.models import Zipcode
   ...: 

In [2]: d=Zipcode.objects.all()
   ...: len(d)
   ...: 
Out[2]: 124340

In [3]: d=Zipcode.objects.filter(pk=1)
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[1, '0600000', '北海道', '札幌市中央区', '以下に掲載がない場合']

In [4]: d=Zipcode.objects.filter(kprefecture="大阪府",kcity__startswith=
   ...: "大阪市")
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[966, '5340000', '大阪府', '大阪市都島区', '以下に掲載がない場合']

In [5]: from django.db.models import Q
   ...: d=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity__
   ...: contains="区"))
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[6214, '6030000', '京都府', '京都市北区', '以下に掲載がない場合']

In [6]: d=Zipcode.objects.filter(kprefecture__endswith="府",kcity__conta
   ...: ins="区")
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[6214, '6030000', '京都府', '京都市北区', '以下に掲載がない場合']

In [7]: d=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity__
   ...: contains="区"))
   ...: print([ len(d), d[0].zip, d[0].kprefecture, d[0].kcity, d[0].kst
   ...: reet])
   ...: 
[24247, '0600000', '北海道', '札幌市中央区', '以下に掲載がない場合']

In [8]: 

16.3 この章でのまとめ

  • shell機能を活用することで、インタラクティブに動作確認や機能の確認を行うことができる(デバッグや、アプリ構築の際に非常に便利)

17 MySQLの利用

17.1 以下の操作を行っている動画


17.2 MySQLのインストール

17.2.1 Ubuntuの場合

sudo apt install mysql-client mysql-server

17.2.2 WindowsやMacの場合

  • 本家のGetting Startや、「OS名 MySQL インストール」などでネット検索かけて頂ければ、インストール方法みつかります

17.3 MySQLへの最初のログイン

  • 最初結構はまりました、/usr/share/doc/mysql-clientにも文書なかったし
  • Ubuntuの19.10の場合、インストール時にrootパスワードの設定がでてきません
  • 以下のコマンドを実行して、パスワードを設定してください。
  • 残りの質問は全部デフォルト(エンターのみ)でOK
sudo mysql_secure_installation
  • 設定してる画面は以下の感じ

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: 
Please set the password for root here.

New password: 

Re-enter new password: 
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : 

 ... skipping.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : 

 ... skipping.
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : 

 ... skipping.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : 

 ... skipping.
All done! 

17.4 MySQLへの最初の接続

  • 以下のコマンドを実行し、上で設定したパスワードを入力するとプロンプトが表示されます
sudo mysql -u root -p
  • ログインが上手くいくと以下の感じに
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 8.0.18-0ubuntu0.19.10.1 (Ubuntu)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

17.5 django用のデータベース作成

  • database_nameは好きなデータベース名に
CREATE DATABASE database_name;
  • 例えばデータベース名を testdb とすると
CREATE DATABASE testdb;

17.6 ユーザーの作成

  • username は希望するユーザー名に
  • host はホスト名,ローカルならlocalhost
  • passward は 希望するパスワードに
CREATE USER 'user_name'@'host' IDENTIFIED BY 'password';
  • 例えば、ユーザー名をtester, ホスト名を localhost, パスワードを how7OjIcus にするなら
CREATE USER 'tester'@'localhost' IDENTIFIED BY 'how7OjIcus';

17.7 ユーザーに権限を付与

  • ‘tester’@’localhost’ にdatabase_nameがtestdb への操作を全部許可する場合
GRANT ALL PRIVILEGES ON testdb.* TO 'tester'@'localhost';
FLUSH PRIVILEGES;

17.8 データベース一覧表示

SHOW DATABASES;

17.9 作成したユーザーで作成したデータベースにアクセスできるか確認

  • パスワードをきかれたら、設定したパスワードを答える
mysql  -u tester -p testdb

17.10 python3-mysqldb(mysqlclient)のインストール

  • sudo apt install python3-mysqldb だと以下になってバージョン古すぎメッセージが出て使えないので、pipでインストール
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 1.3.10.
  • mysql_configが必用になるため、mysql_configを含む libmysqlclient-dev をインストール
sudo apt install libmysqlclient-dev
  • Ubuntuでローカルにインストールする場合
  • windowsとかMacなど他なら、pip3をpipにして -t 以降無しで実行 ( pip install mysqlclient )
pip3 install mysqlclient -t ~/python3/dist-packages
  • これらをアップグレードする場合
pip3 install --upgrade Django -t ~/python3/dist-packages
pip3 install --upgrade mysqlclient -t ~/python3/dist-packages

17.10.1 UbuntuなどDebian系以外の場合(windowsやmacや他の系統のLinux)

  • ローカルじゃなく普通にインストールする場合
pip install mysqlclient

17.11 MySQLをプロジェクトで利用

17.11.1 プロジェクトの作成

  • カスタムユーザーアプリの作成と、カスタムユーザーアプリの初期設定は省略します。通常の場合、後でカスタムユーザー追加は操作が大変なので最初に必ず追加しましょう。
django-admin startproject mytest007
cd mytest007

17.11.2 プロジェクト名/setting.pyを変更

  • 作成したデータベース名 testdb
  • ログインユーザー名が tester@localhost の場合
  • 日本語設定や、タイムゾーンの設定は省略します。
DATABASES = {
    #    'default': {
    #        'ENGINE': 'django.db.backends.sqlite3',
    #        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #    }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'testdb',
        'USER': 'tester',
        'PASSWORD': 'how7OjIcus', 
        'HOST': '',
        'PORT': '',
    }
}

17.11.3 データベースとの連携

python manage.py makemigrations
python manage.py migrate

17.11.4 作成されたtableを確認

$ mysql -u tester -p testdb
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 8.0.18-0ubuntu0.19.10.1 (Ubuntu)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show tables;
+------------------------------+
| Tables_in_testdb           |
+------------------------------+
| auth_group                   |
| auth_group_permissions       |
| auth_permission              |
| django_admin_log             |
| django_content_type          |
| django_migrations            |
| django_session               |
+------------------------------+
11 rows in set (0.01 sec)

mysql> quit
Bye

17.11.5 作成したデータベースのバックアップ

  • ユーザー名 tester
  • パスワードが how7OjIcus
  • データベース名が testdb
  • バックアップファイル名が dump.sqlの場合
mysqldump -u tester -phow7OjIcus testdb > dump.sql

17.12 この章について

  • デフォルトのSqlite3ではなくMySQLをデータベースとして使う方法
  • 他MySQLを使うにあたっての最低限の操作例
  • ユーザー名やパスワードは同じものを使わないで!

18 実際裏で実行されてるSQLの確認方法

18.1 以下の操作を行っている動画


18.2 裏で実行されてるSQLの確認方法例

  • 以前この文書で作成した 郵便番号検索アプリ を利用して試してみます
  • djangoのshell機能を利用します
  • DEBUG=Trueの時のみこの方法可能だそうです

18.2.1 djangoのshell起動

  • 郵便番号アプリのmanage.pyがあるディレクトリで以下を実行
python manage.py shell

18.2.2 モデルのクラスをimport

from zipcodes.models import Zipcode
from django.db.models import Q

18.2.3 実際に調べてみる1

d1=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity__contains="区"))
str(d1.query)

18.2.4 実際に調べてみる2

d2=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity__contains="区"))
str(d2.query)

18.2.5 実際に調べてみる3

d3=Zipcode.objects.filter(kprefecture__endswith="府",kcity__contains="区")
str(d3.query)

18.2.6 実際に調べてみる4

from django.db import connection
connection.queries

18.2.7 実際に操作したときのスクショ

$ python manage.py shell
Python 3.7.5 (default, Nov 20 2019, 09:21:52) 
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from zipcodes.models import Zipcode

In [2]: from django.db.models import Q

In [3]: d1=Zipcode.objects.filter(Q(kprefecture__endswith="府")|Q(kcity_
   ...: _contains="区"))

In [4]: str(d1.query)
Out[4]: 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE %府 ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE %区% ESCAPE \'\\\')'

In [5]: from django.db import connection

In [6]: connection.queries
Out[6]: []

In [7]: len(d1)
Out[7]: 24247

In [8]: connection.queries
Out[8]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'}]

In [9]: d2=Zipcode.objects.filter(Q(kprefecture__endswith="府")&Q(kcity_
   ...: _contains="区"))
   ...: str(d2.query)
   ...: 
Out[9]: 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE %府 ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE %区% ESCAPE \'\\\')'

In [10]: connection.queries
Out[10]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'}]

In [11]: len(d2)
Out[11]: 6214

In [12]: connection.queries
Out[12]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.030'}]

In [13]: d3=Zipcode.objects.filter(kprefecture__endswith="府",kcity__con
    ...: tains="区")
    ...: str(d3.query)
    ...: 
Out[13]: 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kcity" LIKE %区% ESCAPE \'\\\' AND "zipcodes_zipcode"."kprefecture" LIKE %府 ESCAPE \'\\\')'

In [14]: len(d3)
Out[14]: 6214

In [15]: connection.queries
Out[15]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.030'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\')',
  'time': '0.036'}]

In [16]: connection.queries4
------------------------------------------------------------------------
AttributeError                         Traceback (most recent call last)
<ipython-input-16-7e2a5aa2c672> in <module>()
----> 1 connection.queries4

/home/pano/python3/dist-packages/django/db/__init__.py in __getattr__(self, item)
     26     """
     27     def __getattr__(self, item):
---> 28         return getattr(connections[DEFAULT_DB_ALIAS], item)
     29 
     30     def __setattr__(self, name, value):

AttributeError: 'DatabaseWrapper' object has no attribute 'queries4'

In [17]: connection.queries
Out[17]: 
[{'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' OR "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.001'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\')',
  'time': '0.030'},
 {'sql': 'SELECT "zipcodes_zipcode"."id", "zipcodes_zipcode"."jisx", "zipcodes_zipcode"."zipold", "zipcodes_zipcode"."zip", "zipcodes_zipcode"."prefecture", "zipcodes_zipcode"."city", "zipcodes_zipcode"."street", "zipcodes_zipcode"."kprefecture", "zipcodes_zipcode"."kcity", "zipcodes_zipcode"."kstreet", "zipcodes_zipcode"."c1", "zipcodes_zipcode"."c2", "zipcodes_zipcode"."c3", "zipcodes_zipcode"."c4", "zipcodes_zipcode"."c5", "zipcodes_zipcode"."c6" FROM "zipcodes_zipcode" WHERE ("zipcodes_zipcode"."kcity" LIKE \'%区%\' ESCAPE \'\\\' AND "zipcodes_zipcode"."kprefecture" LIKE \'%府\' ESCAPE \'\\\')',
  'time': '0.036'}]

In [18]: quit

18.3 この章のまとめ

  • モデルを利用した時に裏で実行されているSQL文の確認方法を試してみた
  • 実際SQL文が実行されるタイミングは必用となってからのケースもある

19 この文書のチェンジログ

  • 2019/11/10 初版
  • 2019/11/10 修正および、テンプレートファイルを利用する章の追加
  • 2019/11/10 足し算アプリ作成する章の追加
  • 2019/11/11 一部修正及び、ファイルアップロードする章の追加
  • 2019/11/11 urlからパラメーター取り出しする章の追加
  • 2019/11/11 カスタムフィルタ作成利用の章追加
  • 2019/11/16 セッションの利用の章を追加
  • 2019/11/16 python3 と入力していた命令を python に変更(Ubuntu以外はpythonであるため) Ubuntuの場合上で.bash_rcで設定しておらず、python2系と3系をインストールしているならpythonとコマンド入力している部分はpython3と入力すべきかも
  • 2019/11/17 カスタムユーザー(AbstractUserを継承タイプ)の章追加
  • 2019/11/18 カスタムユーザー(AbstractBaseUserを継承タイプ)の章追加、最初の概要変更
  • 2019/11/27 郵便番号検索アプリ作成の章追加
  • 2019/11/28 djangoのシェル機能の章追加
  • 2019/11/28 MySQLの利用の章追加
  • 2019/11/28 実際裏で実行されてるSQLの確認方法の章追加

20 続きを追記してきます。

著者: NM Max

Created: 2019-11-28 木 07:43

Validate

Python django 事始め

Ubuntuのパッケージバージョンのdjangoのインストール

# python3 バージョンのdjangoインストール
sudo apt update
sudo apt install python3-django

# django のバージョン確認(python3)
python3 -m django --version

# python2 バージョンのdjangoインストール
sudo apt update
sudo apt install python-django

# django のバージョン確認(python2)
python -m django --version

pipを利用して最新のdjangoをインストール

#Python3 pip3のインストール(Ubuntu)
sudo apt install python3-pip python3 

#ローカルのpython3/dist-packagesにdjangoをインストール
mkdir -p python3/dist-packages
pip3 install Django -t python3/dist-packages

# python3でパッケージの検索パス確認方法
import sys
print(sys.path)

# ローカルのパッケージとパッケージに含まれる実行ファイルを環境変数に追加する
# ~/.bashrcファイルの最後に追加し新しいターミナルを開くか再起動すると有効化
export PYTHONPATH="$HOME/python3/dist-packages:$PYTHONPATH"
export PATH="$HOME/python3/dist-packages/bin:$PATH"

# django のバージョン確認(python3)
python3 -m django --version