2026年6月6日土曜日

MacBook late 2008にlubuntu 24.04をインストール

lubuntu 20.04をインストールして利用していたMacBook late 2008は、 13インチのアルミ筐体のノートPCで、キーボードもとてもよい。 HDDの調子が悪くなったので、SSDへ入れ替えてlubuntu 24.04をインストールした。 メモリはもともと4GBで、いろいろ動かすとメモリが不足したことがあったので、 ついでに8GBへアップグレードした。バッテリーも膨らんできてしまったので、交換した。

このMacBookは、メモリやHDDへのアクセスが容易で、実際の交換作業は十数分で終わった。 最近のメモリがオンボードで交換できないノートパソコンでは考えられない気軽さである。

SSDへlubuntu24.04をインストールするために、それを書き込んだUSBから起動しようと、 OPTIONボタンを押しながら電源ボタンを押したがUSBから起動しなかった。 そこで、OPTIONボタンとCOMMANDボタンを押しながら電源ボタンを押したらところ、 USBから起動してきたので、そのままインストールすることができた。覚え違いだっただろうか。

ネットワークは有線で接続しておき、いつもの通りにインストールは完了した。 wifiは、Additional DriversからBroadcomのドライバーを追加して利用できた。 NVIDIAのドライバーは、自動的に使われるnouveauで今の所問題がないのでそのまま使っている。

もう18年前のPCが、最新ではないが、 十分新しい現行のOSで軽快に動作しているところが素晴らしい。 このような長く使えるPCが増えてほしい。

Surface go 3を使ってみて

Windows搭載のタブレットとして、 DellのLatitude10を使っていましたが、 2018年には液晶が漏れて、 電源も入らなくなっていました。 しばらくタブレットなしでしたが、 旅行時に持ち出すPCとして surface go 3を購入して使ってきました。

Latitude10は、運用するためにいろいろ設定を行ったり手間がかかりましたが、 surface go 3は、特別なことをせずに使えて、 あえて書くようなこともありませんでした。 2026年の現在もWindows11Home(25H2)で十分安定して動いています。 4年以上使っていますので、Latitude10が不調になってきた時期程度に時間が経過しています。 それを考えると、完成度の高いタブレットだと感じています。

購入したsurface go 3のスペックは、 CPUがIntel Core i3-10100Y、 メモリ8GB、ストレージ128GBのモデルです。 ペンもキーボードカバーも合わせて購入しました。 ペンは、タブレットではあると便利でしたので購入しました。 キーボードは、ソフトウェアキーボードでは満足できないので購入しました。 Latitude10では、外部キーボードとしてポメラを使ったりしていました。

普段は別のノートPCを使用しているため、使用頻度は、高くありません。 ときどきの旅行記録を書くのに持ち出し、空港での待ち時間などに使えています。 旅行記録を書くのには、Windowsのメモ帳で十分です。 他には、Vimエディタ、Python、Libre Officeもインストールしていますが、 移動中にそれらをしっかり利用することはありません。 この用途程度なら、ポメラでも十分なのですが、 移動中にメールのチェックが必要になったり、 急遽、書類を作成しなければならなくなったりしたときに助かりました。

心配な箇所は、ストレージが128GBとちょっと少ない点です。 すでに97GB使用してしまっています。 データやポータブルなソルトウェアは micro SDに入れていますが、挿入したまま利用していて、一度、そのmicro SDが破損しました。 断片的にファイルが破損ていて、ソフトウェアは動いているのに、なにかおかしいという 状態の原因は気づきにくかったです。 別のストレージへのバックアップは大切ですね。

自立させるために、キックスタンドがついていますが、膝上で使うのには不安定なので、 テーブルのある場所が必要です。 キックスタンドは弱弱しいので、それを開くのに両手を使うため、 本体をどこかに置かなければならず、開閉に気を使います。 キックスタンドは、タブレット単体で自立させるのものとしてはよいものですが、 よくキーボードを使うのであれば、 タブレットである必要性はあまりないのかなと感じています。

そのような経験から、 10インチから11インチのクラムシェルのノートPCで、 キーボードがSurface go 3のキーボードカバーようにちゃんとしていて、 バッテリーまわりが安心できるものを探してみると、見つからないものです。 Surface go 3を大切に使っていかないとと思いました。

2025年1月5日日曜日

DELL Inspiron 15 5567にLubuntu 24.04をインストール

IntelのCore i7、メモリ8GBのもので、Windows10で動いていました。 調子が悪くなりシステムリカバリなどをしながら使っていましたが、 再びHDD上のWindowsが動作しなくなったため、 新しいパソコンに変更することにしました。 新しいパソコンにデータを移動するため、HDDを取り出してデータを読み出しました。

分解方法は、サービスマニュアルなどがありますが、 難しいのはバックカバーを外す作業でした。 隙間にギターのピックを差し込みながら、少しずつ広げていきました。 割れてしまわないかと心配しながら、どうにか開けることができました。 カバーが開いてしまえば、あとはHDDを取り出すのは簡単でした。

本体のハードウェアは問題はないので、 開けたついでに使えるように整えておこうとSSD(Netac SSD 512GB 内蔵2.5インチSATA3 6Gb/s 7mm 3D NAND FLASH)を購入し、 追加の8GBのメモリ(Transcend ノートPC用メモリPC4-25600(DDR4-3200)8GB 1.2V 260pin SO-DIMM)を購入し、装着しました。 少し前に作成したlubuntu24.04のインストール用USBを差し込み、電源を入れると、 インストーラーが無事に起動しました。 OSが入っているストレージであれば起動先をUSBからにする必要があったことでしょう。 wifiも最初から利用でき、支持に従いインストールするとあっさり完了しました。

日本語入力のために、fcitx-mozcをインストールして、タッチパッドの設定をしました。 タッチパッドのために/etc/X11/xorg.conf.dの中に、 以下の内容の設定ファイル30-touchpad.confを作りました。

Section "InputClass"
Identifier "touchpad"
Driver "libinput"
MatchIsTouchpad "on"
Option "Tapping" "on"
Option "NaturalScrolling" "true"
EndSection
タッチパッドで二本指を使ったスクロールの向きを気持ちよくするために、 NaturalScrollingにtrueを設定しています。

Wifi経由でのプリンタ(EPSON EP-315)も利用できました。 EPSONのホームページからドライバをダウンロードし、インストールしたあと、 メニューの設定、プリンターから設定するだけで利用できるようになりました。

最近古いPCにdebianやlubuntuを入れてきましたが、 その中で第7世代のCore i7でSSD、メモリ16GBというのは高スペックです。 非常にサクサクと気持ちよく操作できています。

2024年7月18日木曜日

tkinterのoptionmenuを使う

tkinterでGUIを作るとき、選択肢から選ばせるようなときにつかうウィジットで、 tkinter.OptionMenuがあります。wxPythonなら、wx.Choiceを使うところです。 普通に選択肢を用意しておいて、使うならシンプルなのですが、 選択肢をユーザーの操作に応じて変化させたいときがあります。 そのような場合のメモを残しておきます。

tkinterでは、ウィジットと結び付けて、その値を保持したり変更を監視したりできるオブジェクトがあります。文字列ならtkinter.StringVar()というものです。ここでは、それを用いてOptionMenuの値を監視します。trace()メソッドを使って、値が設定されたときにon_selected()が呼び出されるようにそておきます。

