電子工作でjsからpythonへ値を渡す必要があったので(raspberrypiに立てたWEBサーバーにHTMLを格納し、それをコントローラーとしてjsのイベントが発生すると非同期通信でpythonスクリプトを実行させてサーボモーターやらを動作させたかった)、色々調べているとCGIを使うことでこれが実現可能な事がわかりました。今回はその時のメモになります。
実際にjsからpythonへ値を渡したいケースなんてのが存在するのかよく分かりませんが、誰か1人でもお役に立てたなら幸いです。
プログラムの流れ的には、
ブラウザ(python httpserver)→javascript(Ajax)→pythonscript(cgi)
となります。
OSはwindows、linux、共にpythonをインストールしていればとりあえずどちらも実行できる事を確認しています。
ファイル構成は下図のような感じです。cgiを使うのでcgi-binディレクトリが必須で、その配下にpythonスクリプトを格納します。
プログラム
html/css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>テスト</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="style.css"> </head> <body> <div class="cam"> <img src="http://ローカルIP/?action=stream"/> </div> <main> <ul> <div class="a"> <li id="forward" class="ledoff">前進</li> </div> <div class="bc"> <div class="b"> <li id="left" class="ledoff">左旋回</li> </div> <div class="n"> <li></li> </div> <div class="c"> <li id="right" class="ledoff">右旋回</li> </div> </div> <div class="d"> <li id="backward" class="ledoff">後退</li> </div> </ul> </main> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script src="main.js"></script> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
* { margin: 0px; padding: 0px; } body { max-width: 600px; font-size: 25px; width: 100%; } img { width: 100%; } main { height: 40vh; background: skyblue; } ul { display: block; height: 40vh; list-style: none; padding-top: 10px; } .bc { display: flex; } .a ,.bc, .d { height: 12vh; } li { width: 100px ; height: 90% ; margin-left: auto; margin-right: auto; background: yellow; } .b { margin-left: auto; } .c { margin-right: auto; } ul li { text-align: center; } .a li, .b li, .c li, .d li { border: solid 1px; } .ledon { background: #f88888; } .n li { background: skyblue; } a:active { color: #ff2020; } |
簡単なリモコン画面を作成しました。
続いてjavascript
javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
$(function(){ let motor = "STOP"; // 「前進」ボタンが押されたときのイベント処理 $('#forward').click(function(event) { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('FOWARD'); } else if(motor == "FOWARD") { $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); } }); // 「後退」ボタンが押されたときのイベント処理 $('#backward').click(function(event) { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('BACKWARD'); } else if(motor == "BACKWARD"){ $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); } }); // 「右」ボタンが押されたときのイベント処理 $('#right').click(function(event) { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('RIGHT'); } else if(motor == "RIGHT") { $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); } }); // 「左」ボタンが押されたときのイベント処理 $('#left').click(function(event) { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('LEFT'); } else if(motor == "LEFT") { $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); } }); // 関数:モーターを動かすマクロ呼び出し function change_motor(typee) { motor = typee; if(typee == "FOWARD") { // 前進 //w().callMacro('FW'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'hhhhh' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "BACKWARD") { //w().callMacro('BK'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'iiii' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "RIGHT") { //w().callMacro('RT'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'jjjj' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "LEFT") { //w().callMacro('LT'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'kkkk' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "STOP") { //w().callMacro('ST'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'ssss' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } } }); |
jqueryで、htmlで作成したボタンを押すとイベントが発生するようにしています。
肝心なのがajaxを使っている所で、cgi-binディレクトリのreceive.pyにdataの内容を非同期でpostします。
続いて、postされた値を受け取るためのpythonスクリプトを作成します。
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!C:\\Users\\xxxx\\AppData\\Local\\Programs\\Python\\Python39\\python.exe import cgi form = cgi.FieldStorage() recieve = form.getvalue('name') if recieve == 'hhhhh': print('Content-type: text/html\n') print(recieve) elif recieve == 'iiii': print('Content-type: text/html\n') print(recieve) elif recieve == 'jjjj': print('Content-type: text/html\n') print(recieve) elif recieve == 'kkkk': print('Content-type: text/html\n') print(recieve) elif recieve == 'ssss': #print('Content-type: text/html\n') print(recieve) |
先頭の#!で始まる記述は「シバン」と呼ばれ、これがないとscriptファイルとしてpythonが動作しません。これはpythonインタプリタの格納場所を示しています。つまり、jsのajaxでpostする先のurlにシバンが記述されていれば、自動的に「ファイル名.py」が実効される仕組みです。上のものはwindowsの場合で、linuxですと#!/usr/bin/python3 とかになります。
cgi.FieldStorage() でpostされた値を受け取って、getvalue(‘name’)で、kye(name)に紐づいた値を抜き取っています。
続くif文で、postされた値を照合して、とりあえず今回はpostされた値をそのままjsに返しています。
jsに値を返却する際は、
print()
を使います。先に ’Content-type: text/html\n’ をprintしていますが、これがないとjsに値を返却出来ないみたいです。(header情報に乗せる必要がある)
実行方法
WEBサーバーを立ち上げる必要があります。
flask等を使えばもっと簡単に出来るようですが、今回はpythonで最もお手軽につかえる「python http.server」を使います。
シェルで今回作成したファイルを格納しているディレクトリまで移動して、そこでWEBサーバーを立ち上げます。
コマンドは以下の通り。
1 |
python -m http.server 8000 --cgi |
8000はポート番号になりますので、他で使用していれば他に変える必要があります。
サーバーが立ち上がれば、chromeブラウザからlocal.host(127.0.0.1:8000)にアクセスします。
htmlの操作ボタンをクリックして、chromeのデベロッパーツールのコンソール画面に値が返ってくればOKです。
値が返ってくる=jsからpythonに値の受け渡しが出来ている証拠になります。
一応動画を載せておきます。↓
ハマりどころ
①windowsでは sys.stdin.readline() でjsから文字列を直接受け取る事ができましたが、linuxではなぜか値を受け取れませんでした。前途したcgi.FieldStorage() でpostされた値を受け取って、getvalue()で、kyeに紐づいた値を抜き取れば、winでもlinuxでもOKでした。
②シェルからpython http serverの起動時、「–cgi」が必須。これが抜けるとcgi-binのディレクトリとその配下のpythonスクリプトがあっても動作しません。