ブログ

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

コンピュータ

C言語からPythonの関数呼び出し4

久しぶりの「C言語からPythonの関数呼び出し」記事として,WinPythonとVisual Studio 2017でPython embeddable用の実行ファイルを作るということを再検討していた.

まず,Python側のコードの例は以下のようにした.
今回もlistを引数としてPythonに渡して,listを返り値としてもらうという例になっている.

# -*- coding: utf-8 -*-

def SampleFunc1(t, f):
    output = [10, 9, 7, 5, 3, 1];
    return output

C(C++)言語側のコードは以下のようにした.

#include <stdio.h>
#include <windows.h>

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

int main()
{
    char Path[MAX_PATH + 1];
    char Dir[MAX_PATH + 1 + 32];
    char drive[MAX_PATH + 1]
        , dir[MAX_PATH + 1]
        , fname[MAX_PATH + 1]
        , ext[MAX_PATH + 1];
    if (0 != GetModuleFileNameA(NULL, Path, MAX_PATH)) {
        _splitpath_s(Path, drive, dir, fname, ext);

        int j = 0;
        for (int i = 0; i < MAX_PATH + 1; i++) {
            Dir[j] = dir[i];
            j++;
            if (dir[i] == '\\') {
                Dir[j] = dir[i];
                j++;
            }
            else if (dir[i] == '\0') {
                break;
            }
        }
        sprintf_s(dir, "%s%s", drive, Dir);
        printf("ディレクトリ パス : %s\n", dir);
    }
    char buf1[2000];
    sprintf_s(buf1, "import sys\r\nsys.path.append('%s')\r\nprint(sys.path)\r\n", dir);
    // 実行ファイルと同じフォルダにあるpyモジュールも使いたいので,前回より少し手を加えた.

    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pArg, *pArg2, *pValue, *pValue2;

    Py_Initialize();
    PyRun_SimpleString(buf1);
    pName = PyUnicode_DecodeFSDefault("SampleFunc");

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

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

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(2); // 関数fncの引数の数を引数にする

            pArg = PyList_New(1000);
            pArg2 = PyList_New(1000);
            for (int i = 0; i < 1000; i++) {
                double value = sin(2.0 * M_PI * 0.01 * 1.0 * i);
                pValue = PyFloat_FromDouble(value);
                pValue2 = PyFloat_FromDouble(2.0);
                if (!pValue) {
                    Py_DECREF(pArgs);
                    Py_DECREF(pArg);
                    Py_DECREF(pArg2);
                    Py_DECREF(pModule);
                    return 1;
                }
                PyList_SetItem(pArg, i, pValue);
                PyList_SetItem(pArg2, i, pValue2);
            }
            if (PyTuple_SetItem(pArgs, 0, pArg2) != 0)
                return 1;
            if (PyTuple_SetItem(pArgs, 1, pArg) != 0)
                return 1;

            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                //printf("%d\n", PyList_CheckExact(pValue));
                Py_ssize_t len = PyList_Size(pValue);

                for (int i = 0; i < len; i++) {
                    //printf("%I32d ", PyFloat_Check(PyList_GetItem(pValue, i)));
                    printf("%.4g\n", PyFloat_AsDouble(PyList_GetItem(pValue, i)));
                }
                //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;
}

プロジェクトのプロパティを次のように設定しておくとF5キーでデバッグがかかるようになる.
(Python部分はデバッグできない) 「ビルド後のイベント」のコマンドライン
copy .\*.py $(TargetDir)

「デバッグ」の「環境」にPython Embeddableのインストールパスを設定する.例えば
PATH="$(Path)C:\python-3.7.2.post1-embed-amd64\;"

なお,[C/C++]の[追加インクルードディレクトリ]にはWinPythonのインストールパスを設定する.例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加のライブラリディレクトリ]には 例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加の依存ファイル]には
python3.lib
を設定する.

 

なお途中にでている「手を加えた」としている部分は次の記事ですこし考え直した.

≫ 続きを読む

2019/02/02 コンピュータ   TakeMe
タグ:Python

