Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

VirtualBox上のUbuntu20.04で音声を出力

Linux系のOSで音声出力させたいことがあったのですが、
Raspberry Piやjetsonだとスピーカー接続させるのが面倒だな。。
と思い、VirtualBox上でUbuntuを動かし、そこで音声を出すことにしました。

しかし、そのままの設定では音声が出力されず困ったので
今後のための備忘録として手順をまとめることにしました!!



■Virtual Box上の設定

まずはVirtual Boxの設定に関してです。
こちらは、VirtualBoxマネージャー上で、
設定⇒オーディオを選択し、
 ・オーディオを有効:チェック
 ・ホストオーディオドライバー:Windows DirectSound
 ・オーディオコントローラー:ICH AC97
 ・拡張機能:オーディオ出力を有効化 にチェック
f:id:Elsammit:20211023150555p:plain

これでVirtual Box上の設定は以上になります。

■Ubuntu20.04上の設定

通常であれば、Virtual Boxの設定だけで音声が出力されるはずなのですが、
Ubuntu側の問題で音声が出力できませんでした。。。

どうやらUbuntu側の内部サウンドカードが検出されないバグがあるようで、対応が必要なようです。
そこで、Ubuntu側で必要だった設定についてまとめていきます。

変更するファイルは、

/etc/modprobe.d/alsa-base.conf

と、

/etc/modprobe.d/blacklist.conf

です。
alsa-base.confの末尾に、

options snd-hda-intel dmic_detect=0
options snd-hda-intel model=generic

を追加し、blacklist.confの末尾に、

blacklist snd_soc_skl

を追加すればOKです。

コマンドで、

echo "options snd-hda-intel model=generic" | sudo tee -a /etc/modprobe.d/alsa-base.conf
echo "options snd-hda-intel dmic_detect=0" | sudo tee -a /etc/modprobe.d/alsa-base.conf
echo "blacklist snd_soc_skl" | sudo tee -a /etc/modprobe.d/blacklist.conf

と入力すれば各ファイルの末尾に追記されますので、こちらの方が楽です。

追記が完了したら、再起動を行えば完了です。

■音声出力されるか確認

下記コマンドを実行して、ザー(サー)と音声が出力されればOKです。

speaker-test -c1 -l1

もしくは、wavファイルをあらかじめ用意しておき、

aplay wavファイル

で音声が出力すればOKです。

■最後に

今回はVirtualBox上のUbuntu仮想環境で音声出力させるための方法をまとめました。
こちらの環境で作りたいアプリを作っていきたいと思います。
作成出来たらまた報告します。



windowsのコマンドプロンプトでLinuxコマンドを使いたい!!(alias登録手順)

WindowsLinuxコマンドプロンプトで使えるコマンドって結構違うんですよね。
よく使うコマンドとして、

ls

dir

仕事やプライベートでWindowsLinuxを行ったり来たりすることが多く、いつも間違ったコマンドを打って怒られる日々。。。
特に自分はコマンド入力はLinuxの方が慣れているのでWindowsでlsと打って結構頻繁に怒られる。。。

毎度OSを意識してコマンド切り替えると面倒!!
ということで、慣れているLinuxコマンドをWindowsで実行出来ないか考えたのですが、
aliasを使えばよくね??となり、Windowsでaliasの設定方法を確認しました!!

今回は備忘録も兼ねてブログにまとめたいと思います。



Windowsコマンドプロンプトでalias設定方法

Widowsにはaliasは存在しないようなのですが、マクロ定義が行える、
DOSKEYコマンド
があります。
こちらを用いればマクロ定義したコマンドで変換を掛けることが出来ます。

DOSKEYコマンドを使用したマクロ登録ですが、
doskey マクロ名=実行コマンド
を実行すれば、コマンドを登録できます。

例えば、lsをdirと登録する場合には、

doskey ls=dir

でOKです。
コマンドプロンプトを立ち上げて上記コマンドを入力すれば以降は、
lsと打つとdirが実行されます。

もしマクロを削除したい場合には、

doskey ls=

と入力すればOK。

もし登録したマクロを一覧で確認したい場合には、

doskey /macros

と入力します。

ここでマクロ定義ですが、パイプやリダイレクトは簡単に行えないようです。
例えば、

doskey lf = ls | less

などのような定義は行えないようです。

■マクロファイル作成

