2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonでローカルアプリケーションサーバを作る。

Last updated at Posted at 2021-12-23

Pythonでローカルアプリケーションサーバを作る。

この手の話になるとどうもソリューションが大げさになりがち。
真っ当なサーバを用意したくない・出来ない。フレームワークを覚えるのが面倒だ等々。
手軽に何とかするために仕組みを作ります。

黒い画面が怖がられる場合も多いかと思うのでTkinterのGUIを使えるように調整します。
とはいえ run_forever を停止させる方法がわからんみたいなのは一度は通る道かと思うのでその辺りどうにかします。

httpサーバ

職場によってはセキュリティポリシーなどでHTMLを直接開けない場合などもあり、httpdが解決の糸口(抜け道)になったりしますのでまずはそこから。
ここまで実装すれば普通にPythonのCGIも動作します。(ここでは詳しいことには触れません)

httpd.py

import http.server
import threading
import os
import time

class httpmod(http.server.HTTPServer):
    def run(self):
        try:
            self.serve_forever()
        except keyboardInterrupt:
            pass
        finally:
            self.server_close()

class HTTPd(threading.Thread):
    def __init__(self, threadID):
        threading.Thread.__init__(self)
        self.threadID = threadID

    def run(self):
        hostIP = ""
        addr = (hostIP, 8000)
        handler = http.server.CGIHTTPRequestHandler
        handler.cgi_derectories = ["/"]
        self.server = httpmod(addr, handler)

        self.wst = threading.Thread(target=self.server.run)
        self.wst.daemon = True
        self.wst.start()

    def stop(self):
        self.server.shutdown()
        self.wst.join()

Tkinterに絡めた使い方は次の様な感じ。

httpd_test.pyw

import httpd
import tkinter as tk
import os
import sys

class App:
    def __init__(self, root):
        self.root = root
        self.server = httpd.HTTPd(0)
        self.server.run()
        self.root.protcol("WM_DELETE_WINDOW", self.quit)

    def quit(self):
        self.server.stop()
        self.server = None
        self.root.destroy()

if __name__ == "__main__":
    sys.stdout = sys.stderr = open(os.devnull, "w")
    root = tk.Tk()
    App(root)
    root.mainloop()

このようになります。
カレントディレクトリにindex.htmlなど用意してブラウザで127.0.0.1:8000を開くとブラウズ出来ることを確認します。

webSocketサーバ

あくまでPython主体で何かするならwebSocketが便利なので実装します。
※httpdと同じくwebSocketも止まらないので止めるための仕組みを入れました。

websocket_server は標準モジュールではないのでインストールします。

pip install websocket-server

websocket.py

import websocket_server
import threading
import json

class WebSocket(threading.Thread):
    def __init__(self, ThreadID, host="", port=50001):
        threading.Thread.__init__(self)
        self.threadID = ThreadID
        self.host = host
        self.port = port

    def newClient(self, client, server):
        dat = {"hello": "welcome"}
        self.send(dat)

    def msgReceived(self, client, server, message):
        dat = {"hello": "i'm received (%s)" % message}
        self.send(dat)

    def send(self, data):
        self.wss.send_message_to_all(json.dumps(data))

    def run(self):
        self.wss = websocket_server.WebsocketServer(port=self.port, host=self.host)
        self.wss.set_fn_new_client(self.newClient)
        self.wss.set_fn_message_received(self.msgReceived)

        self.wsst = threading.Thread(target=self.wss.run_forever)
        self.wsst.daemon = True
        self.wsst.start()

    def stop(self):
        self.wss.shutdown()
        self.wsst.join()

データのやり取りにはjsonを使うものとして、sendだけ実装してみました。
httpdとwebsocketを取り込むとこんな感じになります。

httpd_test2.pyw

import httpd
import websocket
import tkinter as tk
import os
import sys

class App:
    def __init__(self, root):
        self.root = root
        self.server = httpd.HTTPd(0)
        self.server.run()
        self.wsserver = websocket.WebSocket(0)
        self.wsserver.run()
        self.root.protocol("WM_DELETE_WINDOW", self.quit)

    def quit(self):
        self.server.stop()
        self.server = None
        self.wsserver.stop()
        self.wsserver = None
        self.root.destroy()

if __name__ == "__main__":
    sys.stdout = sys.stderr = open(os.devnull, "w")
    root = tk.Tk()
    App(root)
    root.mainloop()

javascript側の受け取り部はこんな感じになりますね。

con = new WebSocket("ws://127.0.0.1:50001/ws")

con.onmessage = function(e) {
    dat = JSON.parse(e.data);
    console.log(dat);
}

ブラウザ側からコンソールでcon.send("hello")等するとwebsocket.pyのmsgReceiveが動作して返事が返ってくるのを確認します。

websocket.pyについては目的によってmsgReceivedの処理を変えたい場合などもあると思います、その場合はWebSocketクラスを継承し、msgRecievedをオーバーライドすれば良いです。

とあるひとつの利用ケース

Tkinterがあるのでわざわざこんな仕組みを使わなくても大抵のことは可能ですが、それでもこの仕組みを使った方が良いというものには**「帳票印刷」**なんてものがあります。
Python、印刷系弱いですからね。(時代はペーパーレスですしね)

SVGを利用すれば1つのHTMLだけで印刷フォーマットを作ることが出来、pyAutoGui等を使えば自動的なプリント操作も可能です。
帳票の内容は動的に作成してもいいですし、webSocketで部分的な書き換え指示を投げても良いかと思います。

何かしらのヒントになれば幸いです。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?