ブログ

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

TakeMe

Plotly.Blazorのサンプル

.NET 8のリリースから半年近く経って、Plotly.Blazorのサンプルが増えていた。

Plotly.Blazorのサンプルが増えていたのでプロットしてみていた。とりあえず.NET6でプロットしたい場合にはサンプルのままで問題ない。
だが、縦横比を1:1にするときはどのようにするかが難しかった。とりあえずnugetパッケージをPlotly.Blazor(4.3.0)をインストール。
.NET 8になって、明示的にApp.razorのRoutesに@rendermodeを指定する必要が出ているようだ。以下Blazor serverで実装していく。
それからplotly.jsを追加していく。

<Routes @rendermode=RenderMode.InteractiveServer />
<script src="_content/Plotly.Blazor/plotly-latest.min.js" type="text/javascript"></script>
<script src="_content/Plotly.Blazor/plotly-interop.js" type="text/javascript"></script>
<script src="_framework/blazor.web.js"></script>

_Imports.razorに以下を追加する。

@using Plotly.Blazor
@using Plotly.Blazor.Traces

例えば、Home.razorに追加してみる。

@page "/"
@inject IJSRuntime JS
<PageTitle>Home</PageTitle>

<PlotlyChart
    @bind-Config="config"
    @bind-Layout="layout"
    @bind-Data="data" 
    @ref="chart"
    style="height: 500px;"/>

@code
{
    PlotlyChart? chart;
    Config config = new Config();
    Layout layout = new Layout();

    // Using of the interface IList is important for the event callback!
    IList<ITrace> data = new List<ITrace>
    {
        new Scatter
        {
            Name = "Sample circle small",
            Mode = Plotly.Blazor.Traces.ScatterLib.ModeFlag.Lines
                | Plotly.Blazor.Traces.ScatterLib.ModeFlag.Markers,
            X = new List<object>(),
            Y = new List<object>()
        },
        new Scatter
        {
            Name = "Sample circle large",
            Mode = Plotly.Blazor.Traces.ScatterLib.ModeFlag.Lines,
            X = new List<object>(),
            Y = new List<object>(),
            Line = new Plotly.Blazor.Traces.ScatterLib.Line() {
                Width = 1.85M,
                Dash = "3 12 5",
            },
        },
        new Scatter
        {
            Name = "Sample circle",
            Mode = Plotly.Blazor.Traces.ScatterLib.ModeFlag.Lines,
            X = new List<object>(),
            Y = new List<object>(),
            Line = new Plotly.Blazor.Traces.ScatterLib.Line() {
                Width = 0.5M,
                Dash = "dashdot",
            },
        },
    };

    private void AddData(int count = 360, double radius = 100D, Scatter? scatter = null)
    {
        Scatter refToScatter = (Scatter)data[0];
        if (scatter != null)
            refToScatter = scatter;

        double interval = 360D / (count - 1);
        for (int i = 0; i < count; i++)
        {
            double theta = ((double)i) * interval / 180D * Math.PI;
            double r = radius;
            refToScatter.X.Add(r * Math.Cos(theta));
            refToScatter.Y.Add(r * Math.Sin(theta));
        }
    }

    protected override void OnInitialized()
    {
        IList<Plotly.Blazor.LayoutLib.YAxis> ys = new List<Plotly.Blazor.LayoutLib.YAxis>();
        ys.Add(new Plotly.Blazor.LayoutLib.YAxis()
            {
                ScaleAnchor = "x",
            });
        layout.YAxis = ys;
        layout.AutoSize = true;

        AddData(count: 31, radius: 100D, scatter: (Scatter)data[0]);
        AddData(count: 98, radius: 60D, scatter: (Scatter)data[1]);
        AddData(count: 67, radius: 80D, scatter: (Scatter)data[2]);

        
    }
}


注意する点として、線幅がDoubleではなくDecimalで定義されている。とりあえず、PlotlyChartのstyleにheightを指定しているが、リサイズしても追従してこない。うまい方法がないかを探しているが、例えば、App.razorの
<script src="_framework/blazor.web.js"></script>
の後くらいに以下のようなコードを挿入して

    <script>
        window.viewportChangeCallback = (dotnetObject) => {
            window.addEventListener('load', () => {
                dotnetObject.invokeMethodAsync('OnResize', window.innerWidth, window.innerHeight);
            });
            window.addEventListener('resize', () => {
                dotnetObject.invokeMethodAsync('OnResize', window.innerWidth, window.innerHeight);
            });
        }
    </script>