doskeyでマクロが定義出来ることは分かりました。
ただ、doskeyのマクロ定義はコマンドプロンプトを再立ち上げすると消えてしまいます。。。

そこでマクロ定義用のファイルを作成し、
コマンドプロンプト立ち上げ時に定義したマクロファイルを読み出してもらうようにします。

まずはマクロ定義用のファイルを作成します。
といってもテキストファイルにマクロの一覧を作成していけばOKです。
例えば、macros.txtというファイルを新規に作成し、

ls=dir $*
l=dir $*
ll=dir $*
chdriv=D:

などのようにテキストファイルにマクロを羅列すればOK。

コマンドプロンプト内で定義したマクロファイルを読み出して一括定義するには、

doskey /macrofile=macros.txtのパス

でOKです。

コマンドプロンプトを開いたときにマクロファイル読み込み

最後に、コマンドプロンプトを開いたときにマクロファイルを読み出して定義する方法です。
まず、コマンドプロンプトのプロパティを開きます。
もし分からなければ、、、
検索窓でcmdと入力し、
ヒットしたコマンドプロンプトアイコン上で右クリックし、
ファイルの場所を開く。
コマンドプロンプトが格納されたパスまで行くことが出来るので、
そこで、コマンドプロンプトを右クリック⇒プロパティを選択すればOK。

後は、リンク先(T:)に先ほどのコマンドを記載すればOK。
ただし、
すでに、

%windir%\system32\cmd.exe e

といった文言が入っているので、
/kオプションを用いて、

%windir%\system32\cmd.exe e /k doskey /macrofile=macros.txtのパス

と連結させればOKです。

これで、コマンドプロンプトを何回開いてもマクロ定義したコマンドを用いることが出来るようになります。

■最後に

今回はWindowsでaliasのようにコマンドをマクロ定義し、Linuxコマンドでも同じような動作をさせる方法を記載してみました。
一回登録してしまえばOKなのでぜひ使ってみてください。

一回登録すれば済むので絶対後で忘れるだろうな。
このブログはいつか自分で見ることになるだろう。

initramfs再作成手順

今回は少し毛色を変えて、Linuxのinitramfsの再構築手順についてまとめたいと思います。
ちょっとinitrdを変更する機会があったのですが、久しぶりに触ることになったので忘れてしまっていたんですよね。。
また一から調べ直しているような状況だったので、今後は困らないように。と思いまとめることにしました!!



■環境

今回用いる環境は、
jetson nanoになります。

ただ、initramfs展開後のフォルダ構成やinitファイルに記載されたスクリプトが異なるだけで、
Linux全般に使用できる手順かと思います。
自分の環境に合わせて読み替えてみてください。

■initrdの展開手順

まずは編集するためにinitramfsを展開していきます。

前準備として、下記コマンドで作業用フォルダの作成を行って下さい。

mkdir hoge
cd hoge
mkdir huga

そして、

展開のコマンドはこちらになります。

unmkinitramfs /boot/initrd ./huga

or

unmkinitramfs /boot/initrd.img-`uname -r` ./huga

お使いのinitramfsの名前に応じて使い分けてみてください。
展開処理にほんの少し時間がかかります。
完了するとhugaフォルダに、

bin  dev  etc  init  lib  mnt  proc  sbin  sys  tmp  usr  var

といったようなinitrd展開後のフォルダ群が格納されているかと思います。

また、こちらのコマンドでも展開が可能です。

cd huga
zcat /boot/initrd | cpio -id

or

cd huga
zcat /boot/initrd.img-`uname -r` | cpio -id

これで展開は完了です。

■initramfs再作成手順

では次に再作成方法です。
展開時に使用したunmkinitramfsと対をなす、
mkinitramfs というコマンドがあり、こちらを利用すれば作成は出来そうでした。
そこで、

mkinitramfs -o 展開ファイル先 huga 

を実行してみたのですが、

depmod: ERROR: Bad version passed huga

といったエラーが出てしまいました。
どうやら、unameを指定しなければならないようだったので、

mkinitramfs -o ./initrd huga `uname -r`

と実行。
無事先ほどのエラーは消えたのですが、、、
元々のinitramfsのサイズが、5MByte程度であったのに対して生成されたinitramfsのサイズは15MByte。。。
明らかにおかしい。。
試しに置き換えてみましたが、、、案の定動作がおかしい。。
原因を調べたのですが泥沼にはまる。。

