IntelliJ IDEA のキーボードショートカットをイラストマーキングした

キーボードショートカットはきちんと慣れたほうが作業効率が上がるのだが、英語とまじってなかなか覚えきれない歳なので、機械翻訳したのだが、絵も欲しいなと思って暇々に勉強がてら絵を起こそうと思った。でも数量が半端ないのでこれも機械処理せねば話にならないと判断してコードを書き始める(どこかに書いたと思うが「同じ作業を50回以上繰り返す必要があるときは人手だと絶対間違うし非効率なので自動化を考えるべき」が自身の最近のポリシー)。

GIMPのスクリプトは1回仕事での作業都合でゴリゴリにスクリプトを作ったことがあったので、少し調べたら最近はPythonでも書けるとのこと。pythonもあまりきちんと勉強していなかったので勉強がてらにごそごそ書いたが、この手のツールはエラーメッセージが簡易なのでどこで間違えたのかがまったく分からない。

と思いつつも、プログラム制御での簡易画像処理はTipsを理解しておくと後々役立つと思って暇々に作成してたものがなんとか動いたので、GitHubに上げる。

https://github.com/mfukushim/MakeKeyboardShortcutIIllustration

生成画像もまとめてアップするので、暗記カードみたいに使いたい人はどうぞ。

変換しやすかった主なものの128枚画像

追記小修正した。

[ Python , Gimp ] pgimp (Python-fuを少し使いやすくするツール) を Windows で使う

最近やはり記憶力が落ちたと思って、ふと思い立って キーボード図付きの IntelliJのショートカットの一覧を作りたいと思った。山ほどあるショートカットの図をいちいち作っていたのでは手間だし、今風の効率化ではないので半自動でプログラムで起こしたいと思った。

そこで昔少し案件でGIMPのスクリプト(Script-fu)で簡易画像ツールを作ったことがあるのだが最近のGIMPを調べたら、Pythonで操作できる時代になったと書いてあった(Python-fu)

ああ、確かにScript-fuはとんでもなく正体不明な言語(今見るとLisp似なのか?)だったのを思い出して、今風言語で書けるとはこれはいいやと思って触りはじめた。

実は私はPythonはあまりきちんと理解しておらず(AI寄り言語だというのに初心者本を斜め読みしたくらい)この際Pythonの勉強もかねて勉強してみようと思った。

http://gimpbook.com/scripting/
https://www.gimp.org/docs/python/
https://developer.gimp.org/api/2.0/
https://qiita.com/dhomma/items/f33ecd1c4d4c6ece7685

とりあえずHelloWorld的なPython-fuのコードをコピペして動かしながら検討していたら、これは IDEで編集支援して欲しいな、と思った。GIMP側のソースか何かでドキュメント付きDLLを作ればよいのではないかと思ったがそこまでPythonに明るくないし、だいたい最近欲しいと思った機能は誰かが作っている時代なので検索したら、やっぱり pgimp というライブラリがあった。

https://github.com/mabu-github/pgimp

が、これがWindowsに対応していない と出たのでどうにか使えるようにした話を以下に記します(前置きが長い)

まず windows上でpython2,python3環境を作った状態でpipでpgimpのインストールを試みたが確かにエラーが出て進まない。いろいろ考えた末に見つけた解決策は

  • WindowsのWSL でUbuntu Linuxを内部実行してそこからpgimpを動かすというのが一番悩まずに済むと判断。
  1. Windows WSL のUbuntuを導入
  2. WSL Ubuntuにpython2,python3を設定
  3. Ubuntuにpgimpを導入
  4. IntelliJ IDEA からWSL Ubuntu経由でのpgimpを実行
  • Ubuntuにpgimpを導入
    https://pypi.org/project/pgimp/
    取り込み前に setuptools と psutil が必要とのことでpython3側に
    pip install setuptools
    pip install psutil
    を取り込み。
    GIMPはWSLで走らせる必要がありWSL側にもGIMPをインストールする必要があるとのこと。
    pip2 install numpy
    pip2 install typing
    sudo apt-get install gimp 
    sudo apt-get install xvfb
    ここまできてやっとでエラーなしで
    pip3 install pgimp
    が通った。
  • IntelliJ でWSL経由でpgimpの実行プロジェクトを起こす
    WSL経由でのpgimp実行ってもっと手間取るかと思ったら、すでにWSL設定の機能が標準であり、全然単純であった。

このあたり https://pgimp.readthedocs.io/en/latest/tutorial.html#create-an-image-and-export-it のサンプルコードも問題なく実行/シンボリックデバッグできて解決。IDE側の文脈支援やヘルプも動作する。ここまで来るまでの試行錯誤が面倒だった。。。ただColor等の細かい定義まで取り込むことは出来ていないようで、残念なことにPythonから直にGIMPを操作する分には基本的な生データしか処理出来ないようだ。主な目的はIDEの補完入力を手伝うみたいな方向と思われるが当初の目標は満たせるか。