選択肢を変更するのが、OptionMenuSetChoices()という名前で作った関数です。選ばれたときの値の設定を通して、on_selected()が呼び出されます。OptionMenuの選択状態と、値を保持するオブジェクトが結びついているため、それを変更するとOptionMenuの表示も変更されます。

動作を試すのに、選択肢を100個増やすボタン、最初に移動するボタン、最後に移動するボタンを配置っしておきました。 Windows11、Python 3.8.3の環境で試しています。

import tkinter as tk

def OptionMenuSetChoices(opmenu, var, choices):
    opmenu['menu'].delete(0, "end")
    for i, choice in enumerate(choices):
        opmenu['menu'].add_command(label=choice, command=tk._setit(var, choice))

class APPFRAME(tk.Frame):
    def __init__(self, master, **kwargs):
        super(APPFRAME, self).__init__(master=master, **kwargs)

        self.var = tk.StringVar(self, "default")
        self.var.trace('w', self.on_selected)

        self.choices = [self.var.get()]
        self.opmenu = tk.OptionMenu(self, self.var, *self.choices)
        self.opmenu.pack()
        OptionMenuSetChoices(self.opmenu, self.var, self.choices)

        btn = tk.Button(self, text='Set Choices', command=self.on_set_choices)
        btn.pack()
        btn_first = tk.Button(self, text='Go First', command=self.on_go_first)
        btn_first.pack()
        btn_last = tk.Button(self, text='Go Last', command=self.on_go_last)
        btn_last.pack()

    def on_selected(self, *args):
        print(self.var.get())

    def on_set_choices(self, *args):
        self.choices = ["choice: %02d" % (i+1) for i in range(100)] 
        OptionMenuSetChoices(self.opmenu, self.var, self.choices)
        self.on_go_first()

    def on_go_first(self):
        self.var.set(self.choices[0])
    
    def on_go_last(self):
        self.var.set(self.choices[-1])

def main():
    root = tk.Tk()
    app = APPFRAME(root)
    app.pack()
    root.mainloop()

if __name__ == '__main__':
    main()

2024年7月15日月曜日

Debian12でplaysound(python)を使う

Debian12をDell mini9に入れ、python3.11が使えるので遊んでいます。 Pythonは、とてもよいプログラミング言語環境だと思いますが、 Linuxの場合は、外部モジュールを入れるのにaptを使ったりする必要があるようで、 いろいろ混乱します。

今回使いたいモジュールは、playsoundです。効果音などmp3を再生したいことがときどきあります。MacBookでlubuntu 20.04も使っていて、そちらで書いたコードをmini9に持ってきて動かそうとしたら、playsoundを使えるようにするまでに手間取りましたのでメモしておきます。

Linuxでpythonのモジュールは、aptを使うなりして、例えば、xxxモジュールなら、

sudo apt install python3-xxx
のようにしますが、playsoundは、pipからインストールするようです。

lubuntuのほうでは、pip install playsoundでインストールできました。ただし、このままだとエラーで再生できませんでした。Gstreamerのpythonバインディングが必要とのことで、以下のようにインストールすることで使えるようになりました。

sudo apt install python3-gst-1.0

mini9でのDebian12では、以下のようにして使いました。 まずは、同じようにgstreamerバインディングをインストールします。 さらにpygobjectをインストールするために、 そのビルドに必要なライブラリをインストールします。

sudo apt install libgirepository1.0-dev
sudo apt install libcairo2-dev

pipを使うために、venvで仮想環境を作り、その中でpipを使ってplaysound、pygobjectをインストールしました。pipとvenvを使うのに、そちらもインストールが必要でした。

sudo apt install python3-pip
sudo apt install python3-venv
mkdir test
python3 -m venv myenv
source myenv/bin/activate
pip install playsound
pip install pygobject
これでpython3を使って、playsoundモジュールを利用することができました。

実際動かしたいコードは、tkinterも使っていましたので、

sudo apt install python3-tk
も行ってtkinterを使えるようにしました。

Dynabook UX/23にDebian 12をインストール

Dell mini9のために32bit(i386)用のDebian12のインストールUSBを作ったのでdynabook UX/23にもインストールしてみた。こちらは古いものですが、キーボードが打ちやすいネットブックです。CPUは、Atom N280なで、32bitのものをいれることになります。メモリは2GBに増設してあってWindows XPで動いていました。

Windows XPのHDDはそのまま残しておいて、Netacの内臓SSD256GB(2.5インチ、SATA、厚さ7mm)のものを購入して交換しました。 バッテリーをはずした後に、本体の裏ブタをトルクスドライバー(T6)で開けて、HDDをスライドさせると簡単に外れました。蓋のほうにスペーサーがついていて、取り付け時は、HDDが外れないようにスライドさせるスペースが埋まるようになっていました。取り外したHDDについている金具を取り外して、新しいSSDに取り付けて、同じように本体に収めました。この金具を取り付けている4本のネジが緩まないように強く締まっていましたので、外すのにネジ山をつぶさないように注意が必要でした。

ふたを閉めてもとにもどしたら、バッテリーをもどして、電源ケーブルをつなぎます。Debian12のインストール用のUSBを本体に差してから、電源ボタンを入れて、F2キーを押しておくと、BIOS画面になりました。電源を入れる前にUSBキーを刺してあるので、それが認識されています。起動先としてUSBを優先に設定して、起動させます。

UX/23は、Dell mini9と違って、wifiをすぐに認識しました。インストール用のネットワーク経路に、どちらかの接続が選べるようです。デスクトップ環境をDell mini9と同じようにLXDEでインストールしました。なにも問題なくインストールは終了し、Debian12が起動しました。

wifiは、connmanというネットワークのマネージャーで設定するようです。 ネットワークの設定は、いろいろあって分かりにくいですね。 Network Manager(nmcli)を使ってもよいとありますが、いろいろ試すのも面倒です。 とりあえず、connman setupを選ぶと、正常にwifiのリストが表示され、問題なく接続はされました。よくわからないのは、ここからで別の場所でwifiを使おうとすると、アクセスポイントには接続されますが、いろいろなWebページは開かないという状況になりました。インストールした場所のwifiでは、正常につながっているようです。どうやら、DHCP経由で、DNSがうまく設定されないようすです。ConnmanによるDNSのマネージメントについていろいろ報告があるみたいですが、とりあえず/etc/resolv.confにGoogle Public DNS(8.8.8.8と8.8.4.4)を追記して、つなげることができました。

2024年6月23日日曜日

lubuntu 24.04 LTSでwxPython4.2.1

古いPCにLinuxを入れて使えるようにしています。 Lubuntu 24.04 LTSを古いデスクトップPCに入れて使っています。 リモートデスクトップとpythonくらいしか利用しませんので 古いパソコンでも十分な用途です。 pythonは、python3が利用でます。 さて、GUIにwxPythonをよく利用していたのですが、 そのようなスクリプトを動かすときにwxPythonをインストールする必要があります。 Windowsでは、pipを用いてインストールしていますが、 lubuntuでは、aptを使ってインストールするようです。
apt install python3-wxtools
でインストールできました。