Home.razorのコード部分に以下を追加してやる。引数はとりあえず使用していないが、何かあった時のために渡している。


    [JSInvokable]
    public void OnResize(int width, int height)
    {
        chart?.Update();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync("window.viewportChangeCallback", DotNetObjectReference.Create(this));
        }
    }

ここまで書いておいてなんだが、このOnResizeの対応は別に書かなくてもConfigのプロパティにResponsiveというのがあるので、これで多くの場合には事足りるようだ。

    Config config = new Config()
    {
        Responsive = true
    };

3次元プロットも以下のようになる。

@page "/Chart3D"

<PlotlyChart @bind-Config="config"
             @bind-Layout="layout"
             @bind-Data="data"
             @ref="chart"
             @bind-Style="Style" />

@code {
    PlotlyChart? chart;
    Config config = new Config()
    { 
        Responsive = true
    };
    Layout layout = new Layout();

    [Parameter]
    public string? Style { get; set; } = "height: 900px;";

    IList<ITrace> data = new List<ITrace>
    {
        new Scatter3D
        {
            Name = "Sample helix 3d",
            Mode = Plotly.Blazor.Traces.Scatter3DLib.ModeFlag.Lines
                | Plotly.Blazor.Traces.Scatter3DLib.ModeFlag.Markers,
            X = new List<object>(),
            Y = new List<object>(),
            Z = new List<object>(),
            Marker = new Plotly.Blazor.Traces.Scatter3DLib.Marker()
            {
                Size = 1M,
            }
        },
    };

    private void AddData(
        int count = 360,
        double radius = 100D,
        double lead = 10D,
        Scatter3D? scatter = null)
    {
        Scatter3D refToScatter = (Scatter3D)data[0];
        if (scatter != null)
            refToScatter = scatter;

        double interval = 360D / (count - 1);
        for (int i = 0; i < count; i++)
        {
            double theta = ((double)i) * interval / 180D * Math.PI;
            double r = radius;
            refToScatter.X.Add(r * Math.Cos(theta));
            refToScatter.Y.Add(r * Math.Sin(theta));
            refToScatter.Z.Add(interval / 360 * lead * i);
        }
    }

    protected override void OnInitialized()
    {
        IList<Plotly.Blazor.LayoutLib.Scene> scene = new List<Plotly.Blazor.LayoutLib.Scene>();
        scene.Add(new Plotly.Blazor.LayoutLib.Scene() {
                AspectMode = Plotly.Blazor.LayoutLib.SceneLib.AspectModeEnum.Data,
                AspectRatio = new Plotly.Blazor.LayoutLib.SceneLib.AspectRatio()
                    {
                        X = 1M,
                        Y = 1M,
                        Z = 1M
                    },
            }
        );
        layout.Scene = scene;


        AddData(count: 31, radius: 100D, lead: 400D, scatter: (Scatter3D)data[0]);
    }
    
}

≫ Read More

2024/05/02 コンピュータ   TakeMe

Aterm WX3000HPと間違えてWX3000HP2を購入

Aterm WX3000HPと間違えてWX3000HP2を購入してしまった。
一応上位の機種なのか?

WX3000HP2とWX3000HPを比較すると中継機能部分が少し不足しているような感じだ。
ただし当然であるが親機としては使用できる。WX3000HP2から接続する機能はない。ほかのデバイスをWX3000HP2に接続してそのデバイス(WG1200HP2など)から有線機器つなぐことはできる。

以前から使用していたのもAtermだった。せっかくAterm同士の切り替えなのにこの機種では設定を引き継ぐ機能が使用できず最初からの設定になって時間がかかったが、動作はおおむね良好である。
本当は間違いだったけど割と良い。
今まで使用していたルータ(WG1200HS3)は子機にしようと思う。WG1200HS3に対して標準のIPv4のプライベートIPアドレスが違う。

そもそも、新規購入にいたったのは、子機に使用していたWG1200HP2が時々フリーズしてしまうことに気づいたことが原因。ファームウェアの更新を進めるページもあったが、ファームウェアの更新ができなくて…
今までWG1200HP2はCONVERTERモードで使用中はクイック設定Webにつながらないと思っていたが、Atermスマートリモコンというアプリがあればつながることが分かった。そもそも、有線接続してボタンを押しながら起動すればできるがかなり手間だった。
知らなかった... (取説読まなかったから)

