ツタンラーメンの忘備録

プログラミングや精神疾患、ラーメンについて書いていきます。たぶん。

python line bot imagemap 画像送信

pythonでLINE botを作っていますが,
github.com
ここでだいたいわかります.しかし画像の送信とimagemapはサンプルがないので実装してみます.

APIリファレンス


画像送信

from linebot.models import ImageSendMessage
def make_image_message():
    messages = ImageSendMessage(
        original_content_url="https://hogehoge.jpg", #JPEG 最大画像サイズ:240×240 最大ファイルサイズ:1MB(注意:仕様が変わっていた)
        preview_image_url="https://hogehoge-mini.jpg" #JPEG 最大画像サイズ:1024×1024 最大ファイルサイズ:1MB(注意:仕様が変わっていた)
    )
    return messages

これむっちゃ面白いんですけどプレビューとタップして表示される画像別になるんですよ!
注意:画像サイズの制限はもう少し大きく,jpgに限定されない.ただし.preview_image_urlにgifを指定しても,反映されない(正確にはアニメーションgifは動かない)ので注意

これを

messages = make_image_messages()
line_bot_api.reply_message(
    event.reply_token,
    messages)

的にすればリプライできます.


imagemap

画像をボタン化できます.(画像は用意できれば用意します.)
これの何がいいかというと

  • 画像を見れることによる直感的操作
  • 選択肢を増やせる(最大50)
from linebot.models import ImagemapSendMessage, BaseSize, ImagemapAction, URIImagemapAction, MessageImagemapAction, ImagemapArea
def make_imagemap():
    messages = ImagemapSendMessage(
        base_url='https://huhahaha/pikach/kawaii/',
        alt_text='this is an imagemap',
        base_size=BaseSize(width=1040, height=1040),
        actions=[
            URIImagemapAction(
                link_uri='https://example.com/',
                area=ImagemapArea(
                    x=0, y=0, width=520, height=1040
                )
            ),
            MessageImagemapAction(
                text='hello',
                area=ImagemapArea(
                    x=520, y=0, width=520, height=1040
                )
            )
        ]
    )
    return messages

画像の保存の仕方に一癖あってけっこう悩みました.

https://huhahaha/pikach/kawaii/

以下に
幅:240px、300px、460px、700px、1040px
の画像を配置します.(横幅が指定であれば,高さは関係ない.また高さに合わせて,heightも変える)

  • 画像フォーマット:JPEGまたはPNG
  • ファイルサイズ:最大1MB

なのですが,保存した画像の拡張子は**取ります**
(どっかで拡張子指定できるようにすればいいのに…)

qiita.com

参考にした

python win32client speak

python

import win32com.client as wincl

に関する記事が少なすぎるので,いくつかまとめます.
まず

speak.Speak("Hello World", i)

のiについてです

for n in range(2):
  speak.Speak("Hello World" + str(n), i)
  sleep(1)
  print(n)

で検証します.



  • i = 0,2

これは
読み上げ->1秒待つ->print出力->読み上げ->1秒待つ->print出力となります



  • i = 1

これは
読み上げと同時に一秒待ってprint出力(読み上げの途中で出力される)->出力から1秒後にprintされる.その間読み上げている(ただし,読み上げが重なることはない)



  • i = 3

最初の文章の読み上げを1秒間行う->print出力->二行目を読み上げ(一行目は最後まで読み上げない)->一秒後にprint出力



for n in range(2):
  speak.Speak("Hello World" + str(n), i)
  sleep(1)
  speak2.Speak("Wow!", i)
  print(n)
  • i = 1

Hello World 1->1->2->Hello World 2->Wow->Wow
になります.

どうなっているのかよくわからない.

python 読み上げ テキストファイル 一行ずつ 速度 変える キーイベント

タイトルが長いですが今回の要件を整理します

  • pythonを使う
  • 文字を読み上げる
  • 内容はテキストファイルである
  • キーイベントで一行ずつ読み上げる

って感じです.ちなみに今回はWindows10を使っています.Macではできません.

ではコード

import win32com.client as wincl
import glob
import os
import random

speak = wincl.Dispatch("SAPI.SpVoice")
speak.Rate = -3


def read_files():
    path = "テキストファイルまでのパス"
    f = open(path) 
    lines = f.readlines()
    f.close()

    return lines

def speak_lines():
    lines = read_files()
  speak_line = -1

    while True:
        input_word = input("""
コマンドを選んでください
r->同じ行を繰り返して読む
u->次の行を読み上げる
d->前の行を読み上げる
>""")
        if input_word == "u":
            if speak_line < len(lines) - 1:
                speak_line += 1
                speak.Speak(lines[speak_line])
        elif input_word == "d":
            if speak_line > 1:
                speak_line -= 1
                speak.Speak(lines[speak_line]))
        elif input_word == "r":
            if 0 <= speak_line and speak_line < len(lines) - 1:
                speak.Speak(lines[speak_line])
        elif input_word in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]:
            speak.Rate = int(input_word) - 6

if __name__ == '__main__':
    speak_scripts()

別のこといろいろしていたのでちょっと効率の悪いことしているのは許してください.
数字を入力すると読み上げスピードが変わります.全体的に見たままです.

speak.Speak("読み上げたい文章")
speak.Rate = Long型 -> スピード(ピッチ?)の変更