ということで、、、
mkinitramfsの利用は断念し、先ほどの展開手順で補足的に記載したcpioを用いた方法に方向転換。
cpioを用いたinitramfs再作成コマンドはこちらになります。

cd ./huga
sudo find . | cpio -R 0:0 -o -H newc | gzip > initrd.img

or 

cd ./huga
sudo find . | cpio -R 0:0 -o -H newc | gzip > initrd.img

生成されたinitramfsを置き換えて再起動すれば、編集後のinitramfsを用いることが出来るようになります。

■initramfs内を閲覧するだけなら

initramfs内のフォルダ構成を確認したいだけであればわざわざ展開しなくても、
こちらのコマンドで一覧表示が可能です。

lsinitramfs /boot/initrd

or 

lsinitramfs /boot/initrd-`uname -r`

■(補足)initrdとinitramfsの違い

最後にinitrdとinitramfsの違いについてまとめます。
詳細はこちらに記載されているのですが、
http://archive.linux.or.jp/JF/JFdocs/kernel-docs-2.6/filesystems/ramfs-rootfs-initramfs.txt.html

どうやらかつてはinitrdというgzipで圧縮されたファイルイメージをカーネルドライバに組み込んで実行していたようです。
ただこの方法だと、ファイルシステムを組み込んであげる必要があったり、initrd実行後、RAMディスクをアンマウントしないと行けなかったり、
と手間が取り扱いが面倒でした。

そこで、登場したのがinitramfs。
initramfsはcpioとgzipで圧縮されたアーカイブになったファイルシステムイメージであり取り扱いがシンプルになっており、
メモリ上にファイルシステムを作っておき、そこに先ほどのイメージを展開するだけなので手順も簡素化されております。

■最後に

今回はinitramfsの作成方法について触れてみました。
久しぶりにLinux OSに近い部分を触ったので忘れている点が多かった。。
たまには触らなきゃダメですね。



javascriptでtableタグ内にinputタグを追加する

今回はjavascriptでList内にbuttonなどのinputタグを追加する方法についてまとめたいと思います。

htmlで記載されたタグを操作する際には、
documentオブジェクトを使用するかと思います。
自分もよく表示内容を切り替えたり属性(Attribute)を切り替えたりするのに使用するのですが、
ある日作業をしている最中に、
 documentオブジェクトを使用して別タグを追加するにはどうすればいいのだろう??
と躓いてしまいました。

今回は今後のために備忘録として残しておきたいと思います。



■(おさらい)documentオブジェクトの基本操作

先ほど冒頭で述べた通り、documentオブジェクトを利用していきます。
ここで一旦documentオブジェクトの使用方法について簡単にまとめておきます。
コードの内容は、htmlで

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="script.js"></script>
    <title>table Test</title>
</head>
    <body>
        <table>
            <tbody id="InsertTable"></tbody>
        </table>    
        <input type="button" value="Add" onclick="AddColumn()">
    </body>
</html>

とした時のtableの追加をdocumentオブジェクトで実施していきます。
コードはこちら。

function AddColumn(){    
    var table = document.getElementById("InsertTable");
    var tr = document.createElement("tr");
    
    var cell_1 = document.createElement("td");
    var cellText_1 = document.createTextNode("hoge");
    cell_1.appendChild(cellText_1);
    tr.appendChild(cell_1);
    
    var cell_2 = document.createElement("td");
    var cellText_2 = document.createTextNode("huga");
    cell_2.appendChild(cellText_2);
    tr.appendChild(cell_2);

    var cell_3 = document.createElement("td");
    var cellText_3 = document.createTextNode("123");
    cell_3.appendChild(cellText_3);
    tr.appendChild(cell_3);

    table.appendChild(tr);
}

細かい解説はおいておきますが、

var table = document.getElementById("InsertTable");

にてtableのIDであるInsertTableを指定して要素の取得を行います。

そして、

    var tr = document.createElement("tr");
    var cell_1 = document.createElement("td");
    var cellText_1 = document.createTextNode("hoge");
    cell_1.appendChild(cellText_1);
    tr.appendChild(cell_1);

にて、子オブジェクトとしてtrタグ→tdタグを追加し、
tdタグにcreateTextNodeでtable内に書き込む文字列を指定。
これらを3要素分追加を行い、最後に

table.appendChild(tr);