letsencryptからcertbotへアップグレードは必要?

letsencryptを使っていたが,"TLS-SNI-01 validation is reaching end-of-life."という内容のメールが届くようになった.
暫く様子見をしていたが,突然使えなくなるのは困るので,一足早くhttps://certbot.eff.org/の指示に出ているクライアントに入れ替えてみた.

"TLS-SNI-01 validation is reaching end-of-life."という内容のメールの中身を確認すると,
一応指示としてはLet's Encryptの専用クライアントを使用している者はバージョンを確認する,アップグレードするなど以下のアドレスを
https://community.letsencrypt.org/t/how-to-stop-using-tls-sni-01-with-certbot/83210
を確認してほしいとメールの本文に書かれていた.

ディストリビューションの標準のリポジトリのクライアントがどのバージョンかわからないので,とりあえず,https://certbot.eff.org/の指示に従ってクライアントを切り替えておいた.

さらに,
sudo sh -c "sed -i.bak -e 's/^\(pref_challs.*\)tls-sni-01\(.*\)/\1http-01\2/g' /etc/letsencrypt/renewal/*; rm -f /etc/letsencrypt/renewal/*.bak"
sudo certbot renew --dry-run
まで実行したところ,
表示錠はhttp-01にしても問題ないようだ.

まだ2週間程度の猶予が与えられているため本当に必要だったのかまだわからない.

≫ 続きを読む

2019/02/02 コンピュータ   TakeMe

Visual C++のプロパティページの設定は構成毎?

今日は,Visual C++のプロパティページでは,構成毎・プラットフォーム毎の設定をするのが面倒だなーと言っている人がいてびっくりした.
というか私も最近知った内容だが...

Visual C++のプロパティページでは,構成毎・プラットフォーム毎の設定ができるが,「すべての構成」「すべてのプラットフォーム」を指定しておくと,構成・プラットフォームで共通の設定を入力できるようになっている.
言われなければ気付かないかもしれない.
最近はRelease/Debug, 32ビット/64ビットの4パターン設定する必要があるが,共通の項目は共通の値を入力できる.

ところで,全く話が変わるが,最近は
#ifdef _WIN32
がきかず
#ifndef _WIN64
が効くことがある...

≫ 続きを読む

2019/02/01 コンピュータ   TakeMe

Python zipimportでpydは読み込めない

前に,「Python embeddable用に追加モジュールのzipファイルを追加」というのを紹介していたが,どうもうまくいくものとうまくいかないものがある.

Python embeddableにはpython**.zipなるファイルが付いている.
なんだこれと思っていたらこれにPythonのモジュールなどのフォルダをそのまま突っ込むと使用できるようになる.
もちろん展開に時間がかかるので多少レスポンスがおちる.

小さなモジュール,Pure Pythonモジュール類は問題なく追加できるが,NumpyやScipyはそれができない.
実はpydの実態がWindows dllであるということ.
Windowsではzipファイルからdllをロードできなくなったのでpydをロードできない.したがって,NumpyやScipyはzipファイルに入れては使用できない(厳密にはpydを使っていない部分だけなら使用できるが,内部でどのように依存しあっているかは外からはわからない).

≫ 続きを読む

2019/01/30 コンピュータ   TakeMe
タグ:Python

Python numpyでFFTを実行する.周波数軸はどうしたら

numpyでFFTを実行する場合,周波数軸はnumpy.linspaceで設定するのが便利である.
よーく見るとずれてしまって困った話.

まず周波数解析の例を示す.周波数軸を作る部分は
np.linspace(0, サンプリング周波数, データ長さ)
で大体あっているのだが,微妙にずれる.
このずれの分が気になったのでよーく考えると以下の例の場合には
freq = np.linspace(0, fsmp - 1/(t_len + t[1] - t[0]), num=len(t));
ということになる.

ピったしの周波数軸が作れる.

import numpy as np
import matplotlib.pyplot as plt

# 例の信号を作成
t = np.linspace(0.001, 4, 4000);
z = 0.1 + 0.2 * np.sin(t * 10 * 2 * np.pi) + 0.2 * np.sin(t * 33 * 2 * np.pi);

