Thermal Camera (Thermo AI Device TiD) Python Form


  1. Introduction
  2. Sensor
  3. Sensor case
  4. Raspberry Pi
  5. Python 5.1 Form 5.2 OMRON Non-contact Temperature Sensor D6T-44L-06 Edition 5.3 Pololu ranging sensor VL53L0X edition 5.4 BOSCH Temperature / Humidity / Barometric Pressure Sensor BME280 5.5 Shutdown / Restart Switch 5.6 OpenCV 5.7 Speedup

Python

Introduction

Python is heavily used in MH Software & Services products. The library is abundant, easy to manage, and does not require compilation, so you can program on your smartphone. The website is also written in Python. The Python version is 3.8.6 as of October 25, 2020.

GUI There are many GUI environments that can be used with Python, but I make heavy use of tkinter, which is installed by default.

test_form.py This is a sample that uses the Form class of MH Software & Services. A 320x160 window will appear at position (100,50). test_form.py (compressed with 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='title')


def main():
    root = Root()
    root.mainloop()


if __name__ == '__main__':
    main()

It's a window like this. form.jpg

form.py Form class when using tkinter with MH Software & Services. It incorporates common functions such as mouse events and preparation of UDP transmission / reception function. Also put udp.py in the same folder. form.py (compressed with 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 This is a common class for UDP communication. Monitor communication with thread. udp.py (compressed with 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 port error'.format(self.port)
                    if lang() == 'ja_JP' else 
                        'Failed UDP port'.format(self.port)
                    )()
                msg = (
                    lambda: 
                        'port{}Is used'.format(self.port)
                    if lang() == 'ja_JP' else
                        'Port {} was used.'.format(self.port)
                    )()
            else:
                title = (
                    lambda: 
                        'Other errors'.format(self.port)
                    if lang() == 'ja_JP' else 
                        'Other error'.format(self.port)
                    )()
                msg = (
                    lambda: 
                        'port{}Is used'.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: Thermal Camera (Thermo AI Device TiD) Python Edition web: Thermo AI Device TiD Python Form (URL is subject to change.)

Recommended Posts

Thermal Camera (Thermo AI Device TiD) Python Form
Thermal Camera (Thermo AI Device TiD) Python Acceleration
Thermal camera (Thermo AI device TiD) Python BME280
Thermal Camera (Thermo AI Device TiD) Python OpenCV Edition
Thermal camera (thermo AI device TiD) Python VL53L0X edition
Thermal Camera (Thermo AI Device TiD) Python D6T-44L-06 Edition
Thermal Camera (Thermo AI Device TiD) Introduction
Thermal camera (Thermo AI device TiD) Sensor edition
Thermal Camera (Thermo AI Device TiD) Raspberry Pi Edition