Start_python’s diary

ふたり暮らし

アラフィフ夫婦のフリーランスプラン

祝100記事/1月の振り返り

100記事達成しました!

なんと!この記事が100記事目です。長いようであっという間でもあります。

記事数が増えてくるとわからないところを見返して調べるとこも多くなってきました。とりあえず昔のコードもナンバリングして残してあるので、そのコードから引っ張ってきて新しく利用する機会もでてきます。

もしブログをせずにプログラミングの勉強だけ進めていたら「あれ?これ昔やったけどどうだったっけ?」とコードもなくまたググっていたことでしょう。現在プログラミングの勉強中の方はどのようにしてるか気になるところです。(いい方法があれば教えてほしいです。)これからプログラミングを始める方も是非ブログなどで経過や詰まったところなど残しておくことをおすすめします。「こんなこともわかってなかったのか」と昔の記事を見ながら振り返れたら楽しそうです。

 

1月を振り返って

Python(プログラミング)とブログを始めてから3ヶ月経ちました。最初のころは簡単なことをやってたとこもありすぐ理解できていたと思うのですが、今は新しいことが出てくるとなかなか理解できなくなってきました。

f:id:Start_python:20200130102658p:plain

まさに上の図でいう「なんも分からん」状態です。

本当に「完全に理解した」と思える時期がありました。「Python使えばなんでも出来る」と思ったり「ああ、これはPythonで出来そうだな」と考えたりしました。今では「Pythonで出来ること」は何があるか、「これちょっと無理なんじゃない?」と考えてしまいます。

当面の目標はマッチングアプリ(恋活アプリではないやつ)を形にすることです。完成時期は決めずにじっくり腰を据えてやっていきます。

Flask 複数の実行ファイルを一つにする方法(トップページに目次を作成)

f:id:Start_python:20200129140947p:plain

 

はじめに

新しいWebアプリを作成したいけど「PythonAnywhere」だと無料では1つのトップページしか登録できません。以前作成したのも残したいので、トップページに目次のようなものを作り各Webアプリを実行できるようにしていきます。今回は「Blueprints」というモジュールを使います。通常「Blueprints」は長くなった一つの実行ファイルを分けるために使用するみたいです。ディレクトリ構成やファイル名なども今回から真剣に考えていきます。

start-python.hateblo.jp

このときに作成した簡単なWebページのコードを使っていきます。

 

動作環境

Windows10
Python 3.7.5
Flask 1.1.1

作業フォルダ/
├─ templates/
│    └─ index.html    ・・・(3)
├─ static/
│    └─ test/
│          ├─ test_style.css ・・・(5)
│          └─ test_script.js ・・・(5)
├─ test/
│    ├─ templates/
│    │    ├─ test_home.heml  ・・・(4)
│    │    └─ test_hellow.heml ・・・(4)
│    ├─ __init__.py
│    └─ test_app.py    ・・・(2)
└─ main.py     ・・・(1)

 

1.起動ファイル

まずは「main.py」から解説します。

from flask import Flask, render_template

app = Flask(__name__)

from test.test_app import app1
app.register_blueprint(app1)


@app.route('/')
def index():
    return render_template('index.html')


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

新しく出てきたのはこの部分

from test.test_app import app1
app.register_blueprint(app1)

「testフォルダ」にある「test_app.py(2)」の「app1」を読み込みます。
「app1」を「app」に登録します。

注意点として、必ず「app = Flask(__name__)」のあとで登録してください。

2.呼び出される実行ファイル

次に「test_app.py」の解説です。

from flask import Blueprint, render_template, request

app1 = Blueprint('test_app', __name__, template_folder='templates')

@app1.route('/test_home.html')
def test_home():
    return render_template('test_home.html')


@app1.route('/test', methods=['GET', 'POST'])
def test():
    if request.method == 'GET':
        res = request.args.get('get_button')
    elif request.method == 'POST':
        res = render_template('test_hellow.html')

    return res

1行目で「Blueprint」をインポートしています。

app1 = Blueprint('test_app', __name__, template_folder='templates')

「app1」を「Blueprint()」を使って定義します。このときにテンプレートのフォルダ(HTMLファイルが入るフォルダ)を「templates」に設定します。

注意点は、必ず同じフォルダに中身は空の「__init__.py」を作ってください。

3.実行後に開くページ(HTMLファイル)

「main.py」の「@app.route('/')」のときに読み込まれるページ「index.html」です。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>ふたり暮らし メニュー画面</title>
  </head>
  <body>

    <h2>メインメニュー</h2>
    <p><a href= "/test_home.html" >ボタンでページを移動する</a></p>
    <p></p>
    <p><a href= "xxxx.html" >ブログ(登録とログイン)-工事中</a></p>

  </body>
</html>

 

<a href= "/test_home.html" >文字</a>

文字をクリックするとページが移動します。
移動先は「URL(IPアドレス)/test_home.html」となり、「test_app.py」の「@app1.route('/test_home.html')」の処理が行われページが表示されます。

4.表示されるページ(HTMLファイル)

目次ページから「test_home.html」へ移動します。

<!DOCTYPE html>
<html lang="ja">
  <head>
  </head>
  <body>

    <form action="/test" method="get">
      <button name="get_button" value="from get" style="width:200px;height:100px">get ボタン</button>
    </form>

    <div style="margin-left:10%;margin-right:10%;">
      <form action="/test" method="post">
        <button name="post_button" style="width: 100%; padding: 10px;">post ボタン</button>
      </form>
    </div>

  </body>
</html>

こちらは前回のまま変更はありません。

「post ボタン」を押すと「test_hellow.html」が表示されます。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <link rel="stylesheet" href="{{url_for('static', filename='test/test_style.css')}}">
    <script src="{{url_for('static', filename='test/test_script.js')}}"></script>
  </head>
  <body>

    <h1>Hello</h1>
    <h2 id='test'>World</h2>

  </body>
</html>

※「CSSファイル」と「Javascriptファイル」の保存場所(読み込まれるフォルダ)を調べるのが大変でした。同じ苦労をされている方のために解説します。

ブラウザのキャッシュによってサーバー上は更新されても表示が反映されないという問題があります。

いい方法が見つからなかったので今回した対応は「毎回、CSSJavascriptのファイル名を変えながら試してみる」でした。

正解の保存場所は、「作業フォルダ/static/」でした。
HSMLファイルは「作業フォルダ/test/templates/」なので、てっきり「作業フォルダ/test/static/」だと思い込んでいました。

<link rel="stylesheet" href="{{url_for('static', filename='test/test_style.css')}}">
<script src="{{url_for('static', filename='test/test_script.js')}}"></script>

今回は上記にように「作業フォルダ/static/test/」に保存しました。(今後Webアプリが増えてきたら混ざるとややこしくなるため)

5.CSSファイルとJavascriptファイル

「test_style.css

h1 {
  color: red;
}

「test_script.js」

window.onload = function() {
  var e = document.getElementById('test');

  e.style.color = 'blue';
}

こちらも前回のまま変更はありません。

 

「PythonAnywhere」へアップしました

http://startpython.pythonanywhere.com/

問題なくアップロードできて動作確認もできました。
(冒頭で説明した通り、無料版では1つのトップページしか登録できませんので内容が消えたり変更される場合がございます)

 

まとめ

普通のホームページ作成(やったことないですが)のようにリンクを張ればページが切り替わると思ってましたが、なかなかてこずりました。後から複数のWebアプリを統一するのは大変なので早めにこの作業ができて良かったです。

何度も諦めそうになりましたが、いろんな方のサイトを参考にさせていただきながらなんとか完成できました。こちらのサイトもどなたかの手助けになれば幸いです。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

はじめてのCSS(HTMLでボールを動かす)

はじめに

今回はHTMLからCSSJavaScriptを外部から読み込み、図形を動かす方法について学んでいきます。CSSは何度か使ってきましたががっつり勉強する意味で「はじめてのCSS」にさせていただきました。また、文字に色を付けたりサイズを変更したりがCSSの基本になると思いますが、いきなり図形の表示からはじめます。

 

動作環境

