投稿

12月, 2020の投稿を表示しています

Inkscape on macOS Big Sur

イメージ
迂闊にも macOS Big Sur にアップデートしてしまって、元からインストールしていた MacPorts 版 Inkscape が使えなくなっていることに気付いた。改めてインストールし直そうとしたが…… sudo port install inkscape poppler のビルドエラー しかし、poppler のビルドの失敗で先に進めなくなった。結構、アチコチで報告されている現象のようだ。 poppler のエラーについては、 #61901 poppler 20.12.1: Failed to build (macOS 11.1 Big Sur) の報告がドンピシャで、正解に辿り着けた。 sudo port -f uninstall cctools sudo port -v install cctools +xcode cctools のデフォルトでのインストールの仕方に問題があるらしく、一旦アンインストールして、+xcode オプションを付けてインストールし直せばよい。これで poppler のビルドが通るので、inkscape のインストールがその先に進み、インストールできた。 ただし、注意点があり、従来のように inkscape コマンドから X11 が自動で立ち上がらないので、先に手動で X11 を立ち上げた状態で inkscape コマンドを実行する必要がある。

GIMP on macOS Big Sur

イメージ
迂闊にも macOS Big Sur にアップデートしてしまって、元からインストールしていた GIMP が重くて使い物にならないことに気付いた( GIMP 公式 でも認識されている問題)。改めて最新版をダウンロードしてきてインストールしても改善せず。 そこで代わりに MacPorts 版の GIMP をインストールしようと思いついた。 sudo port install gimp poppler のビルドエラー しかし、poppler のビルドの失敗で先に進めなくなった。結構、アチコチで報告されている現象のようだ。 poppler のエラーについては、 #61901 poppler 20.12.1: Failed to build (macOS 11.1 Big Sur) の報告がドンピシャで、正解に辿り着けた。 sudo port -f uninstall cctools sudo port -v install cctools +xcode cctools のデフォルトでのインストールの仕方に問題があるらしく、一旦アンインストールして、+xcode オプションを付けてインストールし直せばよい。これで poppler のビルドが通るので、GIMP のインストールがその先に進むようになる、 MacPorts 版だからなのかはわからないが、これは X11 アプリとして動く。そのため、macOS の UI との連携性はイマイチで、フォルダー(Documents / Downloads / Desktop が駄目。Pictures なら ok)を開いたりすることができない。一応、画像ファイルから開くアプリを選んで GIMP を立ち上げることはできるが、一旦立ち上がった GIMP からファイルを開くことができないので、2 枚目以降の画像をレイヤーで開いて合わせて利用するようなことができない。究極的には、公式版の Big Sur 対応の改善を待つしかない。

Model-ViewModel-View の最近のイメージ

イメージ
以前にも MVVM について 記事にしたことがあった が、最近のイメージについて整理してみる。 基本的には、Model-ViewModel-View の 3 段構成である点については前の記事の通りなのだが、それぞれの部分において、抽象的ではなくより具体的な性質の違いが見えてきた。絵にすると次のような感じ: Model Model 部分はミキサーに材料を入れてジュースにするようなイメージで、最終的にひとつの結果の出力に到る。Perl や Python 等で手続志向のコンソールプログラミングをする時のようなイメージ。Web スクレイピング等で、データを加工するその複雑な処理過程をプログラミングすることになるが、時間の流れとしては一方向で、またデータオブジェクトも、成果物としては一つである(ミキサーの絵のように、入力される側の要素は、複数でも構わない)。 流れ的には一直線であるが、データの加工工程そのものに重要性がある。 ViewModel ViewModel 部分は時間の流れ的には一方向ではあるものの、Model に生じた更新を伝播させて複数の View 用の LiveData オブジェクトに帰着させる流れを定義する。ここでは伝播が分岐したりし、さらに、オブジェクト同士の依存関係から、前後関係に留意しなければならないので、Model 部分よりは安易ではない。 例えば、View 側にさらすための LiveData オブジェクトが、あるリストとインデックスとそこからリストとインデックスに依存して取り出す一つの要素だったとする。何も考えずにバラバラにそれぞれを LiveData として定義しただけだと、インデックスが最後尾にあった場合に、リストが伸び縮みすると、要素を取り出す時に IndexOutOfBound エラーを引き起してしまう。そのため、まずはリストを更新し、インデックスが新しいリストのサイズに対して問題ないかをチェックして必要な場合は修正し、それからリストとインデックスに従って要素を取り出すという順序になるようにコードを記述する必要がある。つまり、1) リスト 2) インデックス 3) 取り出す要素 という 3 者の先後は必ず守らなければならないものであることがわかる。 View ViewModel で非同期的な部分は吸収できてい

Material Slider 試用

