- 紹介編
- センサ編
- センサケース編
- Raspberry Pi編
- Python編
5.1 Form編
5.2 オムロン 非接触温度センサ D6T-44L-06編
5.3 Pololu 測距センサ VL53L0X編
5.4 BOSCH 温湿度・気圧センサ BME280
5.5 シャットダウン・再起動スイッチ編
5.6 OpenCV編
5.7 高速化編
Python
はじめに
MH ソフトウェア & サービスの製品にはPythonが多用されています。ライブラリが豊富で、管理しやすく、コンパイルが不要の為、スマホでもプログラミングできます。
WebサイトもPythonで書かれています。
Pythonのバージョンは、2020/10/25時点で3.8.6となっています。
GUI
Pythonで使用できるGUI環境は多数ありますが、標準でインストールされるtkinterを多用しています。
test_form.py
MH ソフトウェア&サービスのFormクラスを使用するサンプルです。
320x160のウィンドウが(100,50)の位置に表示されます。
test_form.py (ZIPで圧縮済)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from form import Form
class Root(Form):
def __init__(self):
super().__init__(
#dialog=True,
border=0,
relief=None,
#icon=icon_file,
#minsize=minimum_size,
name='main',
position=['320', '160', '100', '50'],
title='タイトル')
def main():
root = Root()
root.mainloop()
if __name__ == '__main__':
main()
form.py
MH ソフトウェア&サービスでtkinterを使用する際のFormクラスです。
マウスイベント、UDP送受信機能の準備など共通機能を盛り込んでいます。
同じフォルダ内にudp.pyも置いてください。
form.py (ZIPで圧縮済)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import threading
import tkinter as tk
from tkinter import ttk
if __name__ == '__main__':
from udp import UDP
else:
from .udp import UDP
class Form(tk.Tk):
def __init__(self, *args, **kwargs):
"""
Argument:
dialog=<class 'bool'>
icon=full_path
hasmenu=True or False: Change height.
master=object
minsize=(width, height)
name=<class "str">
position=[width, height, left, top]
title=<class "str">
topmost=False
udp_port=4000
udp_bufsize=4096
"""
if kwargs.get('toplevel', False):
# Use for dialog new window.
tk.Toplevel.__init__(self)
self.resizable(0, 0)
self.grab_set()
# Focus to me.
self.focus_set()
# Not show taskbar.
self.transient(self.master)
else:
super().__init__(
#border=0,
#relief=None,
)
if len(kwargs.get('minsize', ())) == 2:
self.minsize(
kwargs.get('minsize')[0],
kwargs.get('minsize')[1])
self.border_style = {'borderwidth': 0, 'relief': 'solid'}
self._scaling = self._get_dpi(self) / 72
self.tk.call('tk', 'scaling', self.scaling)
#self.tk.call('tk', 'scaling', 0.5)
# 96 is development PC's DPI. This value is default.
self._screen_ratio = 96 / self._get_dpi(self)
self.master = kwargs.get('master', None)
self.title(kwargs.get('title', ''))
self.name = kwargs.get('name', '')
self._hasmenu = kwargs.get('hasmenu', False)
if len(kwargs.get('position', [])) == 4:
width = int(kwargs.get('position')[0])
if self.minsize()[0] > width:
width = self.minsize()[0]
height = int(kwargs.get('position')[1])
if self.minsize()[1] > height:
height = self.minsize()[1]
x = kwargs.get('position')[2]
y = kwargs.get('position')[3]
self.position = [width, height, x, y]
if kwargs.get('icon', '') != '':
self.icon = kwargs.get('icon', '')
if kwargs.get('topmost', False):
self.grab_set()
if kwargs.get('dialog', False):
self.resizable(0,0)
self._attach_events()
if kwargs.get('udp_port', '') != '':
self.udp = UDP(self, *args, **kwargs)
self.udp.receive.trace('w', self.udp_receive_changed)
self.resize_event = True
@property
def host(self):
result = socket.gethostbyname_ex(socket.gethostname())
result = result[2][0]
return result
@property
def icon(self):
return self._icon
@icon.setter
def icon(self, value):
self._icon = value
self.tk.call('wm', 'iconphoto', self._w, tk.PhotoImage(file=self._icon))
@property
def position(self):
return [
self.winfo_width(),
self.winfo_height(),
self.winfo_x(),
self.winfo_y()]
@position.setter
def position(self, value):
"""
geometry=[width, height, left, top]
"""
if self._hasmenu:
value[1] += 20
self.geometry(f'{value[0]}x{value[1]}+{value[2]}+{value[3]}')
@property
def resize_event(self):
return self._resize_event
@resize_event.setter
def resize_event(self, value):
self._resize_event = value
if self._resize_event:
self.bind('<Configure>', self._on_resize)
else:
self.unbind('<Configure>')
@property
def scaling(self):
return self._scaling
@property
def screen_ratio(self):
return self._screen_ratio
def _attach_events(self):
self.protocol('WM_DELETE_WINDOW', self.on_exit)
self.bind('<Configure>', self._on_resize)
self.unbind('<Configure>')
self.bind('<MouseWheel>', self._mouse_wheel)
self.bind('<Motion>', self._mouse_move)
self.bind('<ButtonPress-1>', self._button1_press)
self.bind('<ButtonRelease-1>', self._button1_release)
self.bind('<ButtonPress-2>', self._button2_press)
self.bind('<ButtonRelease-2>', self._button2_release)
self.bind('<B2-Motion>', self._button2_move)
self.bind('<ButtonPress-3>', self._button3_press)
self.bind('<ButtonRelease-3>', self._button3_release)
self.bind('<Expose>', self._expose)
def _button1_press(self, *args):
pass
def _button1_release(self, *args):
pass
def _button2(self, *args):
pass
def _button2_move(self, event):
pass
def _button2_press(self, *args):
pass
def _button2_release(self, *args):
pass
def _button3(self, *args):
pass
def _button3_press(self, *args):
pass
def _button3_release(self, *args):
pass
def _expose(self, *args):
pass
def _get_dpi(self, window):
MM_TO_IN = 1/25.4
pxw = window.winfo_screenwidth()
inw = window.winfo_screenmmwidth() * MM_TO_IN
return int(pxw/inw)
def _mouse_move(self, *args):
pass
def _mouse_wheel(self, event):
sf = 1.0
if event.delta < 0:
sf = 0.9
else:
sf = 1.3
pass
def on_exit(self):
self.destroy()
def _on_resize(self, event):
"""
override from child.
This event is hook <"Configure">
If size is not changed, through the function.
"""
#if (self.geometry != self.geometry()):
# self.geometry = self.geometry()
self.on_resize(event)
def on_resize(self, event):
# override from child.
pass
def udp_receive_changed(self, *args):
"""
When UDP use, then override this function .
"""
pass
def main():
root = Form()
#root.wm_attributes("-transparentcolor", "white")
root.mainloop()
if __name__ == '__main__':
main()
udp.py
UDP通信の共通クラスです。
threadで通信を監視します。
udp.py (ZIPで圧縮済)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import socket
import threading
import tkinter as tk
class UDP():
def __call__(self):
return self.receive.get()
def __init__(self, master, *args, **kwargs):
self.binded = False
self.error_message_enable = kwargs.get('error_message_enable', True)
self.error = self.Error(self)
self._port_changing = False
self._port = int(kwargs.get('udp_port', '4000'))
self._bufsize = int(kwargs.get('udp_bufsize', '4096'))
self.receive = tk.StringVar(value='')
self._sock = None
self._start()
def __new__(cls, master, *args, **kwargs):
cls.master = master
if hasattr(cls.master, 'root'):
cls.root = cls.master.root
if hasattr(cls.root, 'ini'):
cls.ini = cls.root.ini
if hasattr(cls.master, "parent"):
cls.parent = cls.master.parent
return super().__new__(cls)
@property
def bufsize(self):
return self._bufsize
@bufsize.setter
def bufsize(self, value):
self._bufsize = value
class Error():
def __init__(self, master):
self.master = master
self.root = self.master.root
self.id = tk.IntVar(value=0)
self.id.trace('w', self._error_changed)
self.message = ''
self.title = ''
def _error_changed(self, *args):
if self.master.error_message_enable:
if self.id.get() > 0:
tk.messagebox.showwarning(self.title, self.message)
def _start(self):
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.settimeout(0.1)
self._socket_bind()
self.thread = threading.Thread(
name='udp{}{}'.format(self.port, self.binded),
target=self._thread,
)
self.thread.setDaemon(True)
self.thread.start()
def _socket_bind(self):
try:
# host is ''.
self._sock.bind(('', self.port))
except Exception as e:
if e.errno == 10048:
title = (
lambda:
'UDPポート異常'.format(self.port)
if lang() == 'ja_JP' else
'Failed UDP port'.format(self.port)
)()
msg = (
lambda:
'ポート {} は使用されています'.format(self.port)
if lang() == 'ja_JP' else
'Port {} was used.'.format(self.port)
)()
else:
title = (
lambda:
'その他のエラー'.format(self.port)
if lang() == 'ja_JP' else
'Other error'.format(self.port)
)()
msg = (
lambda:
'ポート {} は使用されています'.format(self.port)
if lang() == 'ja_JP' else
'Port {} was used.'.format(self.port)
)()
#tk.messagebox.showwarning(title, msg)
self.error.message = msg
self.error.title = title
self.error.id.set(1)
self.binded = False
else:
self.error.message = ''
self.error.title = ''
self.error.id.set(0)
self.binded = True
def _thread(self):
while True:
data, addr = None, None
while self.binded:
try:
if self._port_changing:
break
data, addr = self._sock.recvfrom(self._bufsize)
data = data.decode()
if (data is not None) & (addr is not None):
self.receive.set('{};{};{}'.format(
data, addr[0], addr[1]))
data, addr = None, None
except Exception as e:
#print(e)
pass
if self._port_changing:
self._sock.close()
del(self._sock)
self._sock = None
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.settimeout(0.1)
self._socket_bind()
self.thread.name = 'udp{}{}'.format(self.port, self.binded)
self._port_changing = False
def udp_reset(self):
if type(self._sock) == socket.socket:
self._sock.close()
del(self._sock)
self._sock = None
@property
def port(self):
return self._port
@port.setter
def port(self, value):
value = int(value)
if value != self._port:
self._port = value
self._port_changing = True
def lang():
"""
Sometime os don't has os.environ['LANG'].
This function can not be use...
"""
result = ""
if ('LANG' in os.environ):
result = os.environ.get('LANG')
result = result.split('.')
result = result[0]
if result != 'ja_JP':
result = 'other'
else:
result = 'other'
return result
YouTube: サーマルカメラ(サーモ AI デバイス TiD) Python編
web: サーモ AI デバイス TiD Python Form編 (URLが変更されました)