さて、これで使えるなら問題よいのですが、しばしば 仮想環境を利用することが増えています。python3では、 すぐにvenvが利用できるようになっています。 たとえば、

mkdir myproject
cd myproject
python3 -m venv myenv
として、myprojectフォルダの中に、 そのプロジェクト用のmyenvという仮想環境を作って利用したりします。 その仮想環境を利用するには、
cd myproject
source myenv/bin/activate
として、仮想環境のフォルダ内にあるactivateスクリプトを用いて利用できるようにします。 アクティベートするといいます。 ここで pip install numpy とすると仮想環境にnumpyがインストールされるわけです。 ある環境がアクティベートされた状態で起動されるpythonでは、 その環境にインストールされたモジュールが利用できるというわけです。 環境を戻すには、deactivateを実行します。

さて、この仮想環境へはpipを用いてモジュールをインストールします。 pip install wxPythonとしてwxPythonををpipでインストールしようとすると、 ビルドエラーが出て停止してしまいました。 こちら https://wxpython.org/blog/2017-08-17-builds-for-linux-with-pip/index.html のサイトを参考にしてインストールをしました。

"What You Need"の箇所にビルドに必要なライブラリが記載されています。 地道にひとつづつインストールしていきます。 私の環境では、

sudo apt install python-dev
sudo apt install libgtk-3-dev
sudo apt install libgstreamer1.0-dev
sudo apt install libgstreamer-plugins-base1.0-dev
sudo apt install libglu1-mesa-dev mesa-common-dev freeglut3-dev
sudo apt list |grep libwebkit2gtk
sudo apt install libwebkit2gtk-4.1-dev
sudo apt install libjpeg-dev
sudo apt install libpng-dev
sudo apt install libtiff-dev
sudo apt install libsdl-dev
sudo apt install libnotify-dev
sudo apt install libsm-dev
としていきました。すでにインストールされているものもいくつかありましたが、
順番に検索エンジンを利用しながら確認していきました。
途中どれをインストールするかlistを見て確認したりもしています。

あとは、"Build Steps"にあることを順番にしていくだけです。もちろん、 ファイル名はダウンロードされたものに合わせて修正します。 書いてある通りにすると無事にwheelファイルがビルドされ、 wxPythonを利用することができるようになりました。

このようにしてpython3.12でwxPython4.2.1が動きました。 ただし、現在ダウンロードされたファイルをコンパイルしたものは、 pythonを起動してimport wxのあとすぐにquit()してもセグメンテーションフォールトを起こします。 "wxpython 4.2.1 segmentation fault"で検索してみるとすでに報告はあるようです。 とりあえずは利用できるので、修正を待ちます。 ちなみに、仮想環境ではなくaptでインストールした環境ではそのようなエラーは起こっていません。

DELL Inspiron Mini9 にDebian 12 bookwormをインストールしてみた

使っていないDell Inspiron Mini9がありました。 もともとWindows XPで使っていたものです。 XPのサポート切れからしばらくたち、 当時のWebブラウザを使っても開けないページも増えてきました。 そのうちLinuxでも入れてみようと置いてあったが、 32bitアーキテクチャがサポート対象から外されて行っている傾向があるので、 Debian12(bookworm)を試してみました。

作業した部屋のネットワーク環境がwifiだけの場所だったので、 ネットワークがすぐに使えるとは考えられないため、 debian-12.5.0-i386-DVD-1.iso をダウンロードしました。 ファイルSHA256SUMS.txtもダウンロードします。 インストール用のUSBは、手元にあった16GBのものを利用しました。 isoのサイズは、およそ3.7GBですので、こんな大きなものは必要なかったでしょう。 USBの作成環境は目の前にあるWindows11のノートパソコンです。

まず、ダウンロードしたisoイメージのハッシュ値を計算して確認します。 コマンドプロンプトを開いて、ダウンロードフォルダへ移動し以下のコマンドでSHA256のハッシュ値を調べます。

certutil -hashfile debian-12.5.0-i386-DVD-1.iso SHA256
出力された文字列とSHA256SUMS.txtの中にあるDVD-1.isoのハッシュ値を比較し 同じものであることを確認します。 正しくダウンロードされていたら、このisoイメージをUSBに書き込み インストール用のUSBを作成します。

USBへのisoイメージの書き込みには、Rufus 4.5(rufus-4.5p.exeポータブル版)を利用しました。 Deviceで、書き込む先のUSBを選んで、ブートの種類の箇所の選択ボタンを押してISOイメージを選択します。 パーティション構成はMBR、ターゲットシステムはBIOSまたはUEFIが選ばれた状態になっています。 書き込むUSBによるのでしょうが、 フォーマットオプションのファイルシステムは、FAT32(規定)クラスターサイズは8192バイト(規定)となっています。 すべてデフォルト状態のままスタートして書き込みを開始しました。 3.7GBのイメージの書き込みなので、時間はかかりましたが、インストール用USBが作成されました。

Mini9にインストール用USBを刺して、起動します。 キー2を押してBIOS設定を開いてUSB優先で起動できるように設定し、再び起動させます。 あとは、グラフィカルインストーラーで指示に従ってインストールしていきます。 私のMini9はUS配列キーボードなのでキーボードの選択で米国を選びました。 途中wifiのところでb43-ucode15.fwといファームウェアが必要だよと言われますが、 ここでは無視して続けていきます。 Mini9は、Etherの口がありますので、あとでネットワークケーブルを使って接続して設定するつもりでいます。 DHCPで接続が試みられますが、現時点でケーブルの接続もしていないので、接続されないまま続けます。 パーティションは推奨設定(初心者用設定)で進めて、 ソフトウェアの選択でデスクトップ環境にGNOMEではなくLXDEを択しました。 トラブルなくインストールが終了し、起動ディスクを抜いて再起動かけると無事に起動させることができました。

ネットワークケーブルが利用できる場所に移動して、ケーブルを接続するとDHCPで問題なく接続されました。 DVDでインストールしたので、リポジトリの設定ができていないので、 /etc/apt/sources.list ファイルを編集しました。 suコマンドでスーパーユーザーになって編集します。 Googleで、"debian bookworm repository"を検索して設定を参考にさせていただきました。

ファイルを編集したのちapt updateし、apt upgradeしてみますと問題はなさそうです。 firefoxがインストールされているので、youtubuに接続してみると動画はカクカクですが音もちゃんと出ています。 add userで、sudoグループにユーザー名を追加し、sudoできるようににしました。

日本語入力は、fcitx-mozcを利用します。 apt install fcitx-mozcをし、 apt remove uim-mozcをしました。

タッチパッドは、タップでクリックができなかったので、 パス/etc/X11/xorg.conf.d/に30-touchpad.confというファイルを作成しました。 内容は、以下のとおりです。

Section "InputClass"
Identifier "touchpad"
Driver "libinput"
MatchIsTouchpad "on"
Option "Tapping" "on"
EndSection

wifiでの接続ですが、 https://wiki.debian.org/bcm43xx を参考にして、

