ブログ

割とコンピュータよりの情報をお届けします。

2018年

C言語からPythonの関数呼び出し(WinPython-64bit-3.6.3.0Qt5の場合)

Visual Studio Express 2017 for Windows Desktopで作るアプリ(Visual C++プロジェクトで作るアプリ)からPythonの関数を呼び出すという例を作成していた。ただし,今回はWinPython-64bit-3.6.3.0Qt5の場合である。

Pythonの関数の側

まず,Pythonの関数の側を用意してみました。内容は,引数として文字列を受け取り,文字列を返す関数です。この例では,この関数の記述をしたファイルをtest.pyとして実行ファイルと同じファイルにおくように用意しました。

def fnc(msg):
    msg2 = msg + 'ehe' + msg
    print(msg);
    return msg2

C++/C言語のプログラムの側

Visual Studioで新しいプロジェクトとしてVisual C++のプロジェクトを作成します。C++/C言語のプログラムの側の中身をご紹介します。

#include <Python.h>

int main()
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pArg, *pValue;

    Py_Initialize();
    pName = PyUnicode_DecodeFSDefault("test");

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, "fnc");

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(1); // 関数fncの引数の数を引数にする
            pArg = PyUnicode_FromString("test");
            if (!pArg) {
                Py_DECREF(pArgs);
                Py_DECREF(pModule);
            }
            PyTuple_SetItem(pArgs, 0, pArg); // 2nd argument is the position of the positional argument
            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                printf("return: %s\n", (char*)PyUnicode_DATA(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr, "Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function\n");
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load module\n");
        return 1;
    }
    if (Py_FinalizeEx() < 0) {
        return 120;
    }
    return 0;
}

ビルドの方法

Python.hはWinPythonのインストールディレクトリのpython-3.6.3.amd64\includeにある。 python36.libはE:\WinPython-64bit-3.6.3.0Qt5\python-3.6.3.amd64\libsにある。これらはプロジェクトのプロパティで設定する必要がある。 いまのところDebug構成の場合,リンクの入力にpython36.libを指定していてもpython36_d.lib(デバッグ用)をリンクしようとしてしまうらしいので,うまく使えない。
WinPythonの場合にはデバッグ用のpython36_d.lib/python36_d.dllを作成するにはソースからビルドすることになってしまいかなり面倒である。
したがって,Debug構成とRelease構成を見分けている_DEBUGという定義を一時的にでも消す方法もある。

#ifdef _DEBUG
  #undef _DEBUG
  #include <Python.h>
  #define _DEBUG
#else
  #include <Python.h>
#endif

実行

WinPython Command Prompt.exeでコマンドプロンプトを開き,Visual Studioがわでビルドしてできたファイルをx64\Releaseに見つける。そして上で用意したtest.pyを同じディレクトリにコピーして。
set PYTHONHOME=<WinPythonインストールディレクトリ>\python-3.6.3.amd64
を指定。現在のPython3のWinPython Command PromptではPYTHONHOMEの設定がなされなくなっているこれがないと,ファイルを実行したときに

Fatal Python error: Py_Initialize: unable to load the file system codec
ModuleNotFoundError: No module named 'encodings'

Current thread 0x00003a98 (most recent call first):

というメッセージが表示されてしまう。
それからピルドしたファイルを実行する。
(WinPython Command PromptはPathを適切に設定して起動するコマンドプロンプトなだけで,普通のコマンドプロンプト(CMD)でもset Path=...を適切に設定していれば可)

≫ 続きを読む

2018/09/09 コンピュータ   TakeMe

PythonにはPython embeddableなるzipファイルも配布されている

PythonにはPython embeddableという配布もあるらしい。容量はかなり小さいディストリビューションとしても使用できるし,アプリに組み込んで処理を依頼することもできる。

参考ページには「pythonNN.dll、pythonNN.zip、vcruntime140.dllがあれば」ということになっているが,少なくとも3.7.0については,実際に使う場合にはどのような依存関係が残っているか分かったものではないので,一緒に配布されているファイルを消さない方がよい。
あくまで最小構成で使う場合ということである。書かれている内容を手順通り実行していくとやっぱり183 MB程度になった。

相当大きな容量のファイルになってしまう。
今回使ったファイルをいかに表示する。前の記事のままだとMatplotlibでwxWidgetsを使う場合には修正を加える必要がある(前の記事のまま実行してしまうとTkinterモジュールがないとか)。

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib
#matplotlib.interactive( True )
matplotlib.use('WXAgg')

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure

import matplotlib.pyplot as plt

import wx