で先ほど指定したtable要素に子オブジェクトを追加して完了です。

■tableタグ内にinputタグを追加する

では、本題のinputタグを追加する方法についてまとめます。
先ほどのコードの3列目にinputタグを追加してみます。

全体のコードはこちら。

function AddColumn(){    
    var table = document.getElementById("InsertTable");
    var tr = document.createElement("tr");
    
    var cell_1 = document.createElement("td");
    var cellText_1 = document.createTextNode("hoge");
    cell_1.appendChild(cellText_1);
    tr.appendChild(cell_1);
    
    var cell_2 = document.createElement("td");
    var cellText_2 = document.createTextNode("huga");
    cell_2.appendChild(cellText_2);
    tr.appendChild(cell_2);

    var cell_3 = document.createElement("td");
    var input_text = document.createElement("input");
    input_text.setAttribute("type", "button");
    input_text.setAttribute("value", "ボタン");
    input_text.setAttribute("onclick", "alert('test')");
    input_text.setAttribute("id", "idNum");
    cell_3.appendChild(input_text);
    tr.appendChild(cell_3);

    table.appendChild(tr);
}

こちらのコードで置き換えた部分は、

    var cell_3 = document.createElement("td");
    var input_text = document.createElement("input");
    input_text.setAttribute("type", "button");
    input_text.setAttribute("value", "ボタン");
    input_text.setAttribute("onclick", "alert('test')");
    input_text.setAttribute("id", "idNum");
    cell_3.appendChild(input_text);
    tr.appendChild(cell_3);

となります。

分かってしまえば何のことはないのですが、
tdタグの子オブジェクトして、

var input_text = document.createElement("input");

でinputタグを新たに生成し、setAttributeで各inputタグのtypeやvalueなどの属性情報を追加し、
appendChildでtdタグの子オブジェクトとして登録を行えばよかったんですよね。

なので、inputタグに限らず他のタグでも
createElement関数で新規生成させ、
appendChild関数で子オブジェクトして登録すればtable内にボタンだろうとテキスト入力だろうと行えるようになります。

■最後に

今回はjavascriptでdocumentオブジェクトを用いてtableタグ内にinputタグなどの別タグを含める方法をまとめました。
もっとWeb系勉強していきたいな。



PyAutoGUIをsshで実行するには

先日よりマウスやキーボード操作を自動化してきました。
その時に用いているのがPyAutoGUIなのですが、、
ラズパイに対して、ssh上でPyAutoGUIで作成したコードを実行したところ、

KeyError: 'DISPLAY'

というエラーが発生し、実行が行えませんでした。

今回は上記を解消し、
ssh上でPyAutoGUIを実行するための方法を備忘録としてまとめたいと思います。



■環境

先ほど記載した通り、
ラズパイに対してwindowsからssh接続してPyAutoGUIのコードを実行したケースになります。

■エラー発生原因

Linux環境変数に、
DISPLAY
があるのですが、
ssh接続したコンソールではこの環境変数が正しく設定出来ておらず、
エラーが出てしまっているようです。

試しにDesktop上、ssh接続したコンソール上のそれぞれで、

echo $DISPLAY

と実行してみたところ、
Desktop上の場合には、

:0.0

と返ってくるのを確認したのですが、ssh上では何も表示されませんでした。

確かにsshでは環境変数DISPLAYに正しい値が入っていないので、
エラーになってしまうのは当然ですね。

■解決方法

環境変数DISPLAYに値が入っていないのが原因なので、
環境変数にDesktop上で設定されている環境変数と同様の値を入れてあげればOK。
コマンドはこちら。

export DISPLAY=:0.0

上記を実行した後にPyAutoGUIを実行すればエラーなく実行させることが出来ました。

■Robotgoはどうか?

PyAutoGUIでエラーになったがgo言語のRobotgoはどうなるのか合わせて確認しました。

当然と言えば当然ですが、
環境変数DISPLAYに値が入っていない場合にはエラーとなってしまいましたが、
環境変数DISPLAYに値を入れれば、実行することが出来ました。

ただしRobotgoでは発生するエラーメッセージが異なり、
こちらのようなメッセージが表示されました。

on_library_load [436]: XOpenDisplay failure!
load_input_helper [1883]: XkbGetKeyboard failed to locate a valid keyboard!
signal: segmentation fault

環境変数DISPLAYとは何か?