python-osc 受信 可変長配列

import argparse

from pythonosc import dispatcher
from pythonosc import osc_server

def print1(unused_addr, *p):
    print(p) //タプルで出力される

def osc_loop():
    parser = argparse.ArgumentParser()
    parser.add_argument("--ip",
      default="127.0.0.1", help="The ip to listen on")
    parser.add_argument("--port",
      type=int, default=12346, help="The port to listen on")
    args = parser.parse_args()

    _dispatcher = dispatcher.Dispatcher()
    _dispatcher.map("/read", print1)

    server = osc_server.ThreadingOSCUDPServer(
      (args.ip, args.port), _dispatcher)
    print("Serving on {}".format(server.server_address))
    server.serve_forever()


if __name__ == "__main__":
    osc_loop()

Arduino analog入力 足りない 増やす

意外とまとまっているサイトがなかったのでまとめておく。
Arduinoのアナログ信号はA0~A5の6つしかない。

が、増やす方法がないわけではない。

方法は二つ(初心者なので厳密にはいろいろ違うことを許してほしい)
・ADコンバータを使う
・MUX(マルチプレクサ)を使う

これら。デジタル信号を増やしたいときはIOエキスパンダーとか言うのがある。これも面白いのであとで記載したい。

とりあえず方法を教えろ、原理はあとで勉強するという人向けの記事であることを最初に述べておく。


まずADコンバータ
f:id:hungrykirby:20170826191005j:plain
こんな回路になる。詳しい説明はいつか解説したい。
画像では薄くてわかりづらいが、小さな丸と凹みの部分をちゃんと確認してください(名前の方向は関係ない)

用いたのは

である。
コードは下に記載されている二つの記事のうち上のやつものがそのまま動く

電子部品使い方:マイコンのADC(MCP3208)変換
sites.google.com
参考にした記事

上記記事を見るといろいろ注意書きがあるので、ちゃんと使う人はちゃんと勉強した方がいいと思う。


次にMUX

f:id:hungrykirby:20170911160658j:plain
こんな回路になる

用いたのは

である。
コードは下記ライブラリが古いが使える。

github.com

適当に描いたコードを

#include <Mux.h>

Mux mux;
int counter=0;
void setup(){
  Serial.begin(9600);
  mux.setup(5,4,3,2,A0);
}

void loop(){
  
  int val = mux.read(counter);
  
  if(counter==15){
    Serial.println(val);
    delay(1000);
  }else{
    Serial.print(counter);
    Serial.print(":");
    Serial.print(val);
    Serial.print(", "); 
  }
  
  counter = ++counter & 15;
  
  delay(50);
}

このうち0しか使っていないので、他の値は乱れがち

Flask SQLAlchemy alembicでtable、columnを作成したい。ついでにherokuにデプロイしたい

# sqlalchemy.url = driver://user:pass@localhost/dbname
sqlalchemy.url = sqlite:///test.db

alebmic.initを正しいパスに変更する。
当たり前であるがこれを忘れて死んでいた。

Tutorial — Alembic 0.9.6 documentation

じゃあ、これをデプロイしようとして意外とはまった
floder構成は


+alembic
 env.py
 +versions
  +???.py
app.py
config.py
alembic.ini

みたいな感じ

env.py

import app #app.pyから呼んでくる

#
#~そのまま~
#

def run_migrations_online():
    #下記を加える
    alembic_config = config.get_section(config.config_ini_section)
    alembic_config['sqlalchemy.url'] = app.app.config['SQLALCHEMY_DATABASE_URI']
    connectable = engine_from_config(
        alembic_config,
        prefix='sqlalchemy.',
        poolclass=pool.NullPool)
    #上記を加える
    
    '''
    #既存のコード
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix='sqlalchemy.',
        poolclass=pool.NullPool)
    '''

    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()

stackoverflow.com
フォルダ構成が違うだけでこれが参考になる

python 暗号化 複合化 テキスト 保存

Pythonで暗号化と復号化 - Qiita

これの延長。暗号化した文字列をテキストファイルに保存したいという謎の欲求により生まれたコード

import base64
from Crypto import Random
from Crypto.Cipher import AES

import string, random

class AESCipher(object):
    def __init__(self, key, block_size=32):
        self.bs = block_size
        if len(key) >= len(str(block_size)):
            self.key = key[:block_size]
        else:
            self.key = self._pad(key)

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:]))

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    def _unpad(self, s):
        return s[:-ord(s[len(s)-1:])]

def writeEncrypted(inputtext, filename, key):
    cipher = AESCipher(key)
    encryptedtxt = cipher.encrypt(inputtext)
    f = open(filename, 'w')
    f.write(str(encryptedtxt).split("\'")[1])
    f.close()

def openDecypt(filename, key):
    cipher = AESCipher(key)
    f = open(filename)
    data = f.read()
    f.close()
    print(cipher.decrypt(data.encode('utf-8')))


if __name__ == '__main__':
    key = ''.join([random.choice(string.ascii_letters + string.digits) for i in range(50)])
    print(key)
    writeEncrypted("hogehuga", "hogehuga.txt", key)
    openDecypt("hogehuga.txt", key)

特殊なことはしていないんだけど
byte->strするときに

b'aiueo'

みたくなるので暗号化した部分だけ取りだす