deb http://deb.debian.org/debian/ bookworm main contrib non-free
を/etc/apt/source.listに追加し、 sudo apt updateを行った後、
sudo apt install firmware-b43-installer
をします。メニューの設定から高度なネットワーク設定でwifiのアクセスポイントを追加したら 利用できるようになりました。 LibreOffice 7.4が入っているので動かしてみても、問題はなさそうです。 pythonも3.11が利用できています。 SMPlayerでSMTubeを使って、YouTubeを観てみたら滑らかに再生できました。 古くて小さいノートパソコンですが、用途によっては現役として十分利用できそうです。

2020年10月19日月曜日

wxPython 4.1のwx.ListCtrlでCheckBox

wxPythonで行の頭にCheckBoxのついたリストコントールを使いたいとき、 wx.ListCtrlにCheckListCtrlMixinを使っていた。 wxPythonの4.1で、以前のスクリプトを動かしたところこれが動かなかった。

どうやら、CheckListCtrlMixinで定義されていたCheckItem()メソッドが、 wx.ListCtrlのbuilt-ins functionになったために、 そちらが呼び出されて動作しない様子である。 新しい、wx.ListCtrlには、CheckListCtrlMixinを利用しなくても、 CheckBox付きのwx.ListCtrlが利用できるようになったらしい。

wxPythonのページに、EnableCheckBoxes(enable=True)のメソッドが説明されていた。 これを使えばよいが、これまで使いたいスクリプトをとりあえす 動かしたいということであれば、 CheckItem()をMixinのものに上書きすると動作するようになった。

def MyCheckItem(self, index, check=True):
    img_idx = self.GetItem(index).GetImage()
    if img_idx == 0 and check:
        self.SetItemImage(index, 1)
        self.OnCheckItem(index, True)
    elif img_idx == 1 and not check:
        self.SetItemImage(index, 0)
        self.OnCheckItem(index, False)

wx.ListCtrl.CheckItem = MyCheckItem
しかしながら、そのうち直さなくてはならないということで、 EnableCheckBoxes()を使ったものを書いてみた。
import wx
import wx.lib.mixins.listctrl

class CheckListCtrl(wx.ListCtrl):
    def __init__(self, panel, style):
        wx.ListCtrl.__init__(self, panel, -1, 
                style=wx.LC_REPORT,size=wx.Size(400, 300))
        self.EnableCheckBoxes(enable=True)
        self.Bind(wx.EVT_LIST_ITEM_CHECKED, self._OnCheckItem)
        self.Bind(wx.EVT_LIST_ITEM_UNCHECKED, self._OnUnCheckItem)

    def _OnCheckItem(self, evt):
        self.OnCheckItem(evt.Index, True)

    def _OnUnCheckItem(self, evt):
        self.OnCheckItem(evt.Index, False)
        
    def OnCheckItem(self, index, flag):
        print(index, flag)

class AppFrame(wx.Frame):
    def __init__(self, *args, **kwargs):

        wx.Frame.__init__(self, None ,wx.ID_ANY, 'CheckListCtrl', size=(400,300))

        self.list = CheckListCtrl(self, style=wx.LC_REPORT)

        titles = ("check", "title1", "title2")

        for index, title in enumerate(titles):
            self.list.InsertColumn(index, title, wx.LIST_FORMAT_LEFT)

        for index, title in enumerate(titles):
            self.list.Append(["name%d" % index, "value", "value" ]) 

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.list, 1, wx.EXPAND, border=0)
        self.SetSizer(self.sizer)
        self.Show()

if __name__ == "__main__":
    app = wx.App()
    win = AppFrame()
    app.MainLoop()
wx.EVT_LIST_ITEM_CHECKEDとwx.EVT_LIST_ITEM_UNCHECKEDをハンドルして、 従来のMixInを利用した場合と同じ引数で、 OnCheckItem()メソッドを呼び出すようにしてみた。

2020年8月22日土曜日

NimでZipファイルにしたテキストを読む

nimというプログラミング言語を試してみた。 pythonのように書けるけど、静的な型付けのコンパイラなので、 実行速度のパフォーマンスがよいということである。

大きなカンマ区切りの値(CSV)やタブ区切りの値(TSV)といった テキストデータをzipで圧縮するとだいぶ小さくなるので、 そのようにして保存しておくことも多い。 そのようなテキストファイルを読み込みたいときに、 zipファイルを扱うモジュールが利用できると便利である。

そこで、datafile.txtというテキストファイルをzip圧縮した、 datafile.zipを読み込んで、各行をプリントするだけというスクリプトを書いてみた。

Python(v3.83 windows 64bit)では、以下のようなスクリプトになる。

import io
import zipfile

def readzip():
    filename = "datafile.zip"
    try:
        z = zipfile.ZipFile(filename, 'r')
    except:
        print("Opening zip failed")
        exit(-1)
    try:
        for fname in z.namelist():
            print(fname)
            fp = io.TextIOWrapper(z.open(fname, 'r'))
            line = fp.readline().strip()
            while line:
                print(line)
                line = fp.readline().strip()
            fp.close()
    finally:
        z.close()

if __name__ == '__main__':
    readzip()       
nimでは、zipパッケージにあるzipfilesモジュールを利用する。
import streams
import zip\zipfiles

proc readzip() =
    var filename = "datafile.zip"
    var z: ZipArchive
    var line = ""
    if not z.open(filename):
        echo "Opening zip failed"
        quit(1)
    try:
        for f in walkFiles(z):
            echo f
            var fs = z.getStream(f)
            while fs.readLine(line):
                echo line
            fs.close()
    finally:
        z.close()

when isMainModule:
    readzip()
コンパイルは、readzip.nimというファイルに書いたとすると、
nim c readzip.nim
でできるが、その前にzipパッケージをインストールする必要がある。 Gitがインストールされていれば、
nimble install zip
でインストールできる。 さらに、Windowsでコンパイルすると、gccでコンパイルされるときに エラーで止まってしまった。 こちらにあるように、修正するとコンパイルできた。

ついでなので、Julia(v1.5 windows 64-bit)でも書いてみた。 juliaでは、ZipFile.jlが利用できる。

using Pkg
Pkg.add("ZipFile")
としてインストールできた。
using ZipFile

function readzip()
    filename = "datafile.zip"
    local z

    try
        z = ZipFile.Reader(filename)
    catch
        println("Opening zip failed")
        exit(-1)
    end

    try
        for f in z.files
            println(f.name)

            while !eof(f)
                line = readline(f)
                println(line)
            end
        end
    finally
        close(z)
    end
end

readzip()       
Juliaでは、tryブロックもスコープを作るようで、tryブロック内で作った変数zを、 そのtryブロックから出た後でも利用するために、local z宣言をしている。

どれも似たような感じに書けたが、Juliaは、関数が充実しているからかシンプルに感じる。 whileのループは、eachline(f)を用いたforループで書くと、すっきりする。

2020年8月10日月曜日

タイムスタンプを得る(Rust)

Rustをインストールしたので、練習してみる。 プログラムの出力結果に、実行日時を付加するときにタイムスタンプがほしいことがある。 ここでのタイムスタンプは、"2020/08/10 09:00:35"といった形に現在日時を示す文字列である。 それを得る関数timestamp_now()を書いてみようと思った。

Pythonでは、

import datetime
dt = datetime.datetime.now()
timestamp_now = "%04d/%02d/%02d %02d:%02d:%02d" % (
    dt.year, dt.month, dt.day, 
    dt.hour, dt.minute, dt.second)
と簡単に得られる。