LinuxGUIを提供するシステムとして、
X Window System
が用いられます。

X window Systemについてはこちらに詳しく載っておりますので参考にしてください。
www.mtioutput.com

上記記事にも記載されてます通り、
X window Systemはクライアント/ サーバーシステムであり、
サーバーからクライアントへ指示を行う際にどのウィンドウに指示を出すのかを知る必要があります。
この指示を出す先を決めているのが、
環境変数DISPLAY
であり、こちらが正しく設定されていなければサーバーはどのディスプレイを使用するのか分からないのでエラーになってしまう。
というわけです。

■補足

環境変数DISPLAYですが、

export DISPLAY=host名:0.0

といったようにホスト名を指定することも出来ます。
このため、

export DISPLAY=localhost:0.0

といったようなホスト名を指定して使用するディスプレイを切り替えることも出来ます。

と言っても自分の環境だと、

Xlib.error.DisplayConnectionError: Can't connect to display "localhost:0": [Errno 111] Connection refused

という別のエラーが出てしまっているのですが。。

とりあえず、

export DISPLAY=:0.0

でやりたいことは出来ているので、運用はこちらで実施していきたいと思います。
エラー原因の解析は時間を見て実施していきます。

■最後に

今回はssh経由でPyAutoGUI(とRobotgo)のコードを実行するための環境設定についてまとめました。
とりあえず、ホスト名指定した時に発生したエラーの原因はゆっくり調べてみたいと思います。



Robotgoでgo言語で自動化

先日、Robotgoを用いた自動化する方法に関してまとめました。
elsammit-beginnerblg.hatenablog.com

今回はこちらの続き。
画像認識やマウス・キーボード入力イベントの取得方法についてまとめたいと思います。



■マウス・キーボードイベント取得

では早速、マウスやキーボードの入力イベントを取得する方法をまとめます。
こちらはユーザが操作したマウスやキーボード操作を記憶するのに利用できるかと思います。

コードですが、こちらも公式のサンプルコードを少し変更を加えております。

package main

import (
  "fmt"
  hook "github.com/robotn/gohook"
)

func main() {
  low()
}

func low() {
	EvChan := hook.Start()
	defer hook.End()

	for ev := range EvChan {
		fmt.Println("hook: ", ev)
	}
}

こちらを実行すると、マウスを動かすごとに

 Event: {Kind: MouseMove, Button: 0, X: 920, Y: 720, Clicks: 0}

といったようにマウスのカーソル位置がX,Yに格納されています。
さらに、Clicksにはクリック押下状態が格納されます。
Clicksが1であればクリックした状態、0であればnotクリックです。

また、マウスの左クリック/右クリックどちらを押下したかの情報は、
Buttonに格納されます。
Buttonが1の場合には左クリック、2の場合には右クリックを示しているようです。

さらにこちらのコードが実行された状態で、
キーボードを入力してみると、

Event: {Kind: KeyDown, Rawcode: 65, Keychar: 97}

といったログが出力されます。
Keycharに押下したボタンのASCIIコードが格納されます。
このため、こちらを追えばキーボードで何を入力されたのかを判定することが出来ます。

また、特定の入力を検知するためにはこちらのコードで実行可能です。

package main

import (
  "fmt"
  "github.com/go-vgo/robotgo"
)

func main() {
  add()
}

func add() {
  fmt.Println("--- Please press ctrl + shift + q to stop hook ---")
  robotgo.EventHook(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
    fmt.Println("ctrl-shift-q")
    robotgo.EventEnd()
  })

  fmt.Println("--- Please press w---")
  robotgo.EventHook(hook.KeyDown, []string{"w"}, func(e hook.Event) {
    fmt.Println("w")
  })

  s := robotgo.EventStart()
  <-robotgo.EventProcess(s)
}

こちらを実行すると、wのキーを押すとログが出力され、
ctrl + shift + q
で実行から抜けることが出来ます。

■画像認識方法

では次に画像認識です。
PyAutoGuiで言うこところのlocateOnScreenに該当します。
あらかじめ取得した画像と一致した情報が画面上に存在するかチェックしたり、その座標情報を取得するために用います。

こちらは前回と同様、vscode上の新しいファイルを作成のボタンが存在するかをチェックするコードを載せたいと思います。
※下記画像をtest_3.pngという名前で実行ファイルと同階層に置いた場合をサンプルとして載せます。
f:id:Elsammit:20210922224932p:plain