Windows10
メモ帳(テキストエディタです)
Google Chrome(ブラウザです)

作業フォルダ/
├─ test.html
├─ test2.css
└─ test3.js

 

f:id:Start_python:20200127224055g:plain

HTMLの解説

test.html

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="test2.css">
  <script src="test3.js"></script>
  <title>ボールの移動</title>
</head>
<body>
  <div id="anime_circle">
  </div>
  <div class="anime_btn" onclick="clickTest();">
    ボタン
  </div>
</body>
</html>

それでは新しく出てきた部分を解説していきます。

<link rel="stylesheet" type="text/css" href="test2.css">

CSSファイルを呼び出します。「test2.css」の部分にファイル名が入ります。

ちなみに、rel属性の値には外部CSSスタイルシートと呼ぶので「stylesheet」を与えます。type属性の値にはtext/cssを指定します。href属性の値にはリンクする外部リソースのURL(ファイル名)が入ります。

<script src="test3.js"></script>

JavaScriptファイルを呼び出します。「test3.js」の部分にファイル名が入ります。

<div class="anime_btn" onclick="clickTest();">
  ボタン
</div>

クラス名を「anime_btn」にします。「ボタン」と表示します。

onclick="clickTest();"

クリックされたときにJavaScriptファイルにある「clickTest()」関数を呼び出します。

CSSの解説

test2.css

#anime_circle{
    height: 100px;
    width: 100px;
    background-color: #FF0000;
    border-radius: 50px;
    position: absolute;
    left: 100px;
    top: 0;
    transition: 2.5s;
}
#anime_circle.active {
    background-color: #0000FF;
    left: calc(100% - 100px);
    top: calc(100% - 100px);
}

.anime_btn {
    margin-top:  10px;
    display:  inline-block;
    padding:  5px 10px;
    border: solid 1px;
    cursor:  pointer;
}

CSSでは、id「anime_circle」とclass「anime_btn」についての設定をしています。

#anime_circle{

}

