[Java]106 メインクラスのクラス図作成

[M1 Mac, Big Sur 11.6.5, VSCode 1.67.1]

自製カラーアプリのリファクタリングに際し、ソースコードの全体像を把握しやすくするため、クラス図の作成に着手しました。

VSCodeにPlantUMLを導入して使用しています。まずはメインクラスのクラス図を作成しました。入れ子になっているクラスは親クラスの外に出して記述し、+–で入れ子であることを表現します。

私にとっては十分複雑な作りになっていることを再認識しました。今までクラス図なしでよくメンテナンスできたものだと思います。

@startuml
package base {
    +class ColorSampleJP66 {
        +{static} gui : JFrame
        +{static} tabbedpane : JTabbedPaneEx
        +{static} tab1 : JPanel
        +{static} tab2 : JPanel
        +{static} tab3 : JPanel
        +{static} tab4 : JPanel
        +{static} tab5 : JPanel
        +{static} tab6 : JPanel
        +{static} layout : BorderLayout
        +{static} default_font : Font
        +{static} home : String
        -locale_lang : ResourceBundle

        -ColorSampleJP66() : void
        +{static} main(String[] args) : void
        +{static} setLAF() : void
        +LAF_OS() : String[][]
        +{static} size(String key) : int
        +{static} makeDir() : void

    }
    +class Tab1 {
        -Tab1(String LAF0) : void
    }
    +class Tab2 {
        -Tab2(String LAF0) : void
    }
    +class Tab3 {
        -Tab3(String LAF0) : void
    }
    +class Tab4 {
        -Tab4(String LAF0) : void
    }
    +class Tab5 {
        -Tab5(String LAF0) : void
    }
    +class Tab6 {
        -Tab6(String LAF0) : void
    }
    +class MyWindowsListener {
        +windowClosing(WindowEvent e) : void
    }

    ColorSampleJP66 +-- Tab1
    ColorSampleJP66 +-- Tab2
    ColorSampleJP66 +-- Tab3
    ColorSampleJP66 +-- Tab4
    ColorSampleJP66 +-- Tab5
    ColorSampleJP66 +-- Tab6
    ColorSampleJP66 +-- MyWindowsListener
    
}
@enduml

[Java]105 VSCodeでPlantUMLによるクラス図作成

[M1 Mac, Big Sur 11.6.5, VSCode 1.67.1]

自製カラーアプリをリファクタリングするにあたり、コードの内容をクラス図で整理することにしました。

HomebrewからPlantUMLとGraphviz(クラス図作成に必要)をインストールしました。

brew install graphviz
brew install plantuml

VSCodeのsettings.jsonに以下を追記し、Graphvizのdotファイルをパス設定等します。

"plantuml.commandArgs": [
        "-DGRAPHVIZ_DOT=/opt/homebrew/Cellar/graphviz/3.0.0/bin/dot",
        "-Xmx2g",
        "-DPLANTUML_LIMIT_SIZE=16384",
    ]

とりあえずネットの作例から簡単なクラス図を表示してみました。

[C++] 55 FLTK : 画像加工アプリ / iOSアプリ用iconset作成機能の実装 Contents.json

iOSアイコンのAppicon.appiconset内にあるContents.jsonはアイコン作成サイトで作ったものを一部書き換えて使っています。

内容は以下の通りです。