古いWG1200HS3を子機に使用してしまうと若干速度が低下してしまう模様。そのうち中継機も購入しようか
ところで、AtermスマートリモコンというアプリはすべてAtermの場合には便利だ。同じネットワーク内の機器を一覧できる。
見えて安心ネット」も「MACアドレスフィルタ」より割と便利だ。
各端末が5 GHz帯でつながっているとか2.4 GHz帯でつながっているとかが表示できるようになる。

4月12日更新
使用を開始してしばらくして、Aterm製品におけるLAN側からの不正アクセスの脆弱性への対処方法について[2024年4月5日更新]という情報が入ってきた。
はこればっかりは仕方ないが偶然更新していた。

≫ Read More

2024/03/20 コンピュータ   TakeMe

DKIM設定失敗

gmailでメールが受け取れなくなったので設定を見直していた。

メールアドレスは*@example.comだけどメールサーバはmail.example.comとなっていた場合はどうするのか...

gmailでメールが受け取れなくなったので設定を見直していた。

ここを参考として設定をしていたが、何かうまくいかなかった。

送信者メールアドレスは*@example.comなのだけどメールサーバはmail.example.comになっている場合、
MXレコードを追加して、example.comのメールはmail.example.comのサーバが担当することを記述する。

このとき DKIMのレコードは
xxxx._domainkey.example.com用のものを設定すればよいようだ。
xxxx._domainkey.mail.example.com用のものを設定しても意味がないようだ。
もちろん両方設定してもよい。

ADSPレコードは古いメールサーバ用の内容で、もう誰も読んでいないのでunknownでよい。消してもよい。

≫ Read More

2024/01/21 コンピュータ   TakeMe

Windows SandboxでExplorerがクラッシュする問題

10月ごろからWindows Sandboxでエクスプローラを開くとエクスプローラが再起動してしまう。
いまだに修正されない。

10月ごろからWindows Sandboxでエクスプローラを開くとエクスプローラが再起動してしまう状況になっている。
デスクトップなどを表示しているのもExplorerなので、これが再起動されるとタスクバーなども一回消えてしまう。これは以上に使いにくい。
いまだに修正されない。

コマンドラインは使用できるので、デスクトップで行うような操作をコマンドラインで与えるとプログラムの実行などは行える。が、ものすごく使いにくい。

暫定的な回避策が上がっていた。次のような内容の*wsbファイルを作ってそれをつかってWindows Sandboxを開けばいいといっている。

<Configuration>
<MappedFolders>
<MappedFolder>
<HostFolder>C:\Windows\SystemApps\Microsoft.WindowsAppRuntime.CBS_8wekyb3d8bbwe</HostFolder>
<SandboxFolder>C:\Windows\SystemApps\Microsoft.WindowsAppRuntime.CBS_8wekyb3d8bbwe</SandboxFolder>
<ReadOnly>true</ReadOnly>
</MappedFolder>
</MappedFolders>
</Configuration>

≫ Read More

2023/11/25 コンピュータ   TakeMe

ConoHa ISOイメージのダウンロード先はディレクトリ内のインデックス表示が可能である必要がある

ConoHaではVPSにISOイメージからのインストール機能があるというので試してみていたら、意外な制限があった。
ダウンロード先はディレクトリ内のインデックス表示が可能である必要があるのだ。

ConoHa VPSでは、任意のISOイメージを使用して仮想マシンのインストールやリカバリなどが行えるようなことが書かれているのを見たので、やってみたくなった。
最初のサーバーは任意のテンプレートから作成することができる。
そして、そのバーチャルマシンにisoイメージをマウントして起動するとインストールができるということだった。
私は試しに、Manjaroのisoイメージをurlとして渡してみた。あえなく失敗。どうも、ダウンロードするディレクトリが一覧可能になっている必要がある。どうもそのような仕様を知らずにCLIツールでisoイメージをダウンロードしようとしていたが、ことごとく失敗していた。
任意のテンプレートからサーバーを契約してそのサーバーにWebサーバをインストールし、そこにisoイメージを露出させて、それをconoha-isoなどでダウンロードするということで解決する。
なんのエラーも返答されないので、数時間かかるものだとばかり思っていた。