環境の構築に気力を使うのは今の時代のやり方ではない。WSLでシンプルに解決出来るならそれが何よりだ。

[STS/Eclipse] Missing JDK “The JRE you are running Eclipse with appears to not be a JDK.”

すっかり何でも屋なので、昔関わった案件でSTSを設定しなおすが、Missing JDK と言われた。各案件の作業都合でJDK環境を切り替えねばならぬ。

https://codeday.me/jp/qa/20181208/52905.html を参考に、 SpringToolSuite4.ini に -vm を書き足すのが楽かな。sdkman を使って切り替える方法も教えてもらったが、Windows上ではちょち設定が冗長になりそうなのでこれで回避。

[Scala] Akkaの単体テストはwith ImplicitSenderを忘れやすい

普段Akkaをごりごり使う訳ではないので、こういうTips的なことはすっかり忘れてひとしきり悩む。

package my_test

import akka.actor.{Actor, ActorRef}
import my_test.CalculatorActor.Add

class CalculatorActor extends Actor {
  override def receive: Receive = {
    case Add(val1,val2) =>{
      val ret = val1+val2
      sender() ! ret  // 計算結果を返して
    }
  }
}

object CalculatorActor {
  case class Add(val1:Int,val2:Int)
}
package my_test

import java.util.concurrent.TimeUnit
import akka.actor.{ActorSystem, Props}
import akka.routing.RoundRobinPool
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.{BeforeAndAfterAll, fixture}
import scala.concurrent.duration.Duration

class CalculatorSpec extends TestKit(ActorSystem("testBatch")) with fixture.FreeSpecLike
  with fixture.TestDataFixture with BeforeAndAfterAll {
  "計算機" - {
    "加算" in { td => {
      val actor = system.actorOf(Props[CalculatorActor].withRouter(RoundRobinPool(2)))
      actor ! CalculatorActor.Add(1, 2)  // 1+2を計算機に送って
      val ret = receiveOne(Duration(10, TimeUnit.SECONDS))
      assert(ret == 3,"計算エラー")  //  結果が3かを確認する
      println(ret)
    }
    }
  }

  override protected def afterAll() {
    super.afterAll()
    Thread.sleep(30 * 1000)
    TestKit.shutdownActorSystem(system, Duration(10, TimeUnit.MINUTES))
  }
}

「1+2を計算機に送って、結果をもらって3かどうか確認する」はこれでよいだろう。と思っていると