Rustでは、chronoという外部クレートを用いることで簡単に得られるようである。 ここでは、chronoを使わずに、std::timeを用いてタイムスタンプを得る。 std::time::SystemTime::now()を用いて、現在時刻のUNIX_EPOCHタイムからの秒数を得て、 Fairfieldの公式を使って日数を勘定すればタイムスタンプが得られるだろうということである。

現在時刻とUNIX_EPOCHタイムとの差を得る関数は以下の通りでよいだろう。

fn now_from_unix_epoch() -> f64 {
    let now = std::time::SystemTime::now();
    now.duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs_f64()
}
次に、その秒数が何日に相当するかを評価する関数である。 Rustは、Pythonのように、気軽に複数の値を返せるようであるので、 のこりの秒数から時、分、秒を返すことにしてある。 tzは、タイムゾーンによる時間のズレを補正するために入れてある。 日本時間はUTCより9時間進んでいるので、9を入れて計算すると日本時間となる。
fn counts_day_from_sec(seconds:f64, tz:f64) -> (u32, u32, u32, f64) {
    const DAY_SEC:f64 = 60.0*60.0*24.0;
    const HOUR_SEC:f64 = 60.0*60.0;
    const MIN_SEC: f64 = 60.0;
    let days = ((seconds + tz * HOUR_SEC)/DAY_SEC).floor();
    let remain_sec = (seconds + tz * HOUR_SEC) - (days*DAY_SEC) as f64;
    let hour = (remain_sec/HOUR_SEC).floor();
    let remain_sec = remain_sec - (hour*HOUR_SEC) as f64;
    let min = (remain_sec/MIN_SEC).floor();
    let sec = remain_sec - (min*MIN_SEC) as f64;
    (days as u32, hour as u32, min as u32, sec) 
}    
得られた日数から日付を得るために、日付から日数を得るFairfiledの公式を使った関数を作る。 1月、2月の時は、年を1年戻して、それぞれ13月、14月として計算する必要がある。
fn daycounts_fairfield(year:u32, month:u32, day:u32) -> u32 {
    let mut y = year;
    let mut m = month;
    if month < 3 {
        y = year -1;
        m = month +12;
    }
    let day_before_the_year = 365 * (y-1);
    let day_of_leep_year = y/4-y/100+y/400;
    let day_in_the_year = 31 +28 +306*(m+1)/10-122 +day;
    (day_before_the_year + day_of_leep_year + day_in_the_year) as u32
}    
次に、この関数を用いて日付を探せばよい。年、月、日を返す。
fn days_to_date(days:u32) -> (u32, u32, u32) {
    let unix_epoch = daycounts_fairfield(1970, 1, 1);
    let mut year = 1970;
    while daycounts_fairfield(year, 1, 1) - unix_epoch < days {
        year = year + 1;
    }
    year = year -1;
    let mut month = 1;
    while daycounts_fairfield(year, month, 1) - unix_epoch < days {
        month = month + 1;
    }
    month = month -1;
    let diff_days = daycounts_fairfield(year, month, 1)-unix_epoch;
    
    (year, month, days-diff_days+1)
}
以上の関数を用いて、タイムスタンプを生成する関数は、こうなる。
fn timestamp_now() -> String {
    let unixtime = now_from_unix_epoch();

    let (days, hour, minute, second) = counts_day_from_sec(unixtime, 9.0);
    let (year, month, day) = days_to_date(days);
    format!("{:04}/{:02}/{:02} {:02}:{:02}:{:02}", 
            year, month, day, hour, minute, second.floor() as u32)
}      
最後は、メイン関数。
fn main() {
    let s = timestamp_now();
    println!("{}", s);
}
以上をtimestamp.rsというファイルに記述したら、
rustc timestamp.rs
でコンパイルでき、timestamp.exeが生成される。

Rustは、f64とu32の除算は定義されていないなど、 どの型で計算するかをしっかり考えておかないといけない。 ただ、コンパイラのエラーメッセージが非常に親切で、メッセージを読むのが楽しい。 また、定数は大文字にしなさいや、変数名をスネークケースにしなさいなど記述についても指示が出た。

2020年8月1日土曜日

Windows10 Rust(mingw)をインストール

Windows10にRustをインストールしてみた。 MSYS2を用いてインストールしたgcc環境があるので、 MSVC版ではなく,こちらを利用したい。

今は、rustupを利用してインストール管理をするようである。 Windows では、rustup-init.exeをダウンロードしてインストールするようだ。 Rustの公式ページのInstallからダウンロードできる。 64ビット環境なので、64-BITのrustup-init.exeをダウンロードし、実行した。

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
と尋ねられるので、(2)を選ぶ。 もしVisual Studioを使っているなら、defaultでよいのだろう。

Default host triple?と尋ねられるので、こちらで x86_64_pc-windows-gnu
を入力した。結局、

  
default host triple: x86_64-pc-windows-gnu
default toolchain: stable
profile: default
modify PATH variable: yes
として、再び最初のメニューが現れるので、
(1) Proceed with installation (default)
を選ぶと、無事にインストールされた。
ユーザーのホームフォルダの.cargoの中にインストールされた。

とりあえずfizzbuzzを書いて試してみる。

fn fizzbuzz(i:u32) -> String {
    if i%15==0 {
        "FizzBuzz".to_string()
    } else if i%3==0 {
        "Fizz".to_string()
    } else if i%5==0 {
        "Buzz".to_string()
    } else {
        i.to_string()
    }
}

fn main() {
    for i in 1..101 {
        println!("{}", fizzbuzz(i));
    }
}          
fizzbuzz.rsというファイルに保存したら、
rustc fizzbuzz.rs
でコンパイルリンクされfizzbuzz.exeが生成された。

2020年7月23日木曜日

Ptyhon3で以前のスクリプトを動かす(epsClip)

ドラッグアンドドロップを使った、 こちらで作成したepsファイルの バウンディングボックスを調整するスクリプトをPython3.8で動かしてみた。 新しい環境にGhostScript9.52 をインストールしたが、GhostView は使えなかったのでこちらのスクリプトを 利用する必要があるからである。 Python3.8になったためというより、wxPython や PIL が変更になったためにそのままでは動かなくなったところがある。 環境はWindows10 64bit、python3.8.3、wxPython4.1.0、PIL(pillow)7.1.2である。

まずは、
import Image
でエラーがでる。
これは、このインポートの仕方は廃止されたためで、以前に調べていた。
from PIL import Image
に修正する。

wxPythonでは、wx.EmptyBitmap、wx.EmptyImage、wx.BitmapFromImageが廃止されたようだ。 wx.EmptyBitmap()およびBitmapFromImage()は、どちらもBitmap()に書き換えればよかった。 wx.EmptyImage()は、wx.Image()で書き換える。

wx.Imageの持っていたtostring()メソッドは廃止されたので、tobytes()メソッドに書き換える。

FileDropTargetのOnDropFiles()ハンドラがbool値を返す必要があるようになったので、return True を加える。

文字列のコード変換などで、decode()を用いていたが、そのような必要はなくなった。 文字列は、utf-8となったのでバイト列にするencode()しかない。 また、バイト列には文字列にするためのdecode()メソッドがある。