以下はWindows用のチートcmd

SET OS_USERNAME=[APIユーザ名]
SET OS_PASSWORD=[APIパスワード]
SET OS_TENANT_NAME=[テナント名]
SET OS_REGION_NAME=[リージョン名 tyo1など(省略するとtyo1と扱われる)]

rem start "" "C:\Program Files\Google\Chrome\Application\chrome.exe" http://127.0.0.1:6543/
rundll32 url.dll,FileProtocolHandler http://127.0.0.1:6543/
conoha-iso server

≫ Read More

2023/07/09 コンピュータ   TakeMe

.NET 6.0 WPFアプリにCefSharpをつけてWebブラウザ化

WebViewが古すぎた時代にWebView2が登場するまでの間、Webブラウザコントールを組み込むのに非常に役に立ったCefSharpだが、最近使用していなかった。
今一度見直すと、導入が楽になるように進化していた。

試しに.NET 6.0のWPFアプリケーションで使ってみた。

まず、Visual Studio 2022でWPF アプリケーションを新規に作る。.NET Frameworkではなく.NETの方である。
nugetパッケージ CefSharp.Wpf.NETCoreをインストールする。
xmlns:wpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"を追加
<wpf:ChromiumWebBrowser x:Name="Browser" Address="www.google.com"/>
を突っ込むだけ。
これだけ…
Quick Startにはいろいろなことが書いているけどAnyCPU対応も特に気にしなくてよいのかな。前に、AnyCPU対応するときに苦労していたのに比べるとCefSharp.Wpf.NETCoreパッケージは比較的スムーズに適用できる。

≫ Read More

2023/06/24 コンピュータ   TakeMe
Tag:WPF

Ubuntu 20.04でのSambaの設定方法を忘れていた

久しくファイル共有サービスのSambaを使っていなかったため、使い方を忘れてしまった。

今一度再確認した。
もう5年近く使用していない。

sudo apt install samba
sudo smbpasswd -a takeme
sudo systemctl restart smbd.service

/etc/samba/smb.configの内容を編集して
sudo systemctl restart smbd.service
を実行する。

[homes]
   comment = Home Directories
   browseable = no
;   browseable = yes

# By default, the home directories are exported read-only. Change the
# next parameter to 'no' if you want to be able to write to them.
;   read only = yes
   read only = no

# File creation mask is set to 0700 for security reasons. If you want to
# create files with group=rw permissions, set next parameter to 0775.
;   create mask = 0700
   create mask = 0775

# Directory creation mask is set to 0700 for security reasons. If you want to
# create dirs. with group=rw permissions, set next parameter to 0775.
;   directory mask = 0700
   directory mask = 0700

# By default, \\server\username shares can be connected to by anyone
# with access to the samba server.
# Un-comment the following parameter to make sure that only "username"
# can connect to \\server\username
# This might need tweaking when using external authentication schemes
   valid users = %S

などなど

≫ Read More

2023/06/22 コンピュータ   TakeMe

オープンソースのロータダイナミクス解析支援ROSS

ROSSを使うと、参考書片手に手間をかけて計算していた回転体の危険速度の計算などをほぼ実装されたまま利用させていただくことができる。

今まで、GNU OctaveやPythonでプログラムを作って行列計算などをしていたが、ROSSなるものが無料で配布されている
ただし参考ページ【ターボ機械シリーズ④】 オープンソースローターダイナミクス解析ソフトROSS)がページ内で参照しているTurboToolsのチュートリアルはross-rotordynamicsのVersion 1.0.0、Numpy 1.2.0以下用になっていてそのまま実行できない。
Numpyの方は軽微な修正でnumpy.complexがなくなったというものなので、complexに置き換えるだけでよい。結構有名なパッケージの仕様変更なので有名である。
ただし、ross-rotordynamicsの方は有名パッケージではないのでversion 1.0.1にバージョン指定をしてインストールした方が良い。GitHubの変更履歴を追ってみると、この後のバージョンで.kxx.plot()の定義がなくなる。

pip install ross-rotordynamics==1.0.1

もしかすると、使用する描画機能にplotlyの出力を抑制するように変化が起こっているかもしれない。
インストールしたrossの中で、numpy.complexを使用している部分を順次complexに置き換えていく作業が必要である。(結構面倒)チュートリアルに頼らないのであれば、このバージョン指定インストールは不要である。ただ、一回参考ページに従ってみたい場合には上のようにしないといけない。