N = 1000           # Number of Samples
dt = 0.01          # Sampling Interval
f1, f2 = 23, 36    # Frequency
t = np.arange(0, N * dt, dt) # Time range
freq = np.linspace(0, 1.0 / dt, N) # Frequecny range

# Signal (Sinusoidal waves, frequencies of f1 and f2 + noise)
f = np.sin(2 * np.pi * f1 * t) \
    + np.sin(2 * np.pi * f2 * t) \
    + 0.8 * np.random.randn(N)

F = np.fft.fft(f) / len(f) * 2
F[0] = F[0] / 2.0

# 振幅スペクトルを計算
Amp = np.abs(F)

# plot
plt.figure()
plt.subplots_adjust(wspace=0.4, \
                    hspace=0.0)
plt.subplot(121)
plt.plot(t, f, label='Raw signal')
plt.xlabel("time")
plt.ylabel("signal")
plt.grid()
plt.ylim([-7, 7])
leg = plt.legend(loc=1)

plt.subplot(122)
plt.plot(freq[0:int(len(F)/2)], \
         Amp[0:int(len(F)/2)], \
         label='Amplitude')
plt.xlabel('frequency')
plt.ylabel('amplitude')
plt.grid()
plt.ylim([0, 2])
leg = plt.legend(loc=1)
plt.show()

 

≫ 続きを読む

2018/09/08 コンピュータ   TakeMe

PythonでFFTの例を作成

Python FFTを実施する例を作成してみた。

そのコードは以下のようになった。

フーリエ変換については,係数の部分が定義によって異なるので,この例のnp.fft.fft()の使用方法ではMATLABやGNU Octaveのように配列要素数分係数がかかるので物理的な意味を持つ振幅に直す場合にはlen(f)で割る必要がある。さらに,下の例では,折り返す分を半分しか表示していないので振幅としては半分になってしまう。そこで2をかけて調整している。

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt


N = 1000           # Number of Samples
dt = 0.01          # Sampling Interval
f1, f2 = 23, 36    # Frequency
t = np.arange(0, N * dt, dt) # Time range
freq = np.linspace(0, 1.0 / dt, N) # Frequecny range

# Signal (Sinusoidal waves, frequencies of f1 and f2 + noise)
f = np.sin(2 * np.pi * f1 * t) \
    + np.sin(2 * np.pi * f2 * t) \
    + 0.8 * np.random.randn(N)

F = np.fft.fft(f) / len(f) * 2
F[0] = F[0] / 2.0

# 振幅スペクトルを計算
Amp = np.abs(F)

# plot
plt.figure()
plt.subplots_adjust(wspace=0.4, \
                    hspace=0.0)
plt.subplot(121)
plt.plot(t, f, label='Raw signal')
plt.xlabel("time")
plt.ylabel("signal")
plt.grid()
plt.ylim([-7, 7])
leg = plt.legend(loc=1)

plt.subplot(122)
plt.plot(freq[0:int(len(F)/2)], \
         Amp[0:int(len(F)/2)], \
         label='Amplitude')
plt.xlabel('frequency')
plt.ylabel('amplitude')
plt.grid()
plt.ylim([0, 2])
leg = plt.legend(loc=1)
plt.show()

≫ 続きを読む

2018/09/08 コンピュータ   TakeMe

Pythonで一定間隔で実行3

pip install timeintervalでインストールされるモジュールだと一定間隔の処理実行も可能らしいが,ライセンスに注意とまだ未完成感が半端ない。

一応サンプル的なコードは以下のようになる。

import timeinterval
from time import sleep
from datetime import datetime


def tick_(arg1, arg2):
    print(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))

stopper = timeinterval.start(1000, tick_, 1, 0)

try:
    while True:
        sleep(1.0);
except KeyboardInterrupt:
    print('stop');
    stopper.stop()
 

ところが,一応 一定周期で処理は実行されていそうだが,stopper.stop()はなぜか効かない。

なんと簡単な,と思ったが,あまり使えそうにない。

≫ 続きを読む

2018/09/04 コンピュータ   TakeMe

WinPythonでPyinstallerを使ってみた

WinPython-64bit-3.6.3.0Qt5でpip install pyinstallerでpyinstallerをインストールしてexeファイル化に使ってみた。

まず作ってみたファイルは以下のようなものです。
pythonnetを使って ずーっとピープ音を出していくようなサンプルです。

import clr

from System import * 

for index in range(500, 12000, 500):
    Console.Beep(index, 400)