もともとGUIのみのスクリプトでprintしていないので、print()関数にする修正は不要であった。 これらの修正をすると、問題なくpython3.8で動作した。

2020年7月22日水曜日

Python3.8でIDLEを使ってみた

Python3.8を使いだして、インストールされたIDLEを使ってみた。 Python2.7のときも、ときどき利用していたPythonコードをちょっと書いてみるのに便利な環境である。 JupyterやSpiderよりも手軽に軽快に利用できるので、使い始めるのはよいかと思う。

驚いたのはPython3.8でついてくるIDLEでは行番号の表示ができるようになっていたことである。 メニューからnew fileを選んでエディタを開くと、メニューのOptionsにShow Line Numbersがあり、 それを選ぶと行番号が表示される。 行番号表示をデフォルトにしたい場合は、 メニューのOption/Configure IDLEを選んで、GeneralタブにあるShow line numbers in new windows のチェックを入れればよい。

複数行をまとめてインデントをずらしたいときは、 行を選んでCtrl+[キー(インデントを減らす)とCtrl+]キー(インデントを増やす)でできる。

検索は、Ctrl+Fキーで検索用の窓が開くが、単語を選択したあとにCtrl+F3キーを押すとその単語を探してくれる。 でも検索を頻繁にするなら、慣れかもしれないがVimがやりやすい。

範囲をコメントにするにはAlt+3キー、コメントを解除するにはAlt+4キーである。

シェルのほうでも利用できるが、 TABキーを押すと登録されている単語から候補が表示され、Alt+/キーを押すとテキスト内の単語から候補が表示される。 また、Ctrl+0キーで対応するカッコを確認できる。

シェルでは、Alt+pキーとAlt+nキーで履歴を利用できる。画面でカーソルを移動させてEnterキーを押すとそこにあるテキストが入力行へと転写される。ブロックを用いたコードを実行したときなどに、ブロックごと転写されるため、再編集しやすい。ちょっと動作を確認したいときなどに便利である。タートルグラフィクスで遊ぶのにも便利だろう。

この程度操作ができればちょっとしたスクリプトを書くのに便利である。 なんといってもPythonをインストールするとすぐに利用できるのがよい。 Vimがインストールされていない環境でならIDLEをエディタとして使うのもよさそうである。

python3 wxPythonでドラッグアンドドロップ

これまでPython2.7でwxPythonを用いて、ファイルをドラッグアンドドロップで開くスクリプトを 書いて用いていた。Python3.8を使い始めて、改めてドラッグアンドドロップの部分を抜き出して、 動作を確認してみた。 環境は、Windows10 64bit, Python3.8.3, wxPython4.1.0。

tkinterにくらべて、ウィンドウ作成の部分に手間をかけているが、やっていることは以下の通り。

まず、wx.FileDropTargetを継承したクラスを作成して、 ファイルがドロップされた時に実行されるOnDropFiles()を 自分の望むように書き換える。

さらに、wx.PanelのSetDropTarget()メソッドを用いて、 作成したFileDropTargetを継承したクラスのインスタンスを設定するだけである。 実行すると、ウィンドウが開き、その中に配置されたパネルにファイルをドロップすると、 そのパスがコンソールに表示される。

import wx

class MyFileDropTarget(wx.FileDropTarget):
    def __init__(self):
        super(MyFileDropTarget, self).__init__()

    def OnDropFiles(self, x, y, filenames):
        for n in filenames:
            print(x, y, n)
        return True

class DropPanel(wx.Panel):
    def __init__(self, parent, *args, **kwargs):
        super(DropPanel, self).__init__(parent, *args, **kwargs)
        self.droptarget = MyFileDropTarget()
        self.SetDropTarget(self.droptarget)
        
class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(MyFrame, self).__init__(*args, **kwargs)
        
        self.panel = DropPanel(self, size=(300, 300))       
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel,  1, wx.EXPAND)
        self.SetSizer(sizer)
        self.Fit()

class MyApp(wx.App):
    def __init__(self):
        wx.App.__init__(self, False)

    def OnInit(self):
        self.frame = MyFrame(None, title=AppName)
        #self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

if __name__ == '__main__':
    AppName = "DnD Test"
    app = MyApp()
    app.MainLoop()  

2020年7月21日火曜日

Python3 tkinterでドラッグアンドドロップ

Pythonのtkinterでファイルをドロップしてそれを開くようなGUIをもったスクリプトを作るのは どうするのか調べてみた。 一つは、ctypes モジュールを用いて、WindowsのAPIを呼び出す方法がある。 もう一つは、TkDNDというTkの拡張ライブラリとそのPythonラッパーであるTkinterDnD2を用いる方法である。 ここでは、後者の方法を試してみた。 環境はWindows10、Python3.8.3、64bit環境である。

まずはこちらにいく。 TkinterDnD2のダウンロードができる。また、TkDND へのダウンロードリンクもある。 行うことは、Tkの拡張ライブラリであるTkDND2.8と、そのPythonラッパーであるTkinterDnD2をインストールすることである。

TkDNDのページへ行く。 64bit環境なら、 Windows Binaries/TkDND2.8の中にある tkdnd2.8-win32-x86_64.tar.gz をダウンロードする。

Python環境がインストールされているフォルダの中にtclというフォルダがある。 それぞれの環境ごとに場所が変わるので、 sys モジュールをインポートして、どこにpythonがインストールされているかを 調べてみるとよいだろう。 そのtklフォルダ中に、tkdnd2.8フォルダをそのまま配置する。

次に、こちらから TkinterDnD2-0.3.zipファイルをダウンロードする。 展開すると、TkinterDnD2フォルダといくつかのdemoファイルおよび 説明のhtmlファイルが得られる。 Pythonがインストールされているフォルダにある、 Lib\site-packagesの中にTkinterDnD2フォルダをそのまま配置すると完了である。

python からdemoを実行してみると動作を確認できる。 簡単なスクリプトは以下のものとなる。 エクスプローラからファイルを投げ込むと、そのファイルパスが表示される。 複数ファイルを投げ入れると、各ファイルパスがスペースで区切られたものとなる。

import sys
import os
import TkinterDnD2 as tkdnd
if sys.version > "3.0":
    import tkinter as tk
else:
    import Tkinter as tk

def drop_enter(event):
    event.widget.focus_force()
    return event.action

def drop_leave(event):
    event.widget._root().focus_force()
    return event.action

def drop_position(event):
    #print(event.x_root, event.y_root)
    return event.action

def drop(event):
    if event.data:
        print(event.data)
    return event.action

app = tkdnd.TkinterDnD.Tk()

app.drop_target_register(tkdnd.DND_FILES)
app.dnd_bind('<<DropEnter>>', drop_enter)
app.dnd_bind('<<DropLeave>>', drop_leave)
app.dnd_bind('<<DropPosition>>', drop_position)
app.dnd_bind('<<Drop>>', drop)

app.mainloop()

2020年7月18日土曜日

MacBook late 2008 にlubuntu20.04をインストールしてみた