id名の前には「#」を付けます。id「anime_circle」の状態を設定します。

  • height: 高さ(単位はピクセル
  • width: 幅(単位はピクセル
  • background-color: 背景色
  • border-radius: 角丸の半径(単位はピクセル)省略すると四角形
  • position: 絶対位置(relativeの場合は相対位置)
  • left: 左の位置(単位はピクセル
  • top: 上の位置(単位はピクセル
  • transition: 変化が始まって終わるまでの時間(単位は秒)
#anime_circle.active {

}

id名の前には「#」を付けます。id「anime_circle」に「.active」という疑似classがついたときに状態(背景色と位置)が変化します。

.anime_btn {

}

class名の前には「.」を付けます。class「anime_btn」の設定をします。

  • margin-top: 上の余白(単位はピクセル
  • display: 横並び表示で大きさや余白の設定が可能な区画(ブロック)
  • padding: 文字から外枠までの余白(上下と左右)(単位はピクセル
  • border: ボーダースタイル(1本線)と線の太さ(単位はピクセル
  • cursor: マウスカーソルの形状(指の形)

JavaScriptの解説

test3.js

function clickTest() {
    target = document.getElementById("anime_circle");
    target.classList.toggle("active")
  }

clickTest()関数を作成します。class「anime_btn」がクリックされたときの処理です。

id「anime_circle」のドキュメントを変数「target」に代入します。

変数「target」に疑似クラスがない場合は、疑似クラス「active」を付けます。それ以外の場合は、疑似クラスはなしにします。

ボタンをクリックするたびにid(anime_circle ←→ anime_circle.active)が切り替わります。

 

まとめ

今回のポイントはCSSの「transition」ではないでしょうか。idが切り替わったときに瞬時に変化するのではなく、色や位置が徐々に変わっていくのはすごいです。
こちらのサイトにtransitionが詳しく説明されていてわかりやすかったです。

qiita.com

CSSJavaScriptのファイル名の付け方にも今後工夫が必要になりそうです。よく使われる名前は、CSSは「style.cssJavaScriptでは「script.js」みたいですが、タグ名と似ているとあとで編集するときに混乱しそうな気もします。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

はじめてのJavaScript(HTMLで現在の時刻を表示する)

はじめに

今回はJavaScriptをHTML内に<script>を用いて記述する方法について学んでいきます。JavaScriptを本格的に学ぶのは大変なので必要最小限で理解できるように心掛けます。

 

動作環境

Windows10
メモ帳(テキストエディタです)
Google Chrome(ブラウザです)

 

ただいまの時刻は

 

 

上のように現在の時刻を表示します。

<!DOCTYPE html>
<html>
<head>
  <title>時計</title>
  <script>
    window.onload = function() {
      setInterval(function() {
        var dd = new Date();
        document.getElementById("T1").innerHTML = dd.toLocaleString();
      }, 1000);
    }
  </script>
</head>
<body>
  <h2>ただいまの時刻は</h2>
  <div style="font-size:150%;">
    <div id="T1"></div>
  </div>
</body>
</html>

保存したファイルをダブルクリックしてブラウザで表示してみましょう。(右クリックで[プログラムから開く]→[Google Chrome]でもいいです)

 

解説

<script>

</script>

<script>タグは、JavaScriptなどのスクリプトを記述するために用います。
<script type="text/Javascript">のようにtype属性を指定されることがありますが、現在は省略されることが多いです。

ちなみにsrc属性でJavaScriptの外部jsファイルを読み込む場合には、<script src="sample.js"></script>のように記述します。

window.onload = function() {

}

「window.onload」はページが表示されてから実行されます。「window.onload」という変数にfunction() {   }内にある関数を代入します。つまり、ページが表示されてから実行される処理です。

setInterval(function() {
  var dd = new Date();
  document.getElementById("T1").innerHTML = dd.toLocaleString();
}, 1000);

はい、ここは難しいです。ゆっくり見ていきましょう。

setInterval(関数,処理間隔)

setIntervalは第一引数に与えられた関数を、第二引数に与えられた間隔で実行します。(functionの中を1000ミリ秒毎に実行します)

var 変数名 = 値

変数「dd」に現在の時刻「new Date()」を代入します。varは、変数の使用開始を明示的に宣言します。

document.getElementById("T1")

指定されたIDオブジェクトを返します。( id「T1」)

.innerHTML

中身を変えるメソッドです。

.toLocaleString()

地域や言語に適した日付や時刻の表記するメソッドです。

まとめると「id[T1]の中身が、現在の時刻を(日本語表記した内容に)1秒(1000ミリ秒)間隔で更新される。」となります。

今は「こんなものなんだ」くらいで大丈夫です。すべて覚える必要はないと思います。

<div style="font-size:150%;">

</div>

フォントサイズを指定します。<div>タグは、囲った部分をブロック要素としてまとめて扱います。(ここではCSSを使っています)

<div id="T1"></div>

<div>タグは、idやclassを付加することもできます。(今は中身が空白の状態です。あとで更新されます)

ちなみにid名はページ内に1度だけ個別に付けることができ、class属性は同じページ内に何度でも使ってグループとして扱うことができます。

 

まとめ

いかがでしたでしょうか。いきなり難しくなった感じがしますが、JavaScriptを記述する方法として<script>タグを使うことと、CSSを記述する方法として<div style=・・・>を使うことがわかれば大丈夫です。

この調子でJavaScriptの勉強を進めていきたいと思います。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

はじめてのHTML言語(HTML5 タグが省略可能になっていた)

はじめに

今回はHTMLの基本について学んでいきましょう。 はじめての方にも理解できるように具体例を使ってわかりやすく解説していきます。

 

動作環境

Windows10
メモ帳(テキストエディタです)
Google Chrome(ブラウザです)

 

まずは作ってみよう

<!DOCTYPE html>
<html>
<head>
<title>タイトル</title>
</head>
<body>
<h1>表題</h1>
<p>ここに本文が入ります</p>
</body>
</html>

メモ帳で上記の文書を作成します。入力が完了したら、メモ帳のメニューから [ファイル]→[名前を付けて保存] で入力した内容をファイルに保存してください。

f:id:Start_python:20200127093307p:plain

お好きな保存場所(どこに保存したか忘れないように)に保存します。
[ファイルの種類]を「すべてのファイル」に変更して[ファイル名]に「test.html」を入力して[保存]ボタンを押します。

ファイル名に関するルール

  • ファイル名のあとに「.html」を必ず付けてください。(拡張子と呼びます)
  • ファイル名は半角英数字と一部の記号文字のみを用いてください。
  • 使用可能な記号はドット(.)、ハイフン(-)、アンダーバー(_)程度に留めておくのが無難です。
  • スペース文字を含んだファイル名は使用しないでください。

実行してみましょう(ブラウザで表示してみましょう)

保存したファイル「test.html」をダブルクリックしてみます。(右クリックで[プログラムから開く]→[Google Chrome]でもいいです)

f:id:Start_python:20200127095829p:plain

このように表示されれば完了です。では解説していきます。

解説

<!DOCTYPE html>

HTML文書の最初の1行目にはドキュメントタイプを記述します。<!DOCTYPE html> は、この文書がHTMLの書式に従って記述されていることを示します。(省略可)

<html>

</html>

<html> で始まり </html> で終わるこの書き方をHTML(Hyper-Text Markup Language)と呼びます。2行目から最終行までを囲んでいます。(省略可)

ちなみに<html lang="ja">と記述して日本語であることを指定できます。

※ここまではHTMLの1つのルールみたいなものなんで「こういうものなんだ」で大丈夫です。

<head>

</head>

ヘッダ部と呼びます。ヘッダ部にはタイトルなどを記述します。(よく聞くヘッダーやフッターのヘッダー<header>とはHTML文書の中での使い方が全く異なります)

ちなみにヘッダ部ではタイトル記述以外にCSSの指定やJavascriptの読み込み、文字符号化(文字コード)などが出来ます。

<title>タイトル</title>

タイトル名を書きます。タイトルはブラウザのタイトルバーに表示されます。また「お気に入り」や「履歴」に表示されたり、検索エンジンの結果として表示されます。省略するとタイトル名は「ファイル名」になります。

タグのルール

<html> や <head> などをタグと呼び、htmlやheadなどをタグ名と呼びます。多くのタグは<html>~</html> のように開始タグ終了タグで囲みます。
ここまででなんとなくわかったと思いますが、開始タグ~終了タグは、他の開始タグ~終了タグの完全に外側か、完全に内側でなくてはなりません。

慣れるまでは開始タグを書いたらすぐに終了タグを書いておき、その中に他のタグを記述する方法がよいでしょう。

<body>

</body>

ブラウザに表示される本文の部分です。基本は<html>タグの中に<head>タグと<body>タグが入ります。

<h1>表題</h1>

章の表題を表わします。数字は表題のレベルで、1~6まで指定でき文字の大きさなどが変わります。

<p>ここに本文が入ります</p>

paragraphの略で、各段落を意味します。改行するために一行ずつ<p></p>で囲むことが多いです。HTML文書のテキストの改行は通常、ひとつの空白文字に変換されてしまいます。自分の好きな位置で改行するには<br>を用います。

 

ここまでは昔の話

今回は基本について学ぶということで今までのコードが読めるように解説してきました。しかし現在、HTML5になってから終了タグやタグ自体が省略できるようになっています。それを踏まえて昔と今を比較してみましょう。結果はどちらも同じです。

<!DOCTYPE html>
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body>
    <h1>表題</h1>
    <p>ここに本文が入ります</p>
  </body>
</html>
  • タグの範囲が一目でわかるように行の先頭に空白を入れています。

<!DOCTYPE html>
<title>タイトル</title>
<h1>表題</h1>
<p>ここに本文が入ります
  • <!DOCTYPE html>は省略可能ですが、省略しない方が良いみたいです。
  • <html>はその最初の内容がコメントでなければ省略可能になりました。
  • <head> は内容が空か、最初の内容が要素なら省略可能になりました。
  • <body> は内容が空か、最初の内容が空白文字かコメントでなければ省略可能になりました。ただし、最初の要素が meta, link, script, style, template なら省略できません。
  • 終了タグ</p>は少し条件が多いですが省略可能になりました。

 

まとめ

今後、HTMLを作成していく場合の記述方法はどちらでも大丈夫です。ただ基本がわかっていないといろいろ調べるときに昔の書き方を見たら内容がわからなくなるので今のところは昔の書き方のほうがいいのかなあと思います。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

Flask matplotlibを使ってグラフを表示する方法(JavaScriptのChart.jsを使ってグラフを描画)

はじめに

今回はちょっと寄り道してFlaskでグラフを表示してみます。本当はグラフのアニメーションを作りたかったのですが諦めました。あとで調べたところ、JavaScriptを使ったほうが簡単できれいに出来るみたいだったのでそちらも作成してみました。

 

動作環境

Windows10
Python 3.7.5
Flask 1.1.1

 

コード

test.py

from flask import Flask, render_template, make_response
from io import BytesIO
import urllib
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt

import random
import numpy as np


app = Flask(__name__)

fig = plt.figure()
ax = fig.add_subplot()

x = [1, 2, 3, 4, 5, 6]
y = [0, 1, 2, 3, 4, 5]


@app.route('/')
def index():
    plt.cla()

    rand = random.randint(0, 5)
    y[rand] += 1

    plt.title('sample')
    plt.bar(x, y)
    plt.legend()

    canvas = FigureCanvasAgg(fig)
    png_output = BytesIO()
    canvas.print_png(png_output)
    data = png_output.getvalue()

    response = make_response(data)
    response.headers['Content-Type'] = 'image/png'
    response.headers['Content-Length'] = len(data)

    return response


if __name__ == "__main__":
    app.run()

 

解説

こちらを参考にさせていただきました。こちらで詳しい解説はされています。

tkstock.site

簡単に説明すると、「plt」グラフを作成(描画はしていません)して「canvas」に画像を出力します。その画像データ「data」をレスポンス「response」で生成します。

png_output = BytesIO()

「BytesIO()」を使用すると普通はファイルに書き出す操作を省き仮想的にメモリ上に書き出すことができます。

data = png_output.getvalue()

バッファの全内容を含むバイト列を返します。

 

うーん、わかったようなわからないような(わかってない)。「グラフの画像データをメモリ上にバイト型データで渡して、また引っ張ってきて画像データに戻す」感じでしょうか。画像データをそのまま渡せたらいいのにと思ってしまいます。

 

f:id:Start_python:20200126185906p:plain

とりあえずグラフを表示できました。

 

rand = random.randint(0, 5)
y[rand] += 1

この2行が入っているのでページを更新(再表示)するとグラフの値が変化します。

plt.cla()

この1行が入らないと再表示するたびにグラフの色が変わっておもしろいです。

 

JavaScriptのChart.jsを使ってグラフを表示

次にhtmlで直接グラフを表示してみます。こちらがコードです。

test.html

<div class="chart-container" style="position: relative; width: 100%; height: 100%;">
  <canvas id="myChart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js">
</script>
<script>
    var ctx = document.getElementById("myChart").getContext('2d');
    var myBarChart = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: ['1', '2', '6', '4', '5', '6'],
            datasets: [{
                label: 'sample',
                data: [0, 1, 2, 3, 4, 5],
                backgroundColor: "rgba(0,0,255,0.8)"
            }]
        }
    });
</script>

結果はこちらです。

f:id:Start_python:20200126190703p:plain

 

JavaScriptはまったく勉強していないので解説できません。JavaScriptでのグラフの書き方は、検索するといくらでも出てくると思いますので今回は省略させていただきます。
(<html><head><body><script>についてもまったくわからないので勉強します)

 

まとめ

今回はグラフを動かすことは出来なかったですが、表示の方法はなんとなくわかりました。ずっとPythonだけをしてきたのでmatplotlibを使った方法がしっくりきますが、JavaScriptで表示するほうが簡単な気もします。

Flaskを覚えるにはJavaScriptを知らないと難しそうです。Pythonは少し休憩してJavaScriptの勉強をはじめてみたくなりました。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

 

Flask ボタンの大きさや位置を変更する(ボタンでページ移動する)

はじめに

今回から「Flask」の基本を勉強していきます。「html」「css」「javascript」の知識が必要になってきます。
まずは基本的なwebページを作ってみます。次にボタンを作ります。

 

動作環境

Windows10
Python 3.7.5
Flask 1.1.1

作業フォルダ/
├─ templates/
│    └─ index.html
├─ static/
│    ├─ index.css
│    └─ index.js
└─ test.py

htmlファイルを保存するフォルダ名は「templates」でないといけません。また、CSSファイルとJavaScriptファイルを保存するフォルダ名は「static」でないといけません。

 

コード

test.py

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


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

index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <link rel="stylesheet" href="{{url_for('static', filename='index.css')}}">
    <script src="{{url_for('static', filename='index.js')}}"></script>
  </head>
  <body>

    <h1>Hello</h1>
    <h2 id='test'>World</h2>

  </body>
</html>

index.css

h1 {
  color: red;
}

index.js

window.onload = function() {
  var e = document.getElementById('test');

  e.style.color = 'blue';
}

 

解説

「html」「css」「javascript」に関してはまったく勉強してきていないので、今回はなんとなく、htmlでcssファイル名とjavascript名を指定して、cssで文字を赤くしてjavascriptで文字を青くしてるなあ、くらいです。

render_template('index.html')

render_template関数でhtmlファイル名を指定して表示します。

 

test.pyを実行すると最後の行に下記が表示されます。

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

ブラウザに「http://127.0.0.1:5000/」を入力して開きます。

f:id:Start_python:20200124123408p:plain

 

これで基本のwebページはできましたので、次はボタンを作成してみます。

作業フォルダ/
├─ templates/
│    ├─ index.html
│    └─ index2.html
├─ static/
│    ├─ index.css
│    └─ index.js
├─ test.py
└─ test2.py

「test2.py」と「index2.html」を新しく作っていきます。

 

コード

test2.py

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index2.html')


@app.route('/test', methods=['GET', 'POST'])
def test():
    if request.method == 'GET':
        res = request.args.get('get_button')
    elif request.method == 'POST':
        res = render_template('index.html')

    return res


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

index2.html

<!DOCTYPE html>
<html lang="ja">
  <head>
  </head>
  <body>

    <form action="/test" method="get">
      <button name="get_button" value="from get" style="width:200px;height:100px">get ボタン</button>
    </form>

    <div style="margin-left:10%;margin-right:10%;">
      <form action="/test" method="post">
        <button name="post_button" style="width: 100%; padding: 10px;">post ボタン</button>
      </form>
    </div>

  </body>
</html>

 

解説

test2.pyを実行してブラウザ(127.0.0.1:5000)を表示すると2つのボタンが表示されます。「get ボタン」を押すと「from get」の文字が表示されます。「post ボタン」を押すとはじめに作った「index.htmlのページ」が表示されます。GETとPOSTの違いについては、まだ説明できる知識がないので次の機会にします。

res = request.args.get('get_button')

getから値を取得するには、request.args.get関数を使います。(postから値を取得する場合は、request.form関数を使います。)

ボタンの大きさ

「get ボタン」・・今回はhtmlファイルで設定しています。
style="width:200px;height:100px"」この場合は大きさは固定で単位はピクセル数です。

「post ボタン」・・今回はhtmlファイルで設定しています。
<div style="margin-left:10%;margin-right:10%;">」で画面の幅に合わせて左右10%を空白にします。「style="width: 100%; padding: 10px;"」さきほど空白にした範囲以外の幅100%で表示します。「padding: 10px」文字の上下左右すべてに10ピクセルの余白ができます。

 

f:id:Start_python:20200124134609g:plain

 

まとめ

Flaskを使って文字を表示するから始まり、webページを表示、ボタンの作成、ボタンから文字を取得とページを移動するまで出来ました。ここまではホームページを作成している感じです。また今回からPythonよりweb作成(htmlやcss)の勉強が必要になってきました。
次回からはPythonを利用したweb作成をしたいと思いますが、まだまだ勉強することが多いです。

「PythonAnywhere」にも公開できましたのでこちらから動作確認も出来ます。

http://startpython.pythonanywhere.com

(1つしか公開できないので別のものに変わってしまう場合があります)

 

こちらのサイトを参考にさせていただきました。

python.zombie-hunting-club.com

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

 

Python Flaskを使ってweb公開する方法 第2回(PythonAnywhereの使い方)

はじめに

今回は、前回「PythonAnywhere」で作成した「Flask(フラスコ)」のファイルを確認して編集してみます。
そして新しいファイルをアップロードしてみます。今回も「Git」は使いません。

 

ファイルの確認と編集

前回、webアプリ作成のときの最後に表示された
パス(/home/Startpython/mysite/flask_app.py)の場所を見てみます。

「PythonAnywhere」のサイトを開きます。

https://www.pythonanywhere.com/

f:id:Start_python:20200122115510p:plain

「Browse files(ブラウズファイル)」を押します。

f:id:Start_python:20200122115925p:plain

「mysite」フォルダの中にある「flask_app.py」を探します。

「flask_app.py」を開いて編集できます。

f:id:Start_python:20200122120450p:plain

Hello from Flask!」の部分を好きな文字に変更してみます。

f:id:Start_python:20200122120550p:plain

「Save」を押して保存します。(これだけでは表示は変更されません)

f:id:Start_python:20200122121140p:plain

「Save」ボタンの右にある「リロードボタン」を押します。

http://startpython.pythonanywhere.com

にアクセスして変更されていれば成功です!

 

新しいファイルをアップロードする

「hello.py」というファイル名で下記のプログラムを作成します。
(Flask公式のチュートリアルから引用しています)

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!"

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

 

PythonAnywhereに「test」フォルダを新しく作って、

f:id:Start_python:20200122123256p:plain

そこに「hello.py」をアップロードしてみます。

f:id:Start_python:20200122123314p:plain

これだけではまだwebサイトは変更されていません。


※ここからがポイントで今までわからなかったことがすっきりしました。

 

パス名と起動ファイル名を設定する

アップロードが終わったら続けて「webタブ」を開きます。

f:id:Start_python:20200122123636p:plain

「Open Web Tab」を押します。

f:id:Start_python:20200122124032p:plain

真ん中くらいにある「/var/www/startpython_pythonanywhere_com_wsgi.py」を押します。

project_home = u'/home/Startpython/mysite'

「mysite」を「test」に変更して

from flask_app import app as application  # noqa

「flask_app」の部分を「hello」に変更します。

f:id:Start_python:20200122124602p:plain

「Save」を押して保存し「リロードボタン」を押します。

 

サイトを開いてみる

http://startpython.pythonanywhere.com

にアクセスして「Hello World!」と表示されれば成功です!

 

まとめ

今回は「PythonAnywhere」上でプログラムを変更する方法と作成したプログラムをアップロードして起動させる方法を勉強しました。

基本的すぎるのかやり方を探しても難しく解説されているところが多く、調べるのに時間がかかったのでなるべくわかりやすく解説したつもりです。

 

次回から本格的に「Flask」の勉強をしていきます。完成させたプログラムは「Git」を使ってアップロードしたいと思います。

 

Python Flaskを使ってweb公開する方法 第1回(PythonAnywhereの使い方)

はじめに

前回は「Git」でファイルをアップロードしてインターネットで公開(デプロイ)するまでをやりましたが、PythonAnywhereのヘルパーツールが全部自動でやってくれたので実際の設定方法などがわかりませんでした。また「Django」が難しすぎたためまだ理解できなさそうだったので「Flask」の基本から勉強していきます。

まずは「PythonAnywhere」で「Flask(フラスコ)」を作成してみます。今回は「Git」は使いません。

 

PythonAnywhereのアカウント作成

こちらのサイトから「PythonAnywhere」の新規登録をします。

https://www.pythonanywhere.com/

f:id:Start_python:20200116141630p:plain

こちらから無料版でアカウントを作成します。

f:id:Start_python:20200122101712p:plain

ユーザー名、メールアドレス、パスワードを入力します。

f:id:Start_python:20200122102321p:plain

チェックを入れて「Register(登録)」を押します。

 

PythonAnywhereの使い方

ログインした画面です。

f:id:Start_python:20200122102944p:plain

「Open Web Tab」を押します。

f:id:Start_python:20200122103400p:plain

「Add a new web app」を押すと上の画面になるので「Next」を押します。
(「Next」ボタンは少し画面を下げないと見えない場合があります。)

≫Flask」 を選択してPythonのバージョン(例:「Python 3.7(Flask 1.1.1)」)を選択します。

f:id:Start_python:20200122104306p:plain

パス(/home/Startpython/mysite/flask_app.py)が表示されます。
「Next」を押せば完了です。

 

削除の方法

無料版では1つのwebアプリしか作れないため、作り直す場合など一度作成したwebアプリを削除したい場合は「Open Web Tab」から一番下にある削除ボタンを押します。

f:id:Start_python:20200122105041p:plain

 

サイトを開いてみる

http://startpython.pythonanywhere.com

にアクセスして「Hello from Flask!」と表示されれば成功です!

 

まとめ

今回は「PythonAnywhere」で「Flask(フラスコ)」を作成してみました。
わかっている人には当たり前のことかもしれませんが、はじめてweb公開しようと思ったときの疑問点ややり方などを詳しく解説できるように勉強していきたいと思います。

「PythonAnywhere」上でファイルを編集してみて動作確認できれば、作成したプログラムをアップロードする流れで進めていきます。

 

次回は作成されたファイルと中身を詳しくみていきます。

Windowsでインターネット上にWebアプリを公開する方法(「Git」と「PythonAnywhere」を使用)

はじめに

前回はサンプルアプリを起動するところまで進みました。今回はそのサンプルアプリをインターネットで公開(デプロイ)していきます。

ここをご覧いただいている方の多くは、デプロイが初めての方もしくは途中であきらめた方だと思いますので、説明は少なめに最短で進めていきたいと思います。

 

デプロイの方法

前回新規登録した「Git」と「PythonAnywhere」を使っていきます。

1.Gitリポジトリを始める

リポジトリを初期化します。コマンドプロンプトから下記を入力(コピペ)します。

cd djangogirls
cd instant-django
git init
git config --global user.name "Your Name"
git config --global user.email you@example.com

事前準備をします。

メモ帳などで下記の内容のファイルを作ります。
.gitignore」の名前(拡張子なし)で保存します。

*.pyc
*~
/.vscode
__pycache__
myvenv
db.sqlite3
/static
.DS_Store

変更内容を保存します。コマンドプロンプトから下記を入力(コピペ)します。

git add --all .
git commit -m "My Django Girls app, first commit"

 

2.GitHubにコードをプッシュする

https://www.github.com/

にアクセスして、ログインします。

f:id:Start_python:20200117143427p:plain

新しいリポジトリを作成します。

f:id:Start_python:20200117143716p:plain

この画面になった場合は、メールを確認して届いているメールから
「Verify email address(メールアドレスの確認)」を押してください。

新しいリポジトリに「my-first-blog」を入力して
「Create repository(リポジトリを作成する)」を押します。

f:id:Start_python:20200117144240p:plain

リポジトリをクローンするためのURLが表示されます。

自分のコンピューター上のGitリポジトリGitHub上のGitリポジトリに結びつけます。
コマンドプロンプトから下記を入力(コピペ)します。

git remote add origin https://github.com/Start-python/my-first-blog.git
git push -u origin master

※「Start-python」の部分はユーザー名ですので変更してください。
※2回目以降なら一行目を下記に変更してください。
(git remote set-url origin https://github.com/Start-python/my-first-blog.git

認証情報(GitのIDとパスワード)を入力します。

 

3.PythonAnywhereでブログを設定する

https://www.pythonanywhere.com/

にアクセスして、ログインします。

※ここから重要(ここがわからず詰まってしまいました。)

Bash(バッシュ)」ボタンを押します。

f:id:Start_python:20200117145226p:plain

PythonAnywhereのヘルパーツールをインストールします。

※※ ここからコマンドプロンプトではありません。Bash(バッシュ)を押して開いた画面です ※※

Bash(バッシュ)画面から下記を入力(コピペ)します。

pip3.7 install --user pythonanywhere

Pythonのバージョンが違う場合は変更してください。

GitHubからアプリを自動的に構成するためのヘルパーを実行します。

Bash(バッシュ)画面から下記を入力(コピペ)します。

pa_autoconfigure_django.py --python=3.7 https://github.com/Start-python/my-first-blog.git

※「Start-python」の部分はユーザー名ですので変更してください。
※2回目以降なら下記に変更してください。
pa_autoconfigure_django.py --python=3.7 --nuke https://github.com/Start-python/my-first-blog.git

f:id:Start_python:20200117145846p:plain

このような画面が表示されます。

管理者の詳細(ID、パスワード)を設定します。
Bash(バッシュ)画面から下記を入力(コピペ)します。

python manage.py createsuperuser

※※ ここまでコマンドプロンプトではありません。Bash(バッシュ)を押して開いた画面です ※※

 

4.サイトを開いてみる

http://startpython.pythonanywhere.com

にアクセスできれば成功です!管理者IDでログインしてみましょう。

f:id:Start_python:20200117150503p:plain


以上です。お疲れ様でした。

 

まとめ

いかがでしたでしょうか。何度かつまづきながらここまで約3時間かかりました。
今のところ、ブログに登録して掲示板が使えるようになった程度です。「GitHub」も何度かダウンロードではお世話になっていますがまったくわからず使っていました。
本当にわからないことだらけですが、公開してみなさんに使ってもらえるサービスが作れることがわかったのでこれからが楽しみです。
うまく進まなかったなど、ご意見ご感想いただけるとうれしいです。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

 

Windowsでマッチングアプリを作ってみよう(Djangoの使い方、最短でチュートリアルを進める)

はじめに

プログラミング初心者がマッチングアプリ作成に挑戦します。マッチングアプリと言っても恋活アプリではなく趣味を通じた出会いや情報交換ができるアプリを目指します。1つ基本形が完成すればそれを利用していろいろ応用できそうです。

まずは「Django」を使ってマッチングサイトから作り始めてみます。「Django」の使い方を勉強していきましょう。

 

使い方

説明を少なくしてなるべく最短でサンプルが使えるまでを解説します。

1.仮想環境を作る(フォルダを作る)

コマンドプロンプトを起ち上げて下記を入力(コピペ)します。

mkdir djangogirls
cd djangogirls
python3 -m venv myvenv

f:id:Start_python:20200116134657p:plain


2.仮想環境の操作(インストールの準備)

続けてコマンドプロンプトに下記を入力(コピペ)します。

myvenv\Scripts\activate
python -m pip install --upgrade pip

djangogirlsフォルダの中に新規テキスト「requirements.txt」を作ります。

f:id:Start_python:20200116135208p:plain

djangogirls/requirements.txtファイル中に下記のテキストを追加して保存します。

Django~=2.2.4

 

3.Djangoのインストール

コマンドプロンプトに下記を入力(コピペ)します。

pip install -r requirements.txt

 

4.Gitのインストールと新規登録

こちらのサイトから「Git」をダウンロードします。

https://git-scm.com/

f:id:Start_python:20200116140425p:plain

ダウンロードした「Git-2.25.0-64-bit.exe」を実行します。

f:id:Start_python:20200116140743p:plain

こちらのサイトから「Git」の新規登録をします。

https://github.com/

f:id:Start_python:20200116141222p:plain

無料版でオッケーです。

5.PythonAnywhereのアカウント作成

こちらのサイトから「PythonAnywhere」の新規登録をします。

https://www.pythonanywhere.com/

f:id:Start_python:20200116141630p:plain

チェックを入れて「Register(登録)」を押します。

6.PythonAnywhere APIトークンの作成

右上にある「Account」を押します。

f:id:Start_python:20200116142300p:plain

API Token」というタブを選んで、
「Create new API token」のボタンを押します。

f:id:Start_python:20200116142535p:plain

これで準備完了です。おつかれさまでした!

(ここまで約1時間かかりました。)

 

サンプルを使ってみましょう

1.ダウンロード

コマンドプロンプトを再起動して下記を入力(コピペ)します。

cd djangogirls
git clone https://github.com/okoppe8/instant-django.git

テンプレートアプリ(instant-django)がダウンロードできました。

2.初期化する

続けてコマンドプロンプトに下記を入力(コピペ)します。

cd instant-django
python -m venv myvenv
myvenv\Scripts\activate
pip install -r requirements.txt
manage.py migrate
manage.py createsuperuser 

アカウント名/e-mailアドレス/パスワードが要求されます。
e-mailアドレスは任意なのでスキップして大丈夫です。

3.起動する

続けてコマンドプロンプトに下記を入力(コピペ)します。

manage.py runserver

f:id:Start_python:20200116143840p:plain

この状態のままでブラウザを起動して、以下のアドレスを入力します。
http://127.0.0.1:8000/

f:id:Start_python:20200116144038p:plain

こちらがサンプルアプリです。ログアウトやログインもできます。
いろいろ操作してみてください。

4.2回目以降の起動方法

コマンドプロンプトを起ち上げ下記を入力(コピペ)します。

cd djangogirls
cd instant-django
python -m venv myvenv
myvenv\Scripts\activate
manage.py runserver

ブラウザを起動して、以下のアドレスを入力します。
http://127.0.0.1:8000/

 

以上です。お疲れ様でした。

 

まとめ

なるべく簡単にしたつもりですがこれでも複雑ですね。
チュートリアル通りにやりましたが「Git」と「PythonAnywhere」の新規登録は今回は必要なかったかもしれません。(次回で使います)
今後の流れとしては、しばらく「Django」を使ってみて仕組みを勉強しながら将来的には独自のマッチングサイトが作れるようになれればと思います。

 

今回はこちらのサイトを参考にさせていただいております。

qiita.com

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

3Dグラフのアニメーションを作成する(Python matplotlibの使い方)

f:id:Start_python:20200115144233g:plain

はじめに

前回に続き、今度はmatplotlibモジュールを使って三次元グラフを作ってみます。
「from mpl_toolkits.mplot3d import Axes3D」を使用します。
サイコロの出た目を、前回の目をx軸、前々回の目をy軸にしたとき、z軸に偶数なら青色で奇数なら赤色で出た目の合計回数グラフで表します。

 

プログラムのコード

import random
import numpy as np
import matplotlib.pylab as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D

# Qテーブルの初期化
q_table_1 = np.zeros((6, 6))
q_table_2 = np.zeros((6, 6))

# 3Dグラフの準備
fig = plt.figure()

x = [1, 2, 3, 4, 5, 6]
y = [1, 2, 3, 4, 5, 6]
X, Y = np.meshgrid(x, y)

a1 = random.randint(0, 5)
a0 = random.randint(0, 5)

def update(i):
    global a0, a1, a2

    plt.cla()

    a2 = a1
    a1 = a0
    a0 = random.randint(0, 5)

    if a0 == 1 or a0 == 3 or a0 == 5:
        q_table_1[a1, a2] += 1
    if a0 == 0 or a0 == 2 or a0 == 4:
        q_table_2[a1, a2] += 1

    Z1 = q_table_1
    Z2 = q_table_2

    ax = Axes3D(fig)
    
    plt.title('i=' + str(i) + '   [ ' + str(a0+1) + ' ]')

    ax.plot_wireframe(X, Y, Z1, color='b')
    ax.plot_wireframe(X, Y, Z2, color='r')

ani = animation.FuncAnimation(fig, update, interval = 100)
plt.show()

 

解説

y軸用に6×6のQテーブルを2つ作ります。出た目「a0+1」が偶数のときはQテーブル1に、奇数のときはQテーブル2の前回x軸と前々回y軸の位置を+1します。

fig = plt.figure()
ax = Axes3D(fig)

で3次元グラフの準備をします。

ax.plot_wireframe(X, Y, Z)

ワイヤーフレーム表示で3Dグラフを作成します。X、Y、Zはすべて二次元配列です。

ani = animation.FuncAnimation(fig, update, interval = 100)

update関数「def update(i):」を100ミリ秒毎に呼び出して実行します。棒グラフのアニメーションのときと同じです。

 

まとめ

3次元グラフのアニメーションはすごく重いです。50回超えたあたりから動きが鈍くなります。アニメーションなしで1万回試行してみた結果がこちらです。

f:id:Start_python:20200115150904p:plain

1万回くらいでは収束していないのがわかります。

このやり方で株価やFXなどの予想が可能なのか実験してみましたが、おそらく実用レベルにはならなそうです。(ランダムなサイコロで収束すれば、株価の歪みを調べるのはおもしろそうだったのですが。)

棒グラフのアニメーションを作成する(Python matplotlibの使い方)

f:id:Start_python:20200114162359g:plain

はじめに

matplotlibモジュールの基本的な使い方を勉強します。「animation.FuncAnimation」を使ってサイコロの出た目の合計回数を棒グラフで表します。

 

棒グラフの表示だけならこちらです。

f:id:Start_python:20200114163057p:plain

※jupyter notebookでグラフを表示する場合は、「%matplotlib nbagg」を最初に入れます。

 

プログラムのコード

import random
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


fig, ax = plt.subplots()

x = [1, 2, 3, 4, 5, 6]
y = [0,0,0,0,0,0]

def update(i):
    plt.cla()

    rand = random.randint(0, 5)
    y[rand] += 1
    ax.bar(x,y)

    plt.title('i=' + str(i) + '   [ ' + str(rand+1) + ' ]')

    if np.max(y) <100:
        plt.ylim(0, 100)

ani = animation.FuncAnimation(fig, update, interval = 100)
plt.show()

 

解説

ani = animation.FuncAnimation(fig, update, interval = 100)

update関数「def update(i):」を100ミリ秒毎に呼び出して実行します。

plt.cla()

グラフを一度削除します。

rand = random.randint(0, 5)
y[rand] += 1
ax.bar(x,y)

rand番目の「y」に1を足します。棒グラフの定義をします。

plt.title('i=' + str(i) + ' [ ' + str(rand+1) + ' ]')

グラフのタイトル名です。

if np.max(y) <100:
    plt.ylim(0, 100)

最大の「y」が100より小さいときは、y軸は0~100に固定します。
省略(指示しない)の場合はy軸は自動で変わります。

plt.show()

グラフを表示します。

 

まとめ

グラフのアニメーションをやってみようと思ったきっかけは、Q学習もどきを利用して株価予想が可能なのかグラフで可視化したかったためです。

f:id:Start_python:20200115152136g:plain

こちらは過去の株価データからある2つの条件から翌日に株価が上がったときは青色、下がったときは赤色のグラフです。青色と赤色の差が大きいところは予想しやすいことになります。

次回は3Dグラフについて詳しく解説したいと思います。

PythonでGIF編集ソフトが完成しました(動画ファイルを読み込んでGIFへの保存も可能)

はじめに

GIFファイルを開いて編集(再生、カット、トリミング)ができるソフトです。

f:id:Start_python:20200113152439g:plain

保存したGIFファイルがこちらです。

f:id:Start_python:20200114130504g:plain

 

使い方

実行ファイルがあるフォルダにdataフォルダを作ってdataフォルダの中にGIFファイルを用意します。

ソフトを起動するとGIFファイルの一覧が出ます。ファイル名をクリックすると画面が変わってGIFが編集できます。

上のボタン

「<」・・・タイトル一覧に戻ります

保存」・・GIFファイルを保存します(保存ファイル名はtest.gifにしています)

下のボタン

「|」・・・最初(1コマ目)に戻ります

|」・・・1コマ戻ります

「▶」・・・再生します(「」・・・停止します)

「|」・・・1コマ進みます

トリミング」・・トリミングの範囲を指定します

「前カット」・・現在のコマより前を削除します

「後カット」・・現在のコマより後ろを削除します

「元に戻す」・・カットした部分を元に戻します(トリミングは戻りません)

 

まとめ

よく似たフリーソフトはいくつもあると思いますが、細かい部分を手直しできるのでプログラミングの練習もかねて今必要な部分だけ作成してみました。
今回でGIF編集については終わりにしたいと思います。また欲しい機能がでてくれば続きを作るかもしれません。

files = glob.glob('./data/*.gif') を files = glob.glob('./data/*.mp4) に変更して
動画ファイルを読み込めば、動画から一部を切り取ってGIFに変換もできます。

 

少し長いですがプログラムコードを公開します。

プログラムのコード

# フル画面を解除して画面の幅と高さを設定
from kivy.config import Config
Config.set('graphics', 'fullscreen', 0)
Config.set('graphics', 'width', 320)
Config.set('graphics', 'height', 568)
Config.set('graphics', 'resizable', 1)

import os
import glob
import cv2
import numpy as np
from PIL import Image
from moviepy.editor import ImageSequenceClip

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Label
from kivy.uix.button import Button
from kivy.uix.image import Image as Img
from kivy.graphics.texture import Texture
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock
from kivy.graphics import Color
from kivy.graphics import Line
from kivy.uix.popup import Popup

from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.resources import resource_add_path

resource_add_path('c:/Windows/Fonts')
LabelBase.register(DEFAULT_FONT, 'msgothic.ttc')


class CustomLayout(BoxLayout):
    pass


class MainScreen(BoxLayout):
    image_texture = ObjectProperty(None)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.ids.button1.text = ''
        self.ids.button2.text = '>'
        self.ids.button3.text = '前カット'
        self.ids.button4.text = '後カット'
        self.ids.button5.text = '元に戻す'

        self.max_img = 1
        self.lines = []
        self.flg = 0

        files = glob.glob('./data/*.gif')
        for file in files:

            gif = cv2.VideoCapture(file)
            is_success, img = gif.read()

            image = np.array(img)
            image = image[:, :, [2, 1, 0]]    # BGR <-> RGB
            image = Image.fromarray(image)

            texture = Texture.create(size=image.size) 
            texture.blit_buffer(image.tobytes())
            texture.flip_vertical()

            self.ids.sv.add_widget(Img(texture = texture, size_hint_x=None, width=80))
            btn = Button(text=os.path.basename(file), on_release=self.on_command,
                    size_hint_y=None, height=80, color=(0,0,0,1), background_color=(1,1,1,0.1))
            self.ids.sv.add_widget(btn)

    def on_slider(self):
        try:
            image = Image.fromarray(self.images[int(self.ids.slider1.value)-1])

            texture = Texture.create(size=image.size) 
            texture.blit_buffer(image.tobytes())
            texture.flip_vertical()

            self.image_texture = texture
        except:
            pass

    def on_command(self,btn):
        self.ids.label1.text = btn.text
        self.ids.button1.text = '<'
        self.ids.button2.text = '保存'
        self.ids.button8.text = '▶'
        self.ids.button10.text = 'トリミング'

        # 読み込み
        gif = cv2.VideoCapture('./data/' + btn.text)
        self.fps = gif.get(cv2.CAP_PROP_FPS)  # fpsは1秒あたりのコマ数

        # 編集
        self.images = []
        i = 0
        while True:
            is_success, img = gif.read()
            if not is_success:
                self.max_img = i
                break

            self.images.append(img)
            i += 1

        self.images = np.array(self.images)
        self.images = self.images[:, :, :, [2, 1, 0]]    # BGR <-> RGB

        self.ids.slider1.max = self.max_img

        image = Image.fromarray(self.images[0])

        texture = Texture.create(size=image.size) 
        texture.blit_buffer(image.tobytes())
        texture.flip_vertical()

        self.image_texture = texture

        self.ids.slider1.value = self.ids.slider1.min

        self.ids.carousel.load_next()

    def on_btn1(self):
        self.ids.carousel.load_previous()
        self.ids.button1.text = ''
        self.ids.button2.text = '>'
        if self.ids.button8.text == '■':
            self.ids.button8.text = '▶'
            self.event.cancel()

    def on_btn2(self):
        if self.ids.button2.text == '保存':
            content = YesNoPopUp(yes=self.save, no=self.no, text='保存します。よろしいですか?')
            self.popup = Popup(title="ファイルの保存", content=content, size_hint=(0.9, 0.5), auto_dismiss=False)
            self.popup.open()
        
        self.ids.button1.text = '<'
        self.ids.button2.text = '保存'
        self.ids.carousel.load_next()

    def on_btn3(self):
        self.ids.slider1.min = self.ids.slider1.value

    def on_btn4(self):
        self.ids.slider1.max = self.ids.slider1.value

    def on_btn5(self):
        self.ids.slider1.min = 1
        self.ids.slider1.max = self.max_img

    def on_btn6(self):
        self.ids.slider1.value = 1
        self.on_slider()

    def on_btn7(self):
        self.ids.slider1.value -= 1
        self.on_slider()

    def on_btn8(self):
        if self.ids.button8.text == '▶':
            self.ids.button8.text = '■'
            self.event = Clock.schedule_interval(self.update, 1 / self.fps)
        else:
            self.ids.button8.text = '▶'
            self.event.cancel()

    def on_btn9(self):
        self.ids.slider1.value += 1
        self.on_slider()

    def on_btn10(self):
        content = YesNoPopUp(yes=self.yes, no=self.no, text='範囲を指定してください')
        self.popup = Popup(title="トリミング", content=content, size_hint=(0.9, 0.5), auto_dismiss=False)
        self.popup.open()

    def update(self, dt):
        if self.ids.slider1.value >= self.ids.slider1.max:
            self.ids.slider1.value = self.ids.slider1.min
        else:
            self.ids.slider1.value += 1
        self.on_slider()

    def on_image1_down(self, touch):
        if self.flg == 0:
            return

        self.x1 = touch.x
        self.y1 = touch.y
        if len(self.lines)>0:
            for line in self.lines:
                self.ids.image1.canvas.remove(line)
            self.lines = []
        with self.ids.image1.canvas:
            touch.ud['line'] = Line(points=[self.x1, self.y1], close='True')
            self.lines.append(touch.ud['line'])

    def on_image1_move(self, touch):
        if self.flg == 0:
            return

        self.x2 = touch.x
        self.y2 = touch.y
        for line in self.lines:
            self.ids.image1.canvas.remove(line)
        self.lines = []
        with self.ids.image1.canvas:
            Color(0, 0, 0)
            touch.ud['line'] = Line(points=[self.x1, self.y1, self.x2, self.y1,
                                            self.x2, self.y2, self.x1, self.y2],
                                            close='True')
            self.lines.append(touch.ud['line'])
            Color(1, 1, 1)
            touch.ud['line'] = Line(points=[self.x1, self.y1, self.x2, self.y1,
                                            self.x2, self.y2, self.x1, self.y2],
                                            dash_offset=5, dash_length=3,
                                            close='True')
            self.lines.append(touch.ud['line'])

    def on_image1_up(self):
        if self.flg == 0:
            return

        content = YesNoPopUp(yes=self.trimming, no=self.no, text='トリミングします。よろしいですか?')
        self.popup = Popup(title="トリミング", content=content, size_hint=(0.9, 0.5), auto_dismiss=False)
        self.popup.open()

    def yes(self):
        self.popup.dismiss()
        if len(self.lines)>0:
            for line in self.lines:
                self.ids.image1.canvas.remove(line)
            self.lines = []
        self.flg = 1

    def no(self):
        self.popup.dismiss()
        if len(self.lines)>0:
            for line in self.lines:
                self.ids.image1.canvas.remove(line)
            self.lines = []
        self.flg = 0

    def trimming(self):
        self.popup.dismiss()
        if len(self.lines)>0:
            for line in self.lines:
                self.ids.image1.canvas.remove(line)
            self.lines = []
        self.flg = 0

        self.img_x1 = int((self.x1 - (self.ids.image1.pos[0] \
            + ((self.ids.image1.size[0] - self.ids.image1.norm_image_size[0]) / 2))) \
            * (self.ids.image1.texture_size[0] / self.ids.image1.norm_image_size[0]))
        self.img_y1 = int(((self.ids.image1.pos[1] \
            + ((self.ids.image1.size[1] - self.ids.image1.norm_image_size[1]) / 2)) \
            + self.ids.image1.norm_image_size[1] - self.y1) \
            * (self.ids.image1.texture_size[1] / self.ids.image1.norm_image_size[1]))
        self.img_x2 = int((self.x2 - (self.ids.image1.pos[0] \
            + ((self.ids.image1.size[0] - self.ids.image1.norm_image_size[0]) / 2))) \
            * (self.ids.image1.texture_size[0] / self.ids.image1.norm_image_size[0]))
        self.img_y2 = int(((self.ids.image1.pos[1] \
            + ((self.ids.image1.size[1] - self.ids.image1.norm_image_size[1]) / 2)) \
            + self.ids.image1.norm_image_size[1] - self.y2) \
            * (self.ids.image1.texture_size[1] / self.ids.image1.norm_image_size[1]))

        if self.img_x1 < 0:
            self.img_x1 = 0
        if self.img_y1 < 0:
            self.img_y1 = 0
        if self.img_x2 > self.ids.image1.texture_size[0]:
            self.img_x2 = self.ids.image1.texture_size[0]
        if self.img_y2 > self.ids.image1.texture_size[1]:
            self.img_y2 = self.ids.image1.texture_size[1]

        if self.img_x1 < self.img_x2 and self.img_y1 < self.img_y2:
            self.images = self.images[:, self.img_y1:self.img_y2, self.img_x1:self.img_x2]
        if self.img_x1 > self.img_x2 and self.img_y1 < self.img_y2:
            self.images = self.images[:, self.img_y1:self.img_y2, self.img_x2:self.img_x1]
        if self.img_x1 < self.img_x2 and self.img_y1 > self.img_y2:
            self.images = self.images[:, self.img_y2:self.img_y1, self.img_x1:self.img_x2]
        if self.img_x1 > self.img_x2 and self.img_y1 > self.img_y2:
            self.images = self.images[:, self.img_y2:self.img_y1, self.img_x2:self.img_x1]

        self.on_slider()

    def save(self):
        self.popup.dismiss()

        self.images = np.array(self.images)
        self.images = self.images[int(self.ids.slider1.min-1):int(self.ids.slider1.max-1)]
        self.images = list(self.images)

        clip = ImageSequenceClip(self.images, fps=self.fps)
        clip.write_gif('./data/test.gif')


class YesNoPopUp(BoxLayout):
    text = StringProperty()
    yes = ObjectProperty(None)
    no = ObjectProperty(None)


class TestApp(App):
    def build(self):
        self.title = 'テスト'
        return MainScreen()

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

kvファイル(test.kv)

<MainScreen>:
    CustomLayout:
        orientation: "vertical"

        BoxLayout:
            orientation: "vertical"

            BoxLayout:
                Button:
                    id: button1
                    size_hint_x: 0.2
                    text: "<"
                    on_release: root.on_btn1()
                Label:
                    id: label1
                    size_hint_x: 0.6
                    text: "FILE NAME"
                    color: 0, 0, 0, 1
                Button:
                    id: button2
                    size_hint_x: 0.2
                    text: ">"
                    on_release: root.on_btn2()

            Carousel:
                id: carousel
                size_hint_y: 10
                scroll_timeout: 0
                anim_move_duration: 0.1
                ScrollView:
                    GridLayout:
                        id: sv
                        cols: 2
                        size_hint_y: None
                        orientation: "vertical"
                        height: self.minimum_height
                BoxLayout:
                    orientation: "vertical"
                    Image:
                        id: image1
                        size_hint_y: 10
                        texture: root.image_texture
                        on_touch_down: root.on_image1_down(args[1])
                        on_touch_move: root.on_image1_move(args[1])
                        on_touch_up: root.on_image1_up()
                    Slider:
                        canvas.before:
                            Color:
                                rgba: 0, 0, 0, 1
                            Rectangle:
                                pos: self.pos
                                size: self.size
                        id: slider1
                        min: 1
                        value_track: True
                        value_track_color: 1,0,0,1
                        on_touch_up: root.on_slider()
                        on_touch_move: root.on_slider()
                    BoxLayout:
                        Button:
                            background_color: 0,0,0,1
                            text: "|◀"
                            on_release: root.on_btn6()
                        Button:
                            background_color: 0,0,0,1
                            text: "◀|"
                            on_release: root.on_btn7()
                        Button:
                            id: button8
                            background_color: 0,0,0,1
                            font_size: 24
                            text: u"▶"
                            on_release: root.on_btn8()
                        Button:
                            background_color: 0,0,0,1
                            text: "|▶"
                            on_release: root.on_btn9()
                        Button:
                            id: button10
                            background_color: 0,0,0,1
                            text: ""
                            on_release: root.on_btn10()

            BoxLayout:
                Button:
                    id: button3
                    text: "3"
                    on_release: root.on_btn3()
                Button:
                    id: button4
                    text: "4"
                    on_release: root.on_btn4()
                Button:
                    id: button5
                    text: "5"
                    on_release: root.on_btn5()

<CustomLayout>:
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size

<YesNoPopUp>
    BoxLayout:
        #size_hint_y: None
        orientation: 'vertical'

        Label:
            size_hint: 1, 0.8
            text: root.text

        BoxLayout:
            size_hint: 1, 0.2
            orientation: 'horizontal'
            Button:
                size_hint: 0.5, 1
                text: 'OK'
                on_release: root.yes()
            Button:
                size_hint: 0.5, 1
                text: 'Cancel'
                on_release: root.no()

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村

Kivy 範囲選択を行い座標を取り出す(Python 点線の描画)

はじめに

今回は範囲選択を行い座標を取り出します。図形の移動の応用です。

f:id:Start_python:20200111204748g:plain


範囲選択(点線の描画)

def __init__(self, **kwargs):
    super().__init__(**kwargs)

    self.lines = []

def on_image1_down(self, touch):
    self.x1 = touch.x
    self.y1 = touch.y
    if len(self.lines)>0:
        for line in self.lines:
            self.ids.image1.canvas.remove(line)
        self.lines = []
    with self.ids.image1.canvas:
        touch.ud['line'] = Line(points=[self.x1, self.y1], close='True')
        self.lines.append(touch.ud['line'])

def on_image1_move(self, touch):
    self.x2 = touch.x
    self.y2 = touch.y
    for line in self.lines:
        self.ids.image1.canvas.remove(line)
    self.lines = []
    with self.ids.image1.canvas:
        Color(0, 0, 0)
        touch.ud['line'] = Line(points=[self.x1, self.y1, self.x2, self.y1,
                                        self.x2, self.y2, self.x1, self.y2],
                                        close='True')
        self.lines.append(touch.ud['line'])
        Color(1, 1, 1)
        touch.ud['line'] = Line(points=[self.x1, self.y1, self.x2, self.y1,
                                        self.x2, self.y2, self.x1, self.y2],
                                        dash_offset=5, dash_length=3,
                                        close='True')
        self.lines.append(touch.ud['line'])

 

解説

座標 (x1, y1) (x2, y2) の範囲で点線の四角形を描画します。
クリックした座標が (x1, y1) で、ドラッグしている座標が (x2, y2) になります。

for line in self.lines:
    self.ids.image1.canvas.remove(line)

1つ前の点線を削除します。

touch.ud['line'] = Line(points=[self.x1, self.y1, self.x2, self.y1,
                                                    self.x2, self.y2, self.x1, self.y2],
                                                    dash_offset=5, dash_length=3,
                                                    close='True')

4つの点を点線で結んだ四角形を描画します。

 

まとめ

やり方自体はそんなに難しくありませんが、その方法を見つけるまでが難しいです。
あとは画像のトリミングを残すだけになりました。画像が縮小されている場合が多いので、座標の位置が表示されている画像のどの位置になるかの計算がたいへんそうです。

 

次回は画像のトリミングをしたいと思います。(今更ですが、外枠を先に表示して枠をドラッグしてトリミングのほうがよかったでしょうか。。)