元高専生のロボット作り

元高専生のロボット作り

主にプログラミング, 電子系について書きます。たまに機械系もやります。メモ代わりの記事ばっか書きます

pythonで外部プロセスから3Dプリンターを操作する

3Dプリンターを監視できるTwitterのbotを作った - 高専生のロボット作りの続きです。

3Dプリンターの監視までは良いんですけど操作もできなくちゃ、、、ということでタイトルの通りです。


方法としては、使用している3Dプリンター操作用ソフト pronterface また、pronterface内のボタンのウィンドウハンドルを取得し、そこにメッセージを送ることで操作するって感じです。


pronterfaceのウィンドウハンドルの取得にはMicrosoft Visual Studio 2015のspy++を使用します。



pronterfaceはあらかじめ起動しておきます。

visual studio2015を起動したら、ツールからspy++(+)をクリック。

検索→ウィンドウ検索をクリックし、ファインダーツールをpronterface画面にドラッグするとpronterfaceのウィンドウハンドルが表示されます。その状態で
OKをクリックすれば、ウィンドウハンドル一覧からpronterfaceの場所に移ります。⊞ボタンを押していけばpronterfaceの子ハンドル(ボタンとかの)がすべて展開されます。

画像はその時の画面です。
f:id:sgrsn1711:20180608194804p:plain

ここで、例えば'Pause'ボタンのハンドルは0x000C08FCで、pronterfaceの一個下のpanelから13子目にあることが分かります。



このウィンドウハンドルそのもの、または、何個目にあるのか どちらかが分かればよいです。

では、コードです

import ctypes
import array 

WM_GETTEXTLEN = 0x000E
WM_GETTEXT = 0x000D

WM_LBUTTONDOWN = 0x201
WM_LBUTTONUP = 0x202
MK_LBUTTON = 0x0001

class Control3DPrinter:

    def __init__(self):
        p_handle = ctypes.windll.user32.FindWindowW(0, "Pronterface")
        c1_handle = ctypes.windll.user32.FindWindowExW(p_handle, 0, "wxWindowClassNR", "panel")
        c2_handles = array.array("i")
        ENUM_CHILDs = ctypes.WINFUNCTYPE(\
                ctypes.c_int,\
                ctypes.c_int,\
                ctypes.py_object)
        ctypes.windll.user32.EnumChildWindows(\
                c1_handle,\
                ENUM_CHILDs(self.addHandle),\
                ctypes.py_object(c2_handles))
    
        self.PAUSE_handle = c2_handles[12]
        self.OFF_handle = c2_handles[13]
        self.G28_handle = c2_handles[70]

    def addHandle(self, handle, list):
        list.append(handle)
        return 1

    def pushButton(self, button_handle):
        ctypes.windll.user32.SendMessageW(button_handle, WM_LBUTTONDOWN, MK_LBUTTON, 0)
        ctypes.windll.user32.SendMessageW(button_handle, WM_LBUTTONUP)
    
    def G28(self):
        self.pushButton(self.G28_handle)

    def PAUSE(self):
        self.pushButton(self.PAUSE_handle)

    def OFF(self):
        self.pushButton(self.OFF_handle)

if __name__ == "__main__":
    controler = Control3DPrinter()
    controler.G28()

先ほどの何個目にあるかという情報から、コンストラクタ内でボタンのウィンドウハンドルを取得しています。
別にウィンドウハンドルそのものが分かっているので直接書いてもいいんですけどね。

pushButton(button_handle)関数では引数のウィンドウハンドルに対して、
「マウスをクリック押下し、離した」というメッセージを送信しています。

ウィンドウハンドルに対して適切にメッセージを送信すれば、外部プロセスからでも操作することができます。


また、応用すれば3Dプリンターだけでなく、いろいろなソフトも操作できますね。

UPBOXでもできるようにするので、できたらまた記事書きます。