[INFO] [11/28/2019 23:10:18.160] [testBatch-akka.actor.default-dispatcher-4] [akka://testBatch/deadLetters] Message [java.lang.Integer] from Actor[akka://testBatch/user/$a/$a#-158423933] to Actor[akka://testBatch/deadLetters] was not delivered. [1] dead letters encountered. If this is not an expected behavior, then [Actor[akka://testBatch/deadLetters]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

でDead Letterになって届かずエラー。

ひとしきり悩んで本を読み直すと 単に

class CalculatorSpec extends TestKit(ActorSystem(“testBatch”)) with fixture.FreeSpecLike with fixture.TestDataFixture with ImplicitSender with BeforeAndAfterAll {

単に with ImplicitSender が足りないだけだった。。。

見えにくい約束事が多いのは望ましくはないものの、昔と比べれば全然訳のわからない約束事は減ったので今のほうが全然よい。Akkaなんて15年前にあったら本当にありがたいと思うし。というものの少なくなって欲しいとは思う。

関係ないが ScalaTest は FreeSpec が日本人向きだと思うけどなー。変な擬似英語に悩まずに済むし、表形式の結果に加工しやすいので表好きな日本人向きだと思うし。

[Scala] scalaのBigDecimalをjava.text.DecimalFormatで変換するとずれることがある

なんかまだ合点がいってないのだが、細かい桁計算が必要でscalaのBigDecimalを使っていて、それを文字列で打ち出す必要が出た際に、javaのDecimalFormatを使ったら出力文字列が合致しなかった。

val a = "4.95"
val b = "0.000000000000000001"

val bigA = BigDecimal(a)
val bigB = BigDecimal(b)

val bigC = bigA+bigB

bigC.toString() // シンプルにtoStringにしたほうが正しい文字列になった

val fm = new java.text.DecimalFormat("0.###################")

println(fm.format(bigA))
println(fm.format(bigB))
println(fm.format(bigC)) // bigA+bigB に文字列一致しない

val fm2 = new java.text.DecimalFormat("0.0000000000000000000")

println(fm2.format(bigA))
println(fm2.format(bigB))
println(fm2.format(bigC)) // bigA+bigB に文字列一致しない

REPL出力

a: String = 4.95
b: String = 0.000000000000000001

biga: scala.math.BigDecimal = 4.95
bigb: scala.math.BigDecimal = 1E-18

bigc: scala.math.BigDecimal = 4.950000000000000001

res0: String = 4.950000000000000001
// シンプルにtoStringにしたほうが正しい文字列になった

fm: java.text.DecimalFormat = java.text.DecimalFormat@674dc

4.95
0.000000000000000001
4.95  // bigA+bigB に文字列一致しない

fm2: java.text.DecimalFormat = java.text.DecimalFormat@674dc

4.9500000000000000000
0.0000000000000000010
4.9500000000000000000  // bigA+bigB に文字列一致しない

ネットで調べて scalaのBigDecimalとjavaのBigDecimalがちょっと違うよ(MathContext.UNLIMITED設定関係)って話は出てきたが、DecimalFormatが違うって話はよくわからんかった。

[Apache poi]そろそろXSSFがデフォルトでよいんじゃないかな

賛否はあるもののお客さんからはExcel等でドキュメントを要求されることが多いことも事実。なのでApache poi(Apacheで出しているMS Excel.Word等MS文書をJavaで操作するライブラリ)などでドキュメント自動生成みたいなところを結局最近やっているのだが、Excel操作ライブラリはXSSF系APIとHSSF系APIに分かれている。

HSSFは拡張子xlsの旧形式、XSSFは拡張子xlsxの今の形式用のAPI。Excelも長いのでAPIが2系統に分かれているのは仕方ないと思うものの、そろそろXSSFがデフォルトでよいんじゃないかなぁ。

Workbook wb = WorkbookFactory.create("file.xslx")
↓
XSSFWorkbook wb = (XSSFWorkbook)WorkbookFactory.create("file.xslx")
または
XSSFWorkbook wb = new XSSFWorkbook("file.xslx")

IDEのコンテキストメニューでAPIがさくっと出ないし、ネットの情報でもHSSFの情報と混じって小一時間悩むし。

[sbt]あまり複雑なことを考えずに機能を追加する

正直まだsbtは理解できていない。まぁ依存性管理ツールはmakeにしろMSbuildにせよ複雑すぎて中をきちんと使いこなすとかできてなかったし。出来合スクリプトで困らんし。

なので専門家からするとまずいのかもしれないが、とりあえず既存のビルドの動きに何かを付け足すくらいの話向け。

  1. ちょこっと作業をまとめたバッチを作りたいだけなのよ→コマンドを使う
    https://www.scala-sbt.org/1.x/docs/ja/Howto-Sequence-using-Commands.html
build.sbt

commands += Command.command("batchtest") { state =>
  "testOnly somepackage.ValidationSpec > list.txt" ::
  "testOnly somepackage.ExecSpec >> list.txt" ::
    state
}
// commandsの使い方はよいがここでリダイレクトは使えないな。。。

2. タスクを作りたいのよ、タスクの前後にラベルを出したいのよ→ 単純なタスク作成
https://qiita.com/mtn81/items/ce482ed16a19f770cc68

3. sbtから外部コマンドを呼びたいのよ→ shell起動
https://www.scala-sbt.org/release/docs/Process.html

lazy val batch = taskKey[Unit]("外部バッチ")
batch in Test := {"powershell.exe ls " !}

4. 依存関係は悩まずにタスクからタスクを呼びたいのよ→ runTask, runInputTask
https://stackoverflow.com/questions/32142055/how-to-call-sbt-inputtask-dynamically

build.sbt

lazy val batch2 = taskKey[Unit]("ドキュメント生成呼び")
batch2 in Test := (doc in Test).value

lazy val batch3 = taskKey[Unit]("テスト1つだけ呼び")
batch3 in Test := (testOnly in Test).toTask(" somepackage.ValidationSpec").value
(↑ toTask(" の部分のスペースが重要)

5. 依存関係は悩まずにタスクでタスクを連続実行させたいのよ→Def.sequential
https://www.scala-sbt.org/1.x/docs/ja/Howto-Sequential-Task.html

build.sbt

lazy val batch4 = taskKey[Unit]("タスク連続")
batch4 in Test := {
  Def.sequential(
    Def.task {
      "powershell.exe date" !
    } ,
    Def.task {
      (testOnly in Test).toTask(" somepackage.ValidationSpec").value
  ).value
}

6. プロジェクトにREPL代わりに書いたAppコードをsbtから呼びたいのよ→runMain

Hello.scala

package tools

object Hello extends App {
   println("Hello")
}
build.sbt

lazy val batch5 = taskKey[Unit]("内部ツール呼び出し")
batch5 in Test := (runMain in Test).toTask(" tools.Hello").value
(↑ toTask(" の部分のスペースが重要)

[ScalaTest] sbt内でタイムスタンプが使いたい

ログの書き出しなどでsbtで書き出しファイルにタイムスタンプを付けたいとか。

build.sbt

testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest,"-fWDT", s"log${java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))}.txt" )

調べたものの結局は使わなかったのだが。。。sbtはまだ勘所がよくつかめぬ。