まずはpyinstallerを使ってみるには,
pyinstaller <コード>.py
を実行してみる。うまくビルドが終わるとdistフォルダに実行ファイルができている。
少しファイル数が多いと思う場合には
pyinstaller <コード>.py --onefile
を実行してみる。コンソールの画面がいらない場合には
pyinstaller <コード>.py --onefile --windowed
を実行してみる。
iconを指定したい場合には
pyinstaller <コード>.py --onefile --windowed --icon=icon.ico

以前のバージョンはファイルサイズが大きくなる傾向が強かったが,結構まとまったファイルができるようになった。

≫ 続きを読む

2018/09/03 コンピュータ   TakeMe

PythonからC#で書いた.NET Frameworkのクラスライブラリを読みだす 2

Cで作ったDLLを呼び出す場合には引数の型などを調べておく必要があり,渡す際も戻しを受け取る場合にも変数型を意識する必要がある。ところが,Python for .NETのDLLの呼び出しの場合にはある程度 自動で型合わせを行ってくれる。

例えばWinPythonについてくるpythonnetで以下のDLLを呼び出してみる。

次のようなコードでTestLibraryName.dllをビルドした。

using System;
using System.Collections.Generic;
using System.Text;

namespace TestLibraryName // dllの名前と同じだと失敗する
{
    public class TestClass
    {
        public string Test(string a)
        {
            return "OUT:" + a;
        }

        public byte[] Test2(byte[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = (byte)(2 * a[i]);
            }
            return a;
        }

        public Int32[] Test3(ref Int32[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = (Int32)(2 * a[i]);
            }
            return a;
        }

        public Int32[] Test4(Int32[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = (Int32)(2 * a[i]);
            }
            return a;
        }
    }
}

Python側に以下のようなコードを用意してみた。

import clr

clr.AddReference('TestLibrary')
from TestLibraryName import TestClass
import System

testClass = TestClass()

# list in Python => .NET array
a = [1, 2, 3]
b = testClass.Test2(a)
for i in range(b.Length):
    print(b[i])

print()

# .NET array => Reference of .NET array
c = System.Array[int](a)
d = testClass.Test3(c)
for i in range(c.Length):
    print(c[i])

print()

# .NET array => .NET array
c = System.Array[int](a)
d = testClass.Test4(c)
for i in range(c.Length):
    print(c[i])

print()

この場合,一番最初の部分でPythonのlistが.NET側でちゃんと配列に直っている。
もう少しサンプルをあさってみたい。

今のところ,C#やVB.NETのdelegateをPythonで作るのがうまくいっていない。.NETにはDelegateクラスが用意されていたと思うので使えるのではないかと思って探している。

≫ 続きを読む

2018/08/30 コンピュータ   TakeMe

PythonからC#で書いた.NET Frameworkのクラスライブラリを読みだす

PythonからC#で書いた.NET Frameworkのクラスライブラリを読み出してみた。
C# でクラスライブラリを書いて,Pythonから使う。

まずは,C#のライブラリの記述内容である。いろいろ試してみると,namespaceの名前の付け方には注意が必要なようです。なお,ビルドは.NET Framework 4.0以上で実行環境にあるものより下のバージョンならよさそう(バージョンを混ぜるとどうなるかは気になるが試していない)。ビルドで出てきたdllをpyファイルのある同じフォルダに移動するとPython for .NETのAddReference()で探せるようになる。

using System;
using System.Collections.Generic;
using System.Text;

namespace TestLibraryName // dllの名前と同じだと失敗する
{
    public class TestClass
    {
        public string Test(string a)
        {
            return "OUT:" + a;
        }
    }
}

次にPythonの側の記述である。

import clr

clr.AddReference('TestLibrary')
from TestLibraryName import TestClass

testClass = TestClass()

print(testClass.Test('test'))

.NETのstring型はうまく文字列に変換が行われるようである。

 

≫ 続きを読む

2018/08/25 コンピュータ   TakeMe

Pythonで一定間隔で実行2

だいぶん前の記事にPythonをつかって一定間隔で処理を実行するというサンプルを書いていた(JavaScriptならsetIntervalに相当する処理をさせたいということだった)。しかし,この時は知らなかったが,Python for .NETを使えば.NET Frameworkの支援を受けて一定間隔処理を記述することも可能であると知った。

Python for .NETを使って一定間隔処理のサンプルは以下のようになる。ちなみにWinPython以外だと入ってないかもしれないのでpip install pythonnetの実行からになると思います。(WinPython 3.7.0.1にはpythonnetがまだ非対応で3.7.0.2から暫定対応している )
最初は,Environment.Versionを調べる例を作ろうとしていたので,最初に残っている。
タイマーtickもPythonで記述してしまう例になっている。