# サンプリング周波数
fsmp = 1 / (t[1] - t[0]);
# 解析時間
t_len = t.max() - t.min();

Fz = np.fft.fft(z) / z.shape[0] * 2; # 折り返すのでパワーが2分の1になっている.
Fz[0] = Fz[0] / 2; # 平均成分は折り返さない.
Fz_abs = np.abs(Fz);

freq = np.linspace(0, fsmp, num=len(t));

fname = 'text.csv';
np.savetxt(fname, np.array([freq, np.real(Fz), np.imag(Fz)]).transpose(), fmt='%.18g', delimiter=',');

plt.figure(1)
plt.subplot(311)
plt.plot(t, z)

plt.subplot(312)
plt.plot(freq, Fz_abs)
plt.xlim([0, fsmp/2]);

#136/4
plt.subplot(313)
plt.plot(range(len(t)), Fz_abs)
#plt.xlim([0, 500]); # 後半(高周波側)は前半の折り返し

plt.show();

≫ 続きを読む

2019/01/23 コンピュータ   TakeMe
タグ:Python

Pythonでは複素数の演算が行えることを知った

最近の言語は複素数が使えることが多いが,Pythonも複素数演算が扱える.

例えば以下のような演算ができる.
以下の例では虚部と実部を分けて指定する方法と極座標表示から得る方法の例.

import cmath

# 虚部と実部を分けて指定
a1 = complex(1, 0);
a2 = complex(2, 0);

# 偏角を使った指定
mag1 = 1.0;
arg1 = 0.0;
a1 = complex(mag1 * cmath.cos(arg1), mag1 * cmath.sin(arg1))

mag2 = 2.0;
arg2 = 0.0;
a2 = complex(mag2 * cmath.cos(arg2), mag2 * cmath.sin(arg2))


trial = complex(100, 0);

a21 = a2 - a1;
b = a21 / trial

print(b)

いったん複素数になってしまえば掛け算,割り算は可能.
(例の偏角はラジアンなので注意)

≫ 続きを読む

2019/01/23 コンピュータ   TakeMe
タグ:Python

Python のプログラムはIDLEでもデバッグできる

PythonのGUIとしてWinPythonにはIDLEが添付している.
このIDLEはブレイクポイントなどの設定をどのように行うのか不明でデバッグには使えないと思っていたが,どうもShellの側のメニューバーにDebugという項目があり,Debuggerを選択すればブレイクポイントも設定できるようになるらしい.

IDLEを起動するとPython ShellというWindowが現れる.このとき,ファイルを開く方法は何ら難しいことはないと思う.
ファイルを開くとエディタの中にファイルが開く.
よくIDEを使った開発になれている方であればF5キーでプログラムが実行されるもの推測しやすいと思う.

ただ,ブレイクポイントはどのように設定するのか?

じつは,エディタの側ではなくPython Shellの側のメニューバーのDebug->Debuggerを選択してデバッグをオンにしておかなければならない.

これをオンにしておくとエディタで行を右クリックした時に,Set Breakpoint / Clear Breakpointという選択ができるようになっている.
毎回Visual Studio Codeをインストールしていた.もっと早く教えてよ.

