WindowsでBitMEXプログラミング(GUI#3)
週末キタ——(゜∀゜)——!!続き書きます。
昨日早く帰って書きたかったんだけど、目いっぱい呑んだわ~(-_-)
早速行きましょう!
1.マルチスレッド
まず、スレッドを使うために11行目に以下を書いておきます。
using System.Threading;
button_start_Clickを書き換え、pythonを起動する代わりにサブスレッドを開始します。新しく作ったrunSubメソッドがサブスレッドです。この中でpythonを起動します。これでGUIアプリはフリーズすることなく動けます。
// 開始ボタンでtrue、停止ボタンでfalseにします。
// サブスレッドはこれがfalseになったら終了します。
private bool run = false;
// サブスレッドの実体です。
private Thread sub = null;
// 開始ボタンのクリック
private void buttonStart_Click(object sender, EventArgs e)
{
run = true;
// サブスレッドを開始します。
sub = new Thread(new ThreadStart(runSub));
sub.Start();
}
// サブスレッド。この中でpythonを実行します。
private void runSub()
{
// 動かすプログラムはpython.exe、引数にsabachan.pyを指定します。
Process proc = new Process();
proc.StartInfo.FileName = "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\python.exe";
proc.StartInfo.Arguments = "sabachan.py";
// pythonを実行します。
proc.Start();
// 停止ボタンが押されてないか、0.1秒ごとにチェックします。
while (run)
Thread.Sleep(100);
// while文を抜けたということは停止ボタンが押されたということ。
// よって、pythonをkill!
proc.Kill();
}
// 終了ボタンのクリック
private void buttonEnd_Click(object sender, EventArgs e)
{
run = false;
// サブスレッドが完全に終了するのを待ちます。
sub.Join();
}
実行すると相変わらず黒い窓が出てきますが、GUIアプリも触れるようになっています。[開始]と[停止]を交互に押してみてください。
それではいよいよ黒い窓とおさらばしましょう。
2.標準出力
ソリューションエクスプローラーのsabachan.cs上で右クリック、[デザイナー表示]でフォーム画面が出てきます。
ツールボックスの「TextBox」を掴んで、フォーム上で適当に離します。
そしたら赤丸の小っちゃいのをクリック、出てきた小窓でMultiLineをチェックします。
チェックしたらTextBoxをドーンと大きくして、一回実行してみましょう。
ではpythonの標準出力をフックしてこのTextBoxに書き込んでいきます。
2つ注意があります。まず50行目、「-u」を頭に付け足してます。これはpythonに「標準出力をバッファリングするな」と指示しています。これ付けないと標準出力のフックがうまくいきません。
もう1つは62行目の「+=」のところ。これを書くと以下のメッセージが出てきますので、タブを押して次のメッセージで[適用]をクリックします。VisualStudioがフックメソッドを自動で作ってくれます。
フックメソッドですが、「e.Data」に標準出力の内容が入ってきますので、それをTextBoxに書き込みます。「Environment.NewLine」は改行です。
// サブスレッド。この中でpythonを実行します。
private void runSub()
{
// 動かすプログラムはpython.exe、引数にsabachan.pyを指定します。
Process proc = new Process();
proc.StartInfo.FileName =
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\python.exe";
proc.StartInfo.Arguments = "-u sabachan.py";
// 黒い窓を出さない。
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
// 標準出力をフックする。
proc.StartInfo.RedirectStandardOutput = true;
// pythonを実行します。
proc.Start();
// フックメソッドの作成と登録。
proc.OutputDataReceived += Proc_OutputDataReceived;
// フックを開始します。
proc.BeginOutputReadLine();
// 停止ボタンが押されてないか、0.1秒ごとにチェックします。
while (run)
Thread.Sleep(100);
// while文を抜けたということは停止ボタンが押されたということ。
// よって、pythonをkill!
proc.Kill();
}
// フックメソッド。pythonがprintした内容が送られてきます。
private void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
textBox1.AppendText(e.Data + Environment.NewLine);
}
そして実行!ついにキタ——(゜∀゜)——!!
ちなみにこのTextBoxは保存のできないメモ帳みたいなものなので、python実行中であろうと好きに書いたり消したりできます。
さてさて、ここまで書いてる途中で、「GUIアプリからパラメーター渡したいなぁ。そのときどきでロット数とか利確・損切り幅とか変えたいしなぁ。」と思ったので後ほどやります!まだ終わらんよ!
3.パラメーター渡し
それでは参ります!(*'ω'*)
TextBoxを2つ追加して適当に配置します。(2つのパラメーターを渡せれば、あとは3つでも4つでも同じ。)
標準出力フック用の大きなTextBoxの名前がtextBox1、下図のボタンの上に新しく配置したTextBoxの名前は、左からtextBox2、textBox3になっています。
GUIアプリの前に、python側を2つのパラメーターを受け取るようにちょいと修正しておきましょう。
import ccxt
import time
import sys
#BitMEXにアクセスする「物体」
bitmex = ccxt.bitmex({
'apiKey': 'BitMEX-APIの「ID」',
'secret': 'BitMEX-APIの「秘密」',
})
#テストネットを使います
bitmex.urls['api'] = bitmex.urls['test']
args = sys.argv
print('【{0}】第1引数[{1}] 第2引数[{2}] プログラムを開始します'.format(args[0], args[1], args[2]))
while True:
usd = bitmex.fetch_ticker('BTC/USD')['last']
jpy = bitmex.fetch_ticker('BTC/JPY')['last']
print('BTC最終価格は[{0}]ドル、[{1}]円。'.format(usd, jpy))
time.sleep(5)
3行目に「import sys」を追加します。すると「sys.argv」という配列にパラメータが入ってきます。2つ渡すと言ったのに3つ受け取ってますね。これは、パラメーターの先頭には必ず自分自身の名前が入ってくると決まっていて、実質2番目からが本当に渡されたパラメーターになるためです。
VisualStudioに戻り、パラメータを渡すようにちょいと修正します。
// 開始ボタンのクリック
private void buttonStart_Click(object sender, EventArgs e)
{
object[] prms = new object[2];
prms[0] = textBox2.Text;
prms[1] = textBox3.Text;
run = true;
// サブスレッドを開始します。
sub = new Thread(new ParameterizedThreadStart(runSub));
sub.Start(prms);
}
// サブスレッド。この中でpythonを実行します。
private void runSub(object prm)
{
// objectをobject配列に再定義します。
object[] prms = (object[])prm;
// 動かすプログラムはpython.exe、引数にsabachan.pyを指定します。
Process proc = new Process();
proc.StartInfo.FileName =
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\Python36_64\\python.exe";
proc.StartInfo.Arguments = string.Format("-u sabachan.py {0} {1}", prms[0], prms[1]);
// 黒い窓を出さない。
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
// 標準出力をフックする。
proc.StartInfo.RedirectStandardOutput = true;
// pythonを実行します。
proc.Start();
// フックメソッドの作成と登録。
proc.OutputDataReceived += Proc_OutputDataReceived;
// フックを開始します。
proc.BeginOutputReadLine();
// 停止ボタンが押されてないか、0.1秒ごとにチェックします。
while (run)
Thread.Sleep(100);
// while文を抜けたということは停止ボタンが押されたということ。
// よって、pythonをkill!
proc.Kill();
}
objectの配列(2つの要素を持つ)を作り、2つのパラメーターをセットしてサブスレッド開始時に渡します。スレッドにパラメーターを引き渡すには、「ParameterizedThreadStart」というクラスを使う必要があります。
このとき、受け取るrunSubメソッドの引数はobject型と決められています。なので、object配列に再定義してやる必要があります。これをキャスト(型変換)すると言います。
※「型」ですが、例えば文字ならstring型、数値ならint型などありますが、object型は「なんでもいい」という大ざっぱな型です。
それでは実行してみましょう。2つのTextBoxに適当な文字・数字を入力してから開始ボタンをクリックです。
はい、これでpython呼び出しGUIアプリとして最低限の実装はできたかなと思います。GUIプログラミングの基礎の基礎ですから、ある意味ここがスタート地点でもあると思います。
初心者だけどここまで動かせた!という方、ぜひぜひこのプロトタイプに機能を追加してデザインも考えたりして、オリジナルのトレードBOTを作ってみてください(*'ω'*)
それではこれにてGUI編は完結といたします。またいつか!(*'▽')