import clr
from time import sleep

clr.AddReference('System')
from System import Environment
from System.Timers import Timer
from System import DateTime
print(Environment.Version.ToString())

running = True

def timer_tick(o, e):
    if running == True:
        print(DateTime.Now.ToString())
    else:
        timer.Stop()

timer = Timer()
timer.Interval = 1000
timer.Elapsed += timer_tick
timer.Start()

try:
    while running == True:
        sleep(1)
except KeyboardInterrupt:
    running = False

ちなみにEnvironment.Versionは4.5から全部4.0.**と表示されるようになってるらしい。(あれ,.NET Framework 4.0? おかしいなと思っても気にしないで)
この例では,clr.AddReference('System')はいらないらしい(Systemは暗黙的にReferenceに含まれるみたい)。

≫ 続きを読む

2018/08/24 コンピュータ   TakeMe
タグ:Python

CefSharp.Wpfの使用例(その後)2

前の記事でCefSharp.Wpfの使用例が出ていたが,デザイナーを使っていなかった。
今回はデザイナーを使う例を掲載する(差分だけ)。

デザイナーの方は,例えば,以下のようになる。Windowタグにxmlns:cefwpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"追加するとコンテンツとしてコントロールを記述できる。(何も言わなくてもわかると思いますが)

<Window x:Class="WPFwebui.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFwebui"
        xmlns:cefwpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Background="White">
    <Grid>
        <cefwpf:ChromiumWebBrowser x:Name="browser" Address="http://www.google.com" Loaded="BrowserLoaded"></cefwpf:ChromiumWebBrowser>
    </Grid>
</Window>

なお前の記事の時はコードビハインドにbrowserを記述していたが,差分となる部分だけをのせると以下のようになるだろう。

    // CefSharp.Wpf.ChromiumWebBrowser browser;

        public MainWindow()
        {
            InitializeComponent();

            //browser = new CefSharp.Wpf.ChromiumWebBrowser();
            // アドレスの取得
            String page = string.Format(@"{0}\html-resources\html\index.html", StartupPath);
            BrowserSettings browserSettings = new BrowserSettings();
            browserSettings.FileAccessFromFileUrls = CefState.Enabled;
            browserSettings.UniversalAccessFromFileUrls = CefState.Enabled;
            browser.BrowserSettings = browserSettings;
            // アドレス設定
            browser.Address = page;
            //Content = browser;
            //browser.Loaded += BrowserLoaded;

以上のようにするとCefSharp.Wpfでデザイナーが使用できる。browserは複数挿入することができる。
タスクマネージャーを見るとCefSharp.BrowserSubprocessが増える。

≫ 続きを読む

2018/08/23 コンピュータ   TakeMe

Pythonでmatplotlibとio.BytesIOの使用例の作成

io.BytesIOを使用すると普通はファイルに書き出す操作を省き仮想的にメモリ上に書き出すことができる。
matplotlibと合わせた使用法の例を作っていた。

まずio.BytesIOは何かというと,C#でいうMemoryStreamのような扱いができるもの。
扱う内容が文字列に限定される場合にはio.StringIOというものも用意されている。
例えば,item = io.BytesIO()で用意しておいて,plt.savefig(item, format='***')でバイト列に書き出す。
さらに,下の例ではImage.openでまるでファイルのようにitemを引数と渡せるようになる。

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
import io

fig = plt.figure()

plt.plot(np.sin(np.linspace(0, 10, num=250)), 'r')
plt.xlim(0, 250)
plt.ylim(-1, 1) 
plt.title('Sinusoidal wave')
item = io.BytesIO()
plt.savefig(item, format='png') #ここで書き出し
item.seek(0)
im = Image.open(item)#さらにメモリ上のファイルのように扱う
im = im.resize((200, 200))
item.close()
im.show()

ファイルを開いてファイルに書き出すように設計されているプログラムで応用ができそう。上の例ではいったん書き出してリサイズしてから表示している。
ファイルへの書き出しは,item.read()をファイルに書き出せばよい。

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
import io

fig = plt.figure()

plt.plot(np.sin(np.linspace(0, 10, num=250)), 'r')
plt.xlim(0, 250)
plt.ylim(-1, 1) 
plt.title('Sinusoidal wave')
item = io.BytesIO()
plt.savefig(item, format='svg') #ここで書き出し
item.seek(0)
with open('test.svg','wb') as out: # Open file as bytes
    out.write(item.read())  

≫ 続きを読む

2018/08/23 コンピュータ   TakeMe