イメージ
いつの間にやら、Material コンポーネントに Slider が追加されていた。以前は、Material デザインの公式サイトにコンセプトとしてのみ掲げられていて、実装が存在しておらず、結局 GitHub で適当なサードパーティライブラリーを探してきて利用するしかなかったが、今や Google 純正の Slider があるのなら、これを使うに越したことはないと思う。早速、使い勝手を見るために、10 分そこらでサンプルを作って試してみた。 サンプルアプリ build.gradle (:app) には依存関係として implementation 'com.google.android.material:material:1.2.1' を記述している。 値の実体としては 0 ~ 1.0f で受け取っており、TextView にはその値をそのまま反映している。吹き出しに表示されるラベルは値をそのまま使用せず、LabelFormatter で値を変換して 0 ~ 100 のインデックスとして表示している。 MainActivity.Java package com.scaredeer.materialslider; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import com.google.android.material.slider.Slider; public class MainActivity extends AppCompatActivity { private TextView textView; private static final int MAX_INDEX = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.lay

LiveData の observer 側の onChanged() がトリガーされるタイミング

source に対する setValue() がトリガーとなる 漠然とサンプルを参考にして LiveData を使い始めてきたので、複数の LiveData の処理がされるタイミングの前後関係が意図した通りにならずに困ってしまった。 MediatorLiveData や Transformations.map / Transformations.switchMap では lambda 表現で onChanged() を定義するが、この onChanged() の処理が、監視先の source の変更よりも先になってしまう現象が発生し、頭を抱えてしまった。 実際のところ、MediatorLiveData や Transformations.* で潜在的に行っていることにおいて、MutableLiveData が使われており、本来的に、source の変更が observer 側に伝えられるタイミングは、setValue() / postValue() であると明記されている。 いずれの場合でも、setValue() または postValue() を呼び出すことによってオブザーバーがトリガーされ、UI が更新されます。 公式ガイド「 LiveData オブジェクトを更新する 」 つまり、Transformations.* では source 側 onChanged() の末尾に隠蔽されているので曖昧に理解してしまっていたが、MediatorLiveData の onChanged() の場合はその中で source 自身が setValue() / postValue() された直後に、observer 側の onChanged() が実行されることになる。そのため、source 側が MediatorLiveData の場合に、必ずしも source 側の変更が先にならないといった現象が発生していたのである。「source が先、observer が後」ではなく、「source の setValue() / postValue() が先、observer の onChanged() が後」というのが正確だったわけである。 active でないと observe 自体が有効にならない もう一つハマったのが、source が更新されてい

LiveData と MutableLiveData の使い分け

イメージ
observe 関係の場合は、できるだけ ViewModel と View(Activity 含む)の関係を疎にして、一方通行の関係がいいと思う。つまり、ViewModel 内部では MutableLiveData 扱いのデータを LiveData にして外側、つまり View 側に対してさらす形である。 private MutableLiveData<T> mData; public LiveData<T> getData() { return mData; } 一方、user action を ViewModel に入力する場合はどうするのがいいか? この場合は、user action の影響を受ける LiveData は、View/Activity からの入力で変化することが当然であるから、上の observe の場合のように MutableLiveData を getter で隠蔽することは冗長である。なので、public な MutableLiveData を getter を介さずに直接さらしてしまえばいいと思う。 public MutableLiveData<T> mData; そうして、当該 MutableLiveData に関する処理を、無理矢理 ViewModel に置かずに、素直に、View/Activity 内で処理を記述するのが、 実は 適切だと思う。 というのも、user action によって処理を分けたいのは、View/Activity 側の都合であって、ViewModel 側の場合ではないケースが考えられるからである。通常、user action を受け付けるのは、Activity が active すなわち、onResume から onPause の間のライフサイクルにおいてである。なので、その間のみ処理を行って、それ以外のタイミングでは処理を停止しているような形にするのが望ましい。そうなると、それらの処理の主となるコードは、Activity 側の各ライフサイクルに応じたメソッドに配置して、処理の結果変更を受ける変数のみ、MutableLiveData として ViewModel 内に所属させておくのがストレートなコード表現になるわけである。 サンプ

QNAP NAS の RAID 1 ミラーディスクの追加エラー

イメージ
QNAP の NAS(2 ドライブ)を RAID 1(ミラーリング)で運用している。HDD は WD Red 6TB × 2。この NAS が夏の停電時に、古い方のドライブ 1 の HDD でエラーが発生し、ドライブ 2 のみの片肺運転となっていた。 QNAP の GUI 上で操作しても状況が変化しないので、一旦、ドライブ 1 を取り外し、外付け HDD ケースに入れ、Windows PC に接続し、パーティションを全て削除して、単一パーティションを作り、NTFS で完全フォーマットをし直した(完全フォーマットなので丸一日を要した)。Windows 上で S.M.A.R.T. 情報を見る限り、問題は発生していないようである。 QNAP に HDD を戻したが、個別情報でドライブ 1 はステータス「準備完了」、S.M.A.R.T. 情報「良好」となっているものの、論理ボリューム(RAID 1 のミラーリングディスク)としては追加されずに、エラー(Add drive 1 to the volume failed)となった。 HDD を再度 Windows PC に戻して、Windows 上で パーティションを削除してパーティションが存在しない状態にしてから 、QNAP に HDD を戻した。すると今回は、ミラーリングに追加され、フォーマット処理が無事開始した。 QNAP でミラーリング(フォーマット処理含む)が終了後、S.M.A.R.T. の完全テストも行ったが、何のエラーも検出されなかった。元のエラーは何だったのだろうと思うが、多分、QNAP の OS が馬鹿だっただけだろう。 以上、QNAP に HDD を追加する・し直す場合には、追加する HDD はパーティションを削除した状態で追加しなければならないという話. 余談 1:WD Red WD Red シリーズは元々は CMR 方式だったのだが、後に RAID に不向きな SMR に変更されてしまった( 参考 )。単に Red だからと安易に信用せず、CMR 方式の型番であることを確認して購入した方がよい。 WD 公式ブログのリスト によると、2TB 〜 6TB の EFAX が SMR なので、買ってはいけない型番。一方、8TB 〜 14TB の EFAX や旧型番の EFRX は