参考
PythonのIDLEでデバッグを行う方法(GAMMSOFT: https://gammasoft.jp/python/debugging-python-idle/)

≫ 続きを読む

2019/01/18 コンピュータ   TakeMe
タグ:Python

SSD(Q-360)でプチフリ?LPM HIPM非対応

2011年製の中古パソコンのハードディスクをSSD Q-360に置き換えて半年.HDDに比べて起動時間が短縮.起動時間など気にならなくなっていた.

年明けになってプチフリとみられる現象が発生するようになった.

理由はよくわかっていないが,設定が原因ではないかと思うようになった.

調べてみるとQ-360はHIPMには対応していないようだ.

そこで,「PCの動作が突然止まってしまう!SSDの謎の不具合「プチフリ」の原因と対策は?」「Windows10のスリープ不安定問題 その1」を参考に設定をいじってみた.
これで設定がうまくいっているのかわからない....

レジストリエディタを使わないと設定できない項目だったのか

 

20190118 追記: ちなみにどうも私の場合にはプチフリーズの原因は違ったらしい.(遅いネットワーク経由でWindows Updateの準備が始まっていたらしい)

≫ 続きを読む

2019/01/12 コンピュータ   TakeMe

WinPython64-3.7.1.0ZeroでSavitzyky-Golayフィルタ

WinPython64-3.7.1.0Zeroを準備して参考ページ「SciPy で Savitzky-Golay フィルタ」を実行できるようにするまで記録する.

1. WinPythonをダウンロードする.

2. ダウンロードしたファイルを実行して適当なフォルダにインストールする.

3. WinPython Command Prompt.exeを実行して,
pip install scipy
を実行する.すると,numpyもインストールされる.
pip install matplotlib
も実行しておく.
4. IDLE (Python GUI).exeを実行して,参考ページからコードをコピーしてくる.
そしてF5キーでこれを実行してみる.
おそらく,
「TypeError: slice indices must be integers or None or have an __index__ method」とエラーになるはず.

5. y1 = signal.savgol_filter(y, n/4+1, 5)などとなっている部分の「/」が問題で「//」に置き換える必要がある.
(y1 = signal.savgol_filter(y, n//4+1, 5)とするということ)

最近のPythonでは「/」が実数を返してしまうということであった.

≫ 続きを読む

2019/01/10 コンピュータ   TakeMe

C#からExcel操作

C#からExcelを操作して計算結果をエクスポートする例を求められた.
 

レイトバインディングならdllへの依存性が減らせるが,プログラムの作成中に面倒なことにオートコンプリートが効かない.Microsoft.Office.Interop.Excelを参照に追加するアーリーバインディングが普通です.

using Excel = Microsoft.Office.Interop.Excel;

これが参考に載っていたやり方.ただし,using Microsoft.Office.Interopでもよいかも.

namespace app
{
    public partial class Form1 : Form
    {
        private Excel.Application objApp;
        private Excel.Workbook objWorkbook;
        private Excel.Worksheet objWorksheet;


        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            objApp = new Excel.Application();
            objWorkbook = objApp.Workbooks.Add();
            objWorksheet = (Excel.Worksheet)objWorkbook.Sheets[1];
            Excel.Range objRange = objWorksheet.Range["A1..B2"];//objApp.get_Range("A1");
            object[,] tbl = new object[2, 2];
            tbl[0, 0] = "TEST";
            tbl[0, 1] = "TEST2";
            tbl[1, 0] = 0;
            tbl[1, 1] = 0.1;
            objRange.Value = tbl;
            objApp.Visible = true;
        }
    }
}

いったんここまで出来上がってからレイトバインディングに直すのが宜しい.
昔は,VBとC#では大きな違いがあったが,最近はC#でもdynamicなる属性が追加されており,InvokeMember関数を最小限にして置き換えができるようになった.

dynamic属性に付け替えExcel.Application()の部分を汎用のType.GetTypeFromProgID()に変更するだけでよい

    public partial class Form1 : Form
    {
        private dynamic objApp;
        private dynamic objWorkbook;
        private dynamic objWorksheet;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Type t = Type.GetTypeFromProgID("Excel.Application");
            objApp = Activator.CreateInstance(t);

            objWorkbook = objApp.Workbooks.Add();
            objWorksheet = objWorkbook.Sheets[1];
            dynamic objRange = objWorksheet.Range["A1..B2"];
            object[,] tbl = new object[2, 2];
            tbl[0, 0] = "TEST";
            tbl[0, 1] = "TEST2";
            tbl[1, 0] = 0;
            tbl[1, 1] = 0.1;
            objRange.Value = tbl;
            objApp.Visible = true;
        }
    }

参考

【Excelの起動から】 C#でExcelを操作する【保存まで】 ( Milk's Memo Note: https://www.milkmemo.com/entry/csharp_excel)

≫ 続きを読む

2019/01/05 コンピュータ   TakeMe