MacBook5.1(2008 Late OSX10.5.8)が9年ぶりくらいに電源が入った。 いろいろ試して、最終的にDVDをくわえ込んだまま眠っていたのだが、 9年ぶりに電源が入りDVDを排出した。 OSは、10.5.8 Leopardだったのだが、ブラウザを使ってみるとセキュリティの関係か、 古いブラウザでは開けないページばかりである。 ブラウザを新しいバージョンにしようとしても、10.5.8をサポートしているものは見つからない。 この古いMacBookなら、10.6のSnow Leopardにアップグレードして、 OS X El Capitan までアップグレードしていく人が多いようである。 しかしながら、わざわざアップグレードしていっても、最新ではない。 古いPCなので、軽量のLinuxディストリビューションを入れて使うのがよさそうと考えた。

軽量のLinuxディストリビューションは、いくつもあって迷うところであるが、 多くのドライバが用意されていそうなUbuntuで軽量のディストリビューションということで、 Lubuntuにした。最新は、20.04であったので、そちらのISOをダウンロードし、 Etcherという起動USBを作成するソフトウェアで、Lubuntuインストール用のUSBキーを作成した。 MacOSとのデュアルブートやrEFindについてなど、検索するといろいろでてくるが、とりあえずUSBキーで起動してみた。 もうMacOSを利用するつもりもないので、シングルブートでインストールしてみる。 USBキーで起動して、普通にインストールするだけである。 パーティションは、MacOSのパーティションを削除し、デフォルト設定でインストールした。 インストーラーはわかりやすいので、迷うところはない。

インストール直後は、wifiのドライバがインストールされていないが、 このMacBookはEtherポートがあり、それが利用できたため、ネットに接続できた。 そのあとで、lubuntuのメニューから(設定>Additional Drivers)を開くと、 ドライバが探されてwifiとnVidiaのドライバをインストールすることができた。

日本語の入力は、
sudo apt install fcitx-mozc
sudo apt install font-noto font-noto-cjk-extra font-noto-color-emoji
その後、(設定>入力メソッド)でfcitxを有効にした。
日本語を入力するときは、Ctrl+Spaceを押すとMozcに切り替わった。

タッチパッドのタップでクリックしたいので、
/etc/X11/xorg.conf.d/30-touchpad.conf
を以下の内容で作成した。

Section "InputClass"
  Identifier "touchpad"
  Driver "libinput"
  MatchIsTouchpad "on"
  Option "Tapping" "on"
EndSection
指一本タップで左クリック、 二本タップで右クリック、 二本指でスワイプするとスクロールするようになった。 スワイプでのスクロールの向きは、 (設定>LXQt settings>キーボードとマウス>Mouse and Touchpad) でNatural scrollingをトグルすると変更できた。

ウィンドウタイトルなどに使われる文字のフォントサイズは、 (設定>LXQt settings>Openbox設定マネージャー) で行った。 デスクトップアイコンのラベルの大きさは、 (設定>LXQt settings>デスクトップ) で、ラベルテキストのフォントサイズで設定した。

Lubuntu 20.04では、python3.8.2がインストールされていた。 pipやtkinterは、aptを用いてインストールした。
sudo apt install python3-pip
sudo apt install python3-tk

ついでに、pythonで起動するように.bashrcに、
alias python='python3'
alias pip='pip3'
を追記した。

OSもブラウザも最新のものが利用できるようになり、 速度的にも軽快に利用できている。 サスペンドおよびサスペンドからの復帰はできた。 しかし、ハイバネーションは機能していない。 古いPCは、軽量LinuxのLiveUSBで起動してみて、 使えそうならLinuxで利用するのもよさそうである。

2020年6月29日月曜日

matplotlibでアニメーション(ライフゲーム)

ライフゲームを表示するスクリプトをmatplotlibのアニメーションで書いたものがこちら。 前のものは、FuncAnimation()へ引き渡す関数をlambda式で作成することで、 フレーム番号以外のパラメータを関数に持たせて受け渡す方法を用いましたが、 Pythonなら、クラスを用いるのもよいです。 クラスにすると、なにかパラメータを保持させていることが明確です。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation

def Rule(alive, neighbours):
    n = sum(neighbours)
    if not alive and n==3:
        return 1 
    if alive and n==2 or n==3:
        return 1 
    if alive and n<=1:
        return 0 
    if alive and n>=4:
        return 0 
    else:
        return alive

def LifeGameUpdate(world, rule):
    w, h = world.shape
    updated_world = np.zeros((w, h))
    for i in range(h):
        for j in range(w):
            im, ip = (i-1) % h, (i+1) % h
            jm, jp = (j-1) % w, (j+1) % w
            neighbours = (
                    world[im, jm], world[im, j], world[im, jp], 
                    world[i, jm], world[i, jp],
                    world[ip, jm], world[ip, j], world[ip, jp])
            updated_world[i, j] = rule(world[i, j], neighbours)
    return updated_world

class LifeGame(object):
    def __init__(self, world, rule):
        self.world = world
        self.rule = rule

    def __call__(self, frame_count):
        plt.cla()
        plt.imshow(self.world)
        self.world = LifeGameUpdate(self.world, self.rule)

if __name__ == '__main__':
    fig = plt.figure()
    world = np.round(np.random.random((100, 100)))
    lifegame = LifeGame(world, Rule)
    anim = matplotlib.animation.FuncAnimation(fig, lifegame, interval=10)
    plt.show()
            

2020年6月28日日曜日

matplotlibでアニメーション

ある画像データをパラメータの変化に合わせてアニメーションをさせながら表示させたい。 そのような場合のメモをしておく。

pcolormesh()を用いて画像データをプロットし、 その下にplot()を用いたパラメータの変化を示す。 さらに、pcolormesh()で表示されている場所をパラメータの変化のグラフ上に線で示す。 pcolormeth()に対するカラーバーの位置も制御したい。 レイアウトの自由度を持たせるために、add_axes()メソッドを用いている。

アニメーションは、

matplotlib.animation.ArtistAnimation
matplotlib.animation.FuncAnimation
のどちらかを用いて行える。

ArtistAnimationでは、各フレームのArtistオブジェクトを保存して、 Animationを作成する。 フレーム数が多くなる場合も考えると、FuncAnimationを利用したい。 ArtistAnimation, FuncAnimationとも、blitキーワードをTrueに設定すると、 指定した場所のみ書き換えるようになるため速く描画できると思われるが、 グラフ(Axes)の外は書き換えが行われなかったりと制約もできてしまう。 pcolormesh()の書き換えでは、軸の枠線がプロットに上書きされて消えてしまうようなことも起こる。 この場合は、blit=Falseを指定することをお勧めする。

MakeData()で、(nx, ny, nz)のshapeをもつデータを作成し、 各zに対する幅nx、高さnyのデータとみて、プロットをしてみる。

FuncAnimation()には、各フレームごとに呼び出される関数を渡す必要がある。 その関数には、フレーム番号を受け取る一つの引数が必要となる。 実際のupdate()関数では、関数内部で用いるfigure、 data、az とフレーム番号を受け取るように書きたい。 そうすると、引数が多くなってしまうので、lambdaを用いて、引数をフレーム番号のみに減らし、 各フレームごとに呼び出される関数としている。

最後のほうにある、

anim.save('anim.gif', writer='pillow')

のコメントを外せば、アニメーションGIFファイルとして保存できる。 writer='pillow'を指定するためにpillow(PIL)をインストールしておく必要がある。

モジュールのバージョン
python : 3.8.3
matplotlib : 3.2.2
numpy : 1.19.0
PIL(pillow): 7.1.2