コードはこちら。

package main

import (
	"fmt"
	"github.com/go-vgo/robotgo"
	"image"
	"os"
)

func main() {
	whereBitmap := robotgo.CaptureScreen(0, 0, 500, 500)
	fmt.Println("whereBitmap------ ", whereBitmap)
	file, _ := os.Open("test_3.png")
	img, _, _ := image.Decode(file)

	bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img))
	fx, fy := robotgo.FindBitmap(bit2, whereBitmap, 0.2)
	fmt.Println("FindBitmap------ ", fx, fy)
	
	arr := robotgo.FindEveryBitmap(bit2, whereBitmap, 0.5)
	fmt.Println("Find every bitmap: ", arr)

	fx, fy = robotgo.FindPic("test_3.png", whereBitmap, 0.5)
	fmt.Println("FindPic------ ", fx, fy)
}

画像認識方法に関して、
 ・FindBitmap
 ・FindEveryBitmap
 ・FindPic
3種類載せています。

ToCBitmap関数とFindEveryBitmap関数は引数として画像を一旦ビットマップに変換した後、
画像認識を行うAPIになります。
一方、FindPic関数は引数に画像パスを与えればよいです。

ToCBitmap関数やFindPic関数は画面上で見つかった単一の座標情報を変換しますが、
FindEveryBitmap関数は画面上に存在する複数位置の座標情報を配列で返します。

FindPicのような画像パスを指定して複数座標位置を取得する関数は存在しないように見受けられました。
ですので、複数座標位置を取得したい場合にはFindEveryBitmap関数一択になってしまいそう。
まぁ、自作すればよいのでしょうが。。

こちらの画像認識を行う上で注意点が2点あります。

①画像認識するキャプチャ画像をあらかじめ指定(取得)する必要がある。
ここまで触れてきませんでしたが先ほどの3種類の関数ですがどれも3つの引数を与えております。
これらは、
 ・第1引数:ビットマップ情報などの画像情報
 ・第2引数:画面上で画像認識する画面キャプチャ
 ・第3引数:認識精度
です。

この第2引数として与えるキャプチャ画像を

whereBitmap := robotgo.CaptureScreen(0, 0, 500, 500)

といった関数であらかじめ取得しなければならないのです。
上記コードは縦横どちらも0~500pxの500x500のサイズのキャプチャを取得する関数になります。
こちらを画像認識させる関数に与えないと、画像認識が正しく行えないです。

ですので、必ず前もって認識させる画面のキャプチャは取得してください。

②精度の範囲は0.2 ~ 1
第3引数に与える認識精度ですが、locateOnScreenと同様に1にしてしまうと全く認識しなくなってしまいます。
このため、値を落とす必要があるのですが、0.2を下回ると逆に認識しなくなってしまうようです。
このため、最低でも0.2以上は設定するようにしてあげてください。
※自分はここに躓いてしまいました。。

また結構厳しめに作られているようで、
0.8や0.7でも結構高い一致率でないと反応しないようでした。

■クロスコンパイル方法

最後にUbuntuWindowsへのクロスコンパイル方法についてまとめておきます。

まず必要なライブラリをUbuntuにインストールします。

sudo apt install gcc-multilib
sudo apt install gcc-mingw-w64
sudo apt install libz-mingw-w64-dev

次に下記コマンドを実行すればOKです。
32bit/64bitは当たり前ですが、WIndows側のOSに合わせてください。

・64bit

GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ファイル名

・32bit

GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ go build -x ファイル名

なお、windows側にはmingwが必要になるので注意ください。

■最後に

今回もRobotgoを用いた自動化についてまとめてみました。
pythonを使うか、go言語を使うかは好みになるかな?
自分はPythonで作り始めてしまったのでRobotgoはまたの機会かな。
ただ、クロスコンパイル出来るのは魅力的ですので今後はRobotgoを使うかも。



go言語で自動化してみる

先日よりPythonを用いてキーボードやマウス入力を自動化の方法をまとめてきました。
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com

実際に使って見て便利な点が多いのですが、、、
Python以外にも自動化させる方法はないのか??
と疑問を持ち、他の言語で自動化する方法について調べてみました。

そこで、go言語であれば自動化を行うためのライブラリが存在することを発見!!
試しに使ってみることにしました!!