{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"3x"},
        {"size":"40x40","expected-size":"80","filename":"80.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"2x"},
        {"size":"40x40","expected-size":"120","filename":"120.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"3x"},
        {"size":"60x60","expected-size":"120","filename":"120.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"2x"},
        {"size":"57x57","expected-size":"57","filename":"57.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"1x"},
        {"size":"29x29","expected-size":"58","filename":"58.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"2x"},
        {"size":"29x29","expected-size":"29","filename":"29.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"1x"},
        {"size":"29x29","expected-size":"87","filename":"87.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"3x"},
        {"size":"57x57","expected-size":"114","filename":"114.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"2x"},
        {"size":"20x20","expected-size":"40","filename":"40.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"2x"},
        {"size":"20x20","expected-size":"60","filename":"60.png","folder":"AppIcon.appiconset/","idiom":"iphone","scale":"3x"},
        {"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"AppIcon.appiconset/","scale":"1x"},
        {"size":"40x40","expected-size":"80","filename":"80.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"},
        {"size":"72x72","expected-size":"72","filename":"72.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"1x"},
        {"size":"76x76","expected-size":"152","filename":"152.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"},
        {"size":"50x50","expected-size":"100","filename":"100.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"},
        {"size":"29x29","expected-size":"58","filename":"58.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"},
        {"size":"76x76","expected-size":"76","filename":"76.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"1x"},
        {"size":"29x29","expected-size":"29","filename":"29.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"1x"},
        {"size":"50x50","expected-size":"50","filename":"50.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"1x"},
        {"size":"72x72","expected-size":"144","filename":"144.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"},
        {"size":"40x40","expected-size":"40","filename":"40.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"1x"},
        {"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"},
        {"size":"20x20","expected-size":"20","filename":"20.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"1x"},
        {"size":"20x20","expected-size":"40","filename":"40.png","folder":"AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]}  

[C++] 54 FLTK : 画像加工アプリ / iOSアプリ用iconset作成機能の実装 appiconset

とりあえずmacOSアプリとiOSアプリに必要なアイコン類を作成できるようにしました。画像処理ライブラリとしてOpenCV(開発はインテル)を使っています。用意するのは2048*2048のpngファイルだけです。

iOSに必要なContents.jsonは前もってホームディレクトリに配置したものをコピーしました。

iOS
macOS
#include <opencv2/opencv.hpp>

void makeIcnsIOS(const char* filename){
	// AppIcon.appiconsetパス作成
	string path_str = string(filename);
	char del = '/';
	vector<string> list = split(path_str, del);
	string imagename = list.back(); 
	list.pop_back();

	const char* del2 = "/";
	string prefix = join(list,del2);
	string dir = prefix + "/" + "AppIcon.appiconset";

	cout<<"dir "<<dir<<endl;

	// iconsetディレクトリ作成
	mkdir(dir.c_str(),
		S_IRUSR | S_IWUSR | S_IXUSR |  // USR RWX
		S_IRGRP | S_IWGRP | S_IXGRP |  // GRP RWX
		S_IROTH | S_IWOTH | S_IXOTH);  // OTH RWX

	// 各種pngファイル作成
	int pixels[19] = {20, 29, 40, 50, 57, 58, 60, 72, 76, 80, 87, 100, 114, 120, 144, 152, 167, 180, 1024};
	string filenames[19] = {"20.png","29.png","40.png","50.png","57.png","58.png","60.png","72.png","76.png","80.png","87.png","100.png","114.png","120.png","144.png","152.png","167.png","180.png","1024.png"};
	
	int num = 0;
	for (int pixel:pixels){
		cv::Mat img,img_resize;
		img = cv::imread(filename,cv::IMREAD_UNCHANGED);
		cv::resize(img,img_resize, cv::Size(pixel,pixel),0,0,cv::INTER_LINEAR);
		// リサイズファイルパス作成
		string filename2 = filenames[num];
		string filepath = dir + "/" + filename2;
		cv::imwrite(filepath, img_resize);

		num += 1;
	}

	// JSONファイルコピー
	std::string jsonname1 = "/Users/[ユーザID]/ImageInspector/Contents.json";
    std::string jsonname2 = prefix + "/AppIcon.appiconset/Contents.json";
	system(("cp " + jsonname1 + " " + jsonname2).c_str());
}

[C++] 53 FLTK : 画像加工アプリ / iOSアプリ用iconset作成機能の実装 Fl_Choice

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

macOSアプリ, iOSアプリではアイコン登録に必要な画像ファイル数、サイズおよびファイルタイプが異なるため、都度選択し作成できるよう自製アプリの機能追加に着手しました。XCodeへのiOSアプリ登録に必要なのは正確にはicnsファイルではなくappiconsetフォルダです。

まずはガワだけ、プルダウンメニューを配置しました。中身はこれから書きます。

<該当箇所のみ>

Fl_Choice *choice;

// icnsタイプ選択項目
Fl_Menu_Item icns_type[3] = {
    {"macOS"},
    {"iOS"},
};

// icnsタイプ選択メニュー
choice = new Fl_Choice(135, 90, 80, 20, nullptr);
choice->menu(icns_type);

[C++] 52 FLTK : findコマンド生成アプリ/ FL_Menu_Itemの設定ミス その2

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

前回の続きです。

FL_Menu_Itemの配列の書き方でイテレータの存在を忘れていて修正を施しましたが、コードの見た目がいまひとつなので以下のように書き換えました。

Fl_Menu_Item pulldown[4] = {
  {"and"},
  {"or"},
  {"not"}
};

実際のアイテム数3に最後のヌルポインタを加えた4を記述する必要があります。こちらの方がすっきりしている感じがします。公式サイトにあるような複雑なメニューであれば最後に0を書くでしょう。

Fl_Menu_Item popup[] = {
 {"&alpha",    FL_ALT+'a', the_cb, (void*)1},
 {"&beta",     FL_ALT+'b', the_cb, (void*)2},
 {"gamma",     FL_ALT+'c', the_cb, (void*)3, FL_MENU_DIVIDER},
 {"&strange",  0,          strange_cb},
 {"&charm",    0,          charm_cb},
 {"&truth",    0,          truth_cb},
 {"b&eauty",   0,          beauty_cb},
 {"sub&menu",  0,          0, 0, FL_SUBMENU},
 {"one"},
 {"two"},
 {"three"},
 {0},
 {"inactive", FL_ALT+'i', 0, 0, FL_MENU_INACTIVE|FL_MENU_DIVIDER},
 {"invisible",FL_ALT+'i', 0, 0, FL_MENU_INVISIBLE},
 {"check",    FL_ALT+'i', 0, 0, FL_MENU_TOGGLE|FL_MENU_VALUE},
 {"box",      FL_ALT+'i', 0, 0, FL_MENU_TOGGLE},
 {0}};

お遊びで私のアプリにこの複雑なメニューを実装してみました。余談ですが、今になって何故altキーの記号が⎇なのかが理解できました。確かに左から右にまっすぐ進まずもう一方に変わっている様子で意味が伝わります。

それにしてもMontereyでは何故間違ったコードでも動いたのか、謎です。

[C++] 51 FLTK : findコマンド生成アプリ/ FL_Menu_Itemの設定ミス

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

macOS MontereyからBig Surにダウングレードしたところ、コマンドキーを押すとアプリが落ちるようになりました。エラー発生時に表示される解析情報を読むとどうやらFL_Menu_Itemまわりがおかしいようでした。

参考サイトのコードと全く同じようにプルダウンメニュー項目の最後に0を追加すると直りました。Montereyやアップグレード前のBig Surでは0がなくても正常に動いていたのに不思議です。

2022/5/18追記:
0(ヌルポインタ)が必要なのはイテレータに次の要素がないことを知らせるためでした。こんなことがすぐに分からないようではまだまだです。

Process:               FileFinder [8791]
Path:                  /Volumes/VOLUME/*/FileFinder
Identifier:            FileFinder
Version:               0
Code Type:             ARM-64 (Native)
Parent Process:        bash [8781]
Responsible:           Terminal [414]
User ID:               501

Date/Time:             2022-05-17 00:09:08.114 +0900
OS Version:            macOS 11.6.5 (20G527)
Report Version:        12
Anonymous UUID:        8099E198-41F7-976F-694A-6B1FB4EBE622


Time Awake Since Boot: 45000 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x1600000000000000 -> 0x0000000000000000 (possible pointer authentication failure)
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Segmentation fault: 11
Termination Reason:    Namespace SIGNAL, Code 0xb
Terminating Process:   exc handler [8791]

VM Regions Near 0:
--> 
    __TEXT                      102480000-10248c000    [   48K] r-x/r-x SM=COW  /Volumes/*

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libfltk.1.3.dylib             	0x00000001026d90e0 Fl_Menu_Item::test_shortcut() const + 20
1   libfltk.1.3.dylib             	0x00000001026d912c Fl_Menu_Item::test_shortcut() const + 96
2   libfltk.1.3.dylib             	0x00000001026d912c Fl_Menu_Item::test_shortcut() const + 96
3   libfltk.1.3.dylib             	0x00000001026bcdbc Fl_Choice::handle(int) + 384
4   libfltk.1.3.dylib             	0x00000001026c69b8 Fl_Group::handle(int) + 960
5   libfltk.1.3.dylib             	0x00000001026b4650 send_event(int, Fl_Widget*, Fl_Window*) + 148
6   libfltk.1.3.dylib             	0x00000001026b45ac Fl::handle_(int, Fl_Window*) + 1656
7   libfltk.1.3.dylib             	0x00000001026b4388 Fl::handle_(int, Fl_Window*) + 1108
8   libfltk.1.3.dylib             	0x00000001026aa2c0 -[FLView flagsChanged:] + 308
9   com.apple.AppKit              	0x00000001a1594c10 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 5644
10  com.apple.AppKit              	0x00000001a1593398 -[NSWindow(NSEventRouting) sendEvent:] + 352
11  com.apple.AppKit              	0x00000001a1592270 -[NSApplication(NSEvent) sendEvent:] + 2568
12  libfltk.1.3.dylib             	0x00000001026a636c fl_mac_flush_and_wait(double) + 536
13  libfltk.1.3.dylib             	0x00000001026b373c Fl::run() + 44
14  FileFinder                    	0x0000000102485e88 main + 3836 (FileFinder.cpp:491)
15  libdyld.dylib                 	0x000000019eb41430 start + 4
# 最後の要素の0は無意味ではなかったようです

Fl_Menu_Item pulldown[] = {
  {"and"},
  {"or"},
  {"not"},
  0
};

[AppleScript] 02 アルファベット入力からローマ字変換 スクリプト案

かなり時間が掛かりましたが、土台が出来上がりました。

以下のスクリプトではクリップボードのアルファベットをひらがなに変換するだけです。

操作中のアプリで文字を全選択してひらがな変換し、アプリに貼り付けるという動作はまだできません。

いつになるかわかりませんが、完成すればAutomatorに登録してショートカットを割り当てる予定です。

それにしてもAppleScriptの情報の少なさには困り果てました。ネット検索する限りでは日本語ユーザーの有識者は2人位しかいらっしゃいません。on – endが関数の設定と分かるまでは全く前に進みませんでした。

use AppleScript version "2.5" --  (10.12) or later
use framework "Foundation"
use scripting additions

property NSString : a reference to current application's NSString
property NSStringTransformLatinToHiragana : a reference to current application's NSStringTransformLatinToHiragana

selectString() -- 全選択のみ可

set latin to the clipboard

set hiragana to alphabetToHiragana(latin)

set the clipboard to hiragana

pasteString() -- 今のところ機能せず

on selectString()
    tell current application
        activate
        tell application "System Events"
            keystroke "ac" using command down
        end tell
    end tell
end selectString

on pasteString()
    tell current application
        activate
        tell application "System Events"
            keystroke "v" using command down
        end tell
    end tell
end pasteString

on alphabetToHiragana(aStr)
	set aString to NSString's stringWithString:aStr
	return (aString's stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:false) as string
end alphabetToHiragana

参考サイト

[AppleScript] 01 アルファベット入力からローマ字変換

アルファベットから”かな”キー連打でローマ字変換するという機能がmacOS Monterey 12.3.1では今のところ使えません。

もうAppleの対応をあてにせず、AppleScript等でなんとかできないか探ってみます。

実装したい内容は以下の通りです。
1.カーソル直近のアルファベット文字列を自動選択する。
2.ローマ字変換でひらがなに変換する。
3.漢字変換する。
アサインするキーはこれまで通り”かな”キー連打あるいは⌘ + 1文字とします。

ネットで調べるとAppleScriptでNSStringTransformLatinToHiraganaを使っているコード例が見つかりました。

またBig Surに戻すのもアホらしいのでなんとかしたいです。

[Swift] 13 自製ヘルスケアapp / UIKit DatePicker

UIDatePickerを配置しました。他の機種にも対応できるようウィジェットの座標・サイズは画面サイズに対する比率で算出しています。

次回はボタンの形などを設定します。

import UIKit

extension UIColor {
    static let blackRGB = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1)
}

class ViewController: UIViewController {
    let appName = UILabel()
    let datepicker_from_lbl = UILabel()
    let datepicker_from = UIDatePicker()
    let datepicker_to_lbl = UILabel()
    let datepicker_to = UIDatePicker()
    let button_HR = UIButton()
    let button_HRV = UIButton()
    
    let width = Float(UIScreen.main.bounds.size.width)
    let height = Float(UIScreen.main.bounds.size.height)

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.white
        
        appName.text = "Health Manager"
        appName.textAlignment = .center
        appName.textColor = UIColor.blackRGB
        appName.font = UIFont.systemFont(ofSize: 40)
        view.addSubview(appName)
        
        datepicker_from_lbl.text = "From"
        datepicker_from_lbl.textColor = UIColor.blackRGB
        datepicker_from_lbl.font = UIFont.systemFont(ofSize: 24)
        view.addSubview(datepicker_from_lbl)
        
        datepicker_to_lbl.text = "To"
        datepicker_to_lbl.textColor = UIColor.blackRGB
        datepicker_to_lbl.font = UIFont.systemFont(ofSize: 24)
        view.addSubview(datepicker_to_lbl)
        
        datepicker_from.preferredDatePickerStyle = .compact
        datepicker_from.datePickerMode = .date
        view.addSubview(datepicker_from)
        
        datepicker_to.preferredDatePickerStyle = .compact
        datepicker_to.datePickerMode = .date
        view.addSubview(datepicker_to)
        
        button_HR.setTitle("心拍数", for: .normal)
        button_HR.setTitleColor(UIColor.blue, for: .normal)
        view.addSubview(button_HR)
        
        button_HRV.setTitle("心拍変動", for: .normal)
        button_HRV.setTitleColor(UIColor.blue, for: .normal)
        view.addSubview(button_HRV)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        appName.sizeToFit()
        let AN_width = CGFloat(width*2/3)
        let AN_height = CGFloat(height*0.1)
        let AN_x = (width - Float(AN_width))/2
        appName.frame = CGRect.init(
            x: CGFloat(AN_x),
            y: CGFloat(height*0.15),
            width: AN_width,
            height: AN_height)

        let DP_width = CGFloat(width*0.2)
        let DP_height = CGFloat(height*0.1)
        let DP_x = width/2
        datepicker_from.frame = CGRect.init(
            x: CGFloat(DP_x),
            y: CGFloat(height*0.25),
            width: DP_width,
            height: DP_height)
        
        let DPL_width = CGFloat(width*0.15)
        let DPL_height = CGFloat(height*0.1)
        let DPL_x = DP_x - width*0.15
        datepicker_from_lbl.frame = CGRect.init(
            x: CGFloat(DPL_x),
            y: CGFloat(height*0.25),
            width: DPL_width,
            height: DPL_height)
        
        datepicker_to.frame = CGRect.init(
            x: CGFloat(DP_x),
            y: CGFloat(height*0.32),
            width: DP_width,
            height: DP_height)
        
        datepicker_to_lbl.frame = CGRect.init(
            x: CGFloat(DPL_x),
            y: CGFloat(height*0.32),
            width: DPL_width,
            height: DPL_height)

        let BT_width = CGFloat(width*0.2)
        let BT_height = CGFloat(height*0.1)
        let BT_x = (width - Float(BT_width))/2
        button_HR.frame = CGRect.init(
            x: CGFloat(BT_x),
            y: CGFloat(height*0.40),
            width: BT_width,
            height: BT_height)
        
        button_HRV.sizeToFit()
        button_HRV.frame = CGRect.init(
            x: CGFloat(BT_x),
            y: CGFloat(height*0.48),
            width: BT_width,
            height: BT_height)

    }
}