import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np

def MakeData(nx, ny, nz):
    data = np.ones((nx, ny, nz))
    ax = np.linspace(-3, 3, nx)
    ay = np.linspace(-3, 3, ny)
    az = np.linspace(0, np.pi, nz)
    for k, z in enumerate(az):
        for j, y in enumerate(ay): 
            for i, x in enumerate(ax):
                data[i, j, k] = np.sin(x+z) * np.cos(y+z)
    return data, az

def update(fig, data, az, iz):
    fig.clear()
    # create axes
    ax0 = fig.add_axes([0.1, 0.4, 0.7, 0.5])
    cbar_ax0 = fig.add_axes([0.82, 0.4, 0.05, 0.5])
    ax1 = fig.add_axes([0.1, 0.1, 0.7, 0.2])
    
    # plot data
    im0 = ax0.pcolormesh(data[:, :, iz].T)
    fig.colorbar(im0, cax=cbar_ax0)
    ax1.plot(az, '-', color='#0000FF')
    ax1.plot([iz, iz], [0, np.pi], '-', color='#FF0000')

    # put text labels
    label = ax1.text(0.1, 0.8, '%.5g (rad)' % az[iz], 
            transform=ax1.transAxes)
    ax0.set_title('Frame %d' % iz)


nx, ny, nz = 30, 60, 100
data, az = MakeData(nx, ny, nz)

fig = plt.figure(figsize=(5, 6))

anim = FuncAnimation(fig, lambda iz: update(fig, data, az, iz), nz, 
        interval=100, blit=False, repeat=False)

#anim.save('anim.gif', writer='pillow')

plt.show() 

2019年7月6日土曜日

USBを使って制御できるリレーモジュールをpythonで動かしてみる

Amazonで検索すると、QuimatリレーモジュールQY15なるものが見つかったので購入してみました。 4個のリレーSRD-05VDC-SL-CをUSBからの出力でON/OFFできるようです。 FT245RLというUSB-パラレル変換のチップが搭載されているようです

カスタマーレビューを見ると、 FTDI社で提供しているライブラリを使い、 BitBangモードにしてバイトデータを書き出せばよいと いうことで、Pythonからも簡単に制御できそうです。

梱包はいたって簡単で、本体とUSBケーブルが1本ついてきました。 それ以外はなにもありません。 USBポートにQY15を接続してみます。 すると、パチッとリレーの動作音がして、LEDが光ります。 デバイスマネージャを見ると、ポートの個所にUSB Serial Portとして認識されていました。

次はソフトウェアです。
Windows10 64bit
Python 2.7 64bit
で試します。

ここ(https://www.ftdichip.com/index.html)のDriversから、 D2XXのドライバーをダウンロードします。 新しいドライバがあるようですが、D2XXは別のページみたいです。 "for D2XX drivers please click here" に従うと、D2XX Driversのダウンロードページに行けます。 試す環境はWindows用64bitなので、それに合わせて64bit版をダウンロードします。 ダウンロードされたCDM v2.12.28 WHQL Certified.zipを展開すると、 中には、ftd2xx.hというヘッダファイル、さらに64bit,32bit版のライブラリが入っていました。 使うのは、64bit環境なのでamd64のフォルダにある、ftd2xx64.dllです。

Pythonは、ctypesモジュールを使ってDLLの関数を呼び出せます。 関数の呼び出し方は、ヘッダファイルftd2xx.hを見ると分かります。 ftd2xx64.dllと同じフォルダーで、Pythonのスクリプトを書いていきます。

まず、ctypesとtimeモジュールをインポートします。 timeモジュールは、動作テストさせるときの待ち時間を作るのに使います。

import ctypes
import time

ctypesモジュールで、DLLファイルをロードします。

dll = ctypes.CDLL('ftd2xx64.dll')

これでDLLを使う準備ができました。

次に、DLLで提供されているFT_Open()関数を使って、制御するためのハンドルを得ます。 ハンドルはポインタですので、ハンドルを格納するポインタ型の領域を確保します。

handle = ctypes.c_void_p()

それでは、FT_Open()関数をコールします。

res=dll.FT_Open(0, ctypes.pointer(handle))

第1引数は、デバイス番号で0を渡します。 第2引数は、結果として得られるハンドルを格納する場所(ポインタを格納する場所へのポインタ)を渡します。 返り値resが0なら、OKです。そうでなければどこか問題があります。

次にボーレート(通信速度)を設定します。

res=dll.FT_SetBaudRate(handle, 921600)

として、921600bpsを設定します。

次に、BitBangモードに設定します。

res=dll.FT_SetBitMode(handle, 0xFF, 1)

第2引数は、バイトデータに含まれる各ビットの入力出力方向を指定するマスクで、すべて出力(1)に設定しています。 第3引数に1を設定することで、BitBangモードを指定します。

あとは、FT_Write()関数でデータを書き出せば、値に応じてリレーがON/OFFするはずです。 各リレーは、1から4ビットに対応していて、各ビットの重みは1、2、4、8になりますので、 3番目のリレーをONにしたければ、4を書き出し、 1番目と3番目のリレーをONにしたければ、1+4=5を書き出せばよいわけです。 そのまえに、FT_Write()関数を使って1バイトデータを書き出す手続きを関数にしておきましょう。

def write_byte(dll, hndl, byte):
    buff = ctypes.c_byte(byte)
    written = ctypes.c_ulong(0)
    return dll.FT_Write(hndl, ctypes.pointer(buff), 1, ctypes.pointer(written))

dll、hndlおよび書き出す1バイトデータを受け取ります。 1バイトデータを格納した、長さ1のバイト型の領域buffを確保します。 書き出されたバイト数を格納するための、領域writtenを確保します。 これら確保した場所へのポインタを、FT_Write()関数に渡して呼び出します。 FT_Write()関数の3番目の引数は、バッファの大きさですので、1を渡します。 この関数を用いて、1番目と3番目のリレーをONにするデータを書き出します。

res = write_byte(dll, handle, 5)

すこし時間を空けて、リレーをOFFにするデータを書き出します。

time.sleep(2)
res = write_byte(dll, handle, 0)

最後に、FT_Close()関数でハンドルを閉じます。

res = dll.FT_Close(handle)

ここまでをまとめて載せます。

import ctypes
import time

def write_byte(dll, hndl, byte):
    data = ctypes.c_byte(byte)
    written = ctypes.c_ulong(0)
    return dll.FT_Write(hndl, 
            ctypes.pointer(data), 1, ctypes.pointer(written)) 

dllfile='ftd2xx64.dll'
dll = ctypes.CDLL(dllfile)
handle = ctypes.c_void_p()
res = dll.FT_Open(0, ctypes.pointer(handle))
res = dll.FT_SetBaudRate(handle, 921600) # 921600 bps 
res = dll.FT_SetBitMode(handle, 0xFF, 1) # All output and bit bang mode 
res = write_byte(dll, handle, 5) 
time.sleep(2)
res = write_byte(dll, handle, 0)
res = dll.FT_Close(handle)         

ftd2xx.dllを扱うPythonのモジュールはあるようですが、 このリレーモジュールを動かす程度なら、自分で書いてしまうほうが楽かもしれません。