≫ Read More

2023/06/21 コンピュータ   TakeMe
Tag:Python

Pythonでsscanfのようなものはないか

「Python sscanf相当」で検索していると「ない」と検索されることが多いけど、parseライブラリにあるような気がする。

PythonにC言語のsscanf相当のものはないのかということを検索していると、「splitで代用する」ようなものが検索喧嘩の先頭にきていやだ。
標準では厳しいが、parseライブラリにこの辺りの処理の対応がある。
例えば次のような例で使える。

import parse
import numpy as np

if __name__ == '__main__':
    test_string = '2023-06-19T01:20:10.010';
    parsed = parse.parse('{}-{}-{}T{}:{}:{}', '2023-06-19T01:20:10.010');
    
    print(parsed);
    
    parsed = np.array(list(parsed), dtype=float);
    print(parsed);

≫ Read More

2023/06/21 コンピュータ   TakeMe
Tag:Python

Pythonで3次元プロット と回転 matplotlib 3.7.1

matplotlibの2くらいから「パースペクティブ」の解除は一語に代わっていた。

matplotlibでパースペクティブの解除について、昔は少しコードを書かないといけなかった。
がそれは今は昔の話で、ax.set_proj_type('ortho');で一発となっていた。
まえに記事を書いてから5年近くたっているから当然かな。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def rotX(X):
    theta = X / 180.0 * np.pi;
    Rx = np.array([[1,0,0,0],
                   [0,np.cos(theta),np.sin(theta),0],
                   [0,-np.sin(theta),np.cos(theta),0],
                   [0,0,0,1]]);
    return Rx;

def rotY(Y):
    theta = Y / 180.0 * np.pi;
    Ry = np.array([[np.cos(theta),0,-np.sin(theta),0],
                   [0,1,0,0],
                   [np.sin(theta),0,np.cos(theta),0],
                   [0,0,0,1]]);
    return Ry;

def rotZ(Z):
    theta = Z / 180.0 * np.pi;
    Rz = np.array([[np.cos(theta),np.sin(theta),0,0],
                   [-np.sin(theta),np.cos(theta),0,0],
                   [0,0,1,0],
                   [0,0,0,1]]);
    return Rz;

def trans(x):
    Tr = np.array([[1,0,0,x[0]],
                   [0,1,0,x[1]],
                   [0,0,1,x[2]],
                   [0,0,0,1]]);
    return Tr;

def main():

    r = 50.0;
    theta = np.arange(0.0, 360.0, 30.0);
    x = np.array([]);
    y = np.array([]);
    z = np.array([]);

    for i in range(10):
        x_ = r * np.cos(theta / 180.0 * np.pi);
        y_ = r * np.sin(theta / 180.0 * np.pi);
        z_ = 10.0 * i * np.ones(x_.shape[0]);
        
        x = np.append(x, x_);
        y = np.append(y, y_);
        z = np.append(z, z_);
    
    X = np.array([x, y, z, np.ones(x.shape[0])]);
    X = np.dot(rotX(45.0), X);
    X = np.dot(rotY(45.0), X);
    X = np.dot(rotZ(10.0), X);
    X = np.dot(trans([10, 10, -10]), X);
    
    fig = plt.figure()
    ax = fig.add_subplot(projection='3d')
    ax.set_proj_type('ortho');
    ax.scatter3D(X[0], X[1], X[2]);
    ax.set_aspect('equal')
    plt.grid()
    plt.savefig('plot201803.svg');
    plt.show();

if __name__ == '__main__':
    main()
    

しかも、バックエンドとしてWebAggを使用するとWebブラウザから3D図をグリグリ操作できる(tornadoパッケージも必要)。私はWebView2と組み合わせて.NETアプリとPythonアプリの組み合わせで使ってみようかと思っています。
(バックエンドをWebAggに切り替えただけでは、show()のところでWebブラウザが起動してしまうところが問題だがほかのアプリへの組み込み方があるらしい)

if __name__ == '__main__':
    plt.rcParams['webagg.open_in_browser'] = False;
    plt.rcParams['webagg.address'] = '0.0.0.0';
    plt.switch_backend('WebAgg');
    main()
    

≫ Read More

2023/06/19 コンピュータ   TakeMe
Tag:Python