■ライブラリの紹介

今回見つけたライブラリは、
Robotgo
です。
Robotgoは、
Go言語でのデスクトップオートメーション。マウス、キーボード、ビットマップ、
画像を制御し、画面、プロセス、ウィンドウハンドル、グローバルイベントリスナーを読み取りが行える。
ライブラリです。
対応するOSは、
WindowsMacLinux
のようで、64bit/32bit両方に対応しています。

■環境構築

今回、WindowsLinuxで動かしてみました。
それぞれの環境構築手順をまとめておきたいと思います。
なお、WindowsLinuxの詳細ですが、
WindowsWindows 10
Linux:Ubuntu20.04
を用いてみました。

まず、Robotgoをインストールする前の下準備をまとめておきます。

最初にWindowsの環境構築手順に関してまとめたいと思います。
といってもWIndowの場合、
MinGW-w64
をインストールするのみです。
すでにインストール済みでしたら他の作業は不要です。
インストール手順は下記にまとまっていますのでこちらをご参考にしてください。
といっても、インストーラを用いてインストールしてパスを通すのみなので簡単。
https://www.javadrive.jp/cstart/install/index6.html

後、もしかしたら下記が必要になるかもです。
(自分は必要でした)

go get github.com/lxn/win

次にLinux
今回Ubuntuを用いたのでUbuntuの手順をまとめます。
下記ライブラリをapt-get する。以上です。

sudo apt install gcc libc6-dev
sudo apt install libx11-dev xorg-dev libxtst-dev libpng ++-dev
sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev 
sudo apt install libxkbcommon-dev
sudo apt install xsel xclip

後は、WindowsLinuxどちらも

go get github.com/go-vgo/robotgo

でインストールするのみです。

■マウス操作自動化

では早速、マウス操作の自動化コードを作成してみます。
といっても今回は公式がご紹介しているサンプルを載せておきます。
公式:https://github.com/go-vgo/robotgo

package main

import (
  "github.com/go-vgo/robotgo"
)

func main() {
  robotgo.ScrollMouse(10, "up")
  robotgo.Scroll(100, 200)

  robotgo.MoveMouse(10, 10)
  robotgo.Drag(10, 10)

  robotgo.MouseClick("left", true)
  robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0)
}
  robotgo.ScrollMouse(10, "up")
  robotgo.Scroll(100, 200)

でマウスのスクロールを行い、

robotgo.MoveMouse(10, 10)

でマウスを移動させ、

robotgo.MouseClick("left", true)

でマウスの左クリックを押下する。

■キーボード入力

次はキーボード入力です。
こちらは公式で展開しているサンプルから少し変更を行い、

Hello WorldだんしゃりHi galaxy. こんにちは世界.

という文字を入力後、テキストファイルを保存する処理になります。

func main() {
  robotgo.TypeStr("Hello World")
  robotgo.TypeStr("だんしゃり", 1.0)

  robotgo.TypeStr("Hi galaxy. こんにちは世界.")
  robotgo.Sleep(1)

  robotgo.KeyTap("enter")
  robotgo.KeyTap("s", "ctrl")

  time.Sleep(1 * time.Second)

  robotgo.TypeStr("こんにちは世界.")

  robotgo.KeyTap("enter")

  robotgo.WriteAll("Test")
  text, err := robotgo.ReadAll()
  if err == nil {
    fmt.Println(text)
  }
}
  robotgo.TypeStr("Hello World")
  robotgo.TypeStr("だんしゃり", 1.0)

  robotgo.TypeStr("Hi galaxy. こんにちは世界.")

でキーボードによる文字入力を行い、

  robotgo.KeyTap("enter")
  robotgo.KeyTap("s", "ctrl")

で特定のキーボードをタップする形になります。

PyAutoGuiで言うところの、
pyautogui.write関数が、robotgo.TypeStr関数に該当し、
pyautogui.press関数が、robotgo.KeyTapに該当します。

robotgo.WriteAll("Test")
text, err := robotgo.ReadAll()

こちらは内部メモリへの文字列書き込みと読み出しを行う処理のようです。

■最後に

今回はgo言語で自動化が行える、Robotgoに関する環境構築や簡単なキーボード・マウス操作に関してまとめてみました。
ちょっと長くなってきたので今回はここまでとします。

まだ画像認識やイベント操作などが書けていないので、次回まとめたいと思います。