粗大メモ置き場

個人用,たまーに来訪者を意識する雑記メモ

Python,PyQtで簡単にGUIを作る ② Splitterを使った配置とcsvのPlotter

目標

PythonでちゃっちゃとGUIを作ることを目標にしています。

前回に引き続き下記の2つを満たすようなものを作成していきます。

  • 複数ある要素を選択
  • 選択した要素をグラフに随時プロット

ossyaritoori.hatenablog.com

f:id:ossyaritoori:20210718014237p:plain
今回つくるやつです。左でcsvのHeaderを選択して右の領域に該当する値がPlotされます。

今回やること

ざっくりと下記のことをやっていきます。

  • Splitterを用いた配置
  • pandasと組み合わせて数値をPlot

Splitterを用いた配置

SetGeometryなどを用いずにSplitterという仕切りを使ってWidgetを配置する方法を用います。

下記のチュートリアルを見てみましょう。

www.finddevguides.com

使い方はVboxたちと似通っていて定義後はaddWidgetでWidgetを追加できます。

#from PyQt5.QtWidgets import *
      splitter1 = QSplitter(Qt.Horizontal) #横向きにStackする
      textedit = QTextEdit()
      textedit2 = QTextEdit()
      splitter1.addWidget(textedit2)
      splitter1.addWidget(textedit)
# 中略
# 最後に予め作成したQVboxlayoutに登録
      vbox.addWidget(splitter1)

また,SplitterにSplitterを追加できるため下記のようにプログラムを書けばいろいろ追加できます。

f:id:ossyaritoori:20210718103855p:plain
上がSplitter1で分けた領域,そしてSplitter2ではSplitter1と下のWidgetを分けている

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

"""
Qt splitter のチュートリアル
https://www.finddevguides.com/Pyqt-qsplitter-widget

Example:Qwidgetから継承
SplitterはaddWidgetで部品を追加可能。Splitter自身も追加可能
基本的に一つの領域に一つの部品を配置できる。
"""

class Example(QWidget):

   def __init__(self):
      super(Example, self).__init__()

      self.initUI()

   def initUI(self):

      hbox = QHBoxLayout(self)
      
      # フレームを作る
      topleft = QFrame()
      topleft.setFrameShape(QFrame.StyledPanel)
      bottom = QFrame()
      bottom.setFrameShape(QFrame.StyledPanel)

      # 水平に分割
      splitter1 = QSplitter(Qt.Horizontal)
      textedit = QTextEdit()
      textedit2 = QTextEdit()
      textedit3 = QTextEdit()
      splitter1.addWidget(textedit2)
      splitter1.addWidget(textedit)
      splitter1.setSizes([100,200])

      splitter2 = QSplitter(Qt.Vertical)
      splitter2.addWidget(splitter1)
      splitter2.addWidget(textedit3)

      # Widgetを追加
      hbox.addWidget(splitter2)

      # レイアウト適用
      self.setLayout(hbox)
      QApplication.setStyle(QStyleFactory.create('Cleanlooks'))

      # Windowサイズを規定
      self.setGeometry(300, 300, 300, 200)
      self.setWindowTitle('QSplitter demo')
      self.show()

def main():
   app = QApplication(sys.argv)
   ex = Example()
   sys.exit(app.exec_())

if __name__ == '__main__':
   main()

csvファイルを読んでPlotする

前回の記事と合わせると冒頭で示したようなcsvファイルから数値列を読んでPlotするGUIが作れます。

流れは下記のとおりです。

  • csvを読み込んでPandasのDataFrameに落とす
  • 数値列のみを抽出してColumn名をリスト化
  • GUIを作成,選択したColumnリストをもとにPlot

データとして使うCSVはAtushi Sakai大先生のリポジトリから拝借してきました。

github.com

pandasで非数値列を弾く方法

CSVを読んでPlotするのは良いですがその際,非数値が混じってエラー終了するのは避けたいです。

下記のようにselect_dtypesを使えば非数値列を排除できて安全です。

        # dfの非数値の列のみ選択
        self.df = df.select_dtypes(include='number')

コード

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import pandas as pd 
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas


"""
Qt splitter のチュートリアル
https://www.finddevguides.com/Pyqt-qsplitter-widget

Example:Qwidgetから継承
SplitterはaddWidgetで部品を追加可能。Splitter自身も追加可能
基本的に一つの領域に一つの部品を配置できる。
"""

class Example(QWidget):
    def __init__(self,filename=None):
        super(Example, self).__init__()
        
        self.loadcsv(filename)
        self.initUI()
    
    def loadcsv(self,filename):
        df = pd.read_csv(filename)
        # 非数値の列のみ選択
        self.df = df.select_dtypes(include='number')
        print(self.df)
        self.columnlist = self.df.columns # list

    def initUI(self):
        hbox = QHBoxLayout(self)
    
        # list/figure作成
        self.create_listwidget()
        self.create_figure()

        # 左側作成
        splitter_l = QSplitter(Qt.Vertical)
        splitter_l.addWidget(self.add_label("Plotする要素を選択"))
        splitter_l.addWidget(self.listWidget)
        # 右側作成
        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(splitter_l)
        splitter.addWidget(self.FigureCanvas)
        #splitter.setSizes([100,200])

        # Widgetを追加
        hbox.addWidget(splitter)

        # レイアウト適用
        self.setLayout(hbox)
        QApplication.setStyle(QStyleFactory.create('Cleanlooks'))

        # Windowサイズを規定
        #self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('CSV plotter demo')
        self.show()

    def create_listwidget(self):
        # リスト作成
        self.listWidget = QListWidget()
        self.listWidget.setSelectionMode( #複数選択可能にする
            QAbstractItemView.MultiSelection
        )
        for col in self.columnlist:
            item = QListWidgetItem(col)
            self.listWidget.addItem(item)
        self.listWidget.itemClicked.connect(self.printItemText)
    
    def create_figure(self):
        # Figureを作成
        self.Figure = plt.figure()
        self.FigureCanvas = FigureCanvas(self.Figure)  # FigureをFigureCanvasに追加
        self.axis = self.Figure.add_subplot(1,1,1) # axを持っておく



    def add_label(self,text):
        # ラベルを作る
        label=QLabel(self)
        label.setText(text)
        label.adjustSize()
        return label


    def printItemText(self):
        # 選択したアイテムを表示
        items = self.listWidget.selectedItems()
        self.selected_columns = []
        for i in range(len(items)):
            self.selected_columns.append(self.listWidget.selectedItems()[i].text())

        self.plot_selected_columns()

    # Plotをアップデート
    def plot_selected_columns(self):
        self.axis.cla() # リセットを掛ける必要がある。
        df = self.df[self.selected_columns]
        df.plot(ax=self.axis)
        plt.grid(); 
        self.FigureCanvas.draw()


def main():
   app = QApplication(sys.argv)
   ex = Example("samplecsv.csv")
   sys.exit(app.exec_())

if __name__ == '__main__':
   main()

余談:Checkbox形式への変更

Listwidgetは結構見た目があれなので自分的にはCheckboxのほうが好きだったりします。 下記のように設定すればCheckboxを導入できます。

    # Checkbox Widgetの作成
    def add_checkbox(self,name):
      chxlist = QCheckBox(name, self)
      chxlist.stateChanged.connect(self.checklist_clicked)
      return chxlist
    
    #  クリックされたCheckboxをみてリストに追加 or 削除
    def checklist_clicked(self, state):
        checked_name = str(self.sender().text())
        if state == Qt.Checked: # if checked
            # add to the list
            self.clicked_list.append(checked_name)
        else: # if unchecked
            # remove from list
            self.clicked_list = [x for x in self.clicked_list if not x == checked_name]

余談2: Checkboxをまとめて導入

Checkboxをまとめて導入したいときは,上記のように一個一個Widgetを作るのではなく,VboxLayoutを用意してその中に入れていくのが良いです。

ちょっと長めの記述になります。

    def create_checkboxes(self):
        vbox = QVBoxLayout()
        for col in self.columnlist:
            vbox.addWidget(self.add_checkbox(col))
        # 箱を用意する
        frame = QFrame(self) # Frame定義
        frame.setFrameShape(QFrame.StyledPanel) # 形状決定。パネル。
        vbox.addStretch(1) # 行間を定義
        frame.setLayout(vbox)
        return frame

補足:Checkboxに変更した版のGUI

以上をまとめると下記のような感じになります。

f:id:ossyaritoori:20210718115306p:plain
記述量は増えますが自分はこっちのほうがスッキリしていて好みです。

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import pandas as pd 
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas


"""
Qt splitter のチュートリアル
https://www.finddevguides.com/Pyqt-qsplitter-widget

Example:Qwidgetから継承
SplitterはaddWidgetで部品を追加可能。Splitter自身も追加可能
基本的に一つの領域に一つの部品を配置できる。
"""

class Example(QWidget):
    def __init__(self,filename=None):
        super(Example, self).__init__()
        
        self.loadcsv(filename)
        self.initUI()
    
    def loadcsv(self,filename):
        df = pd.read_csv(filename)
        # 非数値の列のみ選択
        self.df = df.select_dtypes(include='number')
        print(self.df)
        self.columnlist = self.df.columns # list

    def initUI(self):
        hbox = QHBoxLayout(self)
    
        # list/figure作成
        #self.create_listwidget()
        self.create_figure()

        # 左側作成
        splitter_l = QSplitter(Qt.Vertical)
        splitter_l.addWidget(self.add_label("Plotする要素を選択"))
        #splitter_l.addWidget(self.listWidget)
        splitter_l.addWidget(self.create_checkboxes())
        # 右側作成
        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(splitter_l)
        splitter.addWidget(self.FigureCanvas)
        #splitter.setSizes([100,200])

        # Widgetを追加
        hbox.addWidget(splitter)

        # レイアウト適用
        self.setLayout(hbox)
        QApplication.setStyle(QStyleFactory.create('Cleanlooks'))

        # Windowサイズを規定
        #self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('CSV plotter demo')
        self.show()

    def create_listwidget(self):
        # リスト作成
        self.listWidget = QListWidget()
        self.listWidget.setSelectionMode( #複数選択可能にする
            QAbstractItemView.MultiSelection
        )
        for col in self.columnlist:
            item = QListWidgetItem(col)
            self.listWidget.addItem(item)
        self.listWidget.itemClicked.connect(self.printItemText)
    
    def create_figure(self):
        # Figureを作成
        self.Figure = plt.figure()
        self.FigureCanvas = FigureCanvas(self.Figure)  # FigureをFigureCanvasに追加
        self.axis = self.Figure.add_subplot(1,1,1) # axを持っておく

    def create_checkboxes(self):
        vbox = QVBoxLayout()
        for col in self.columnlist:
            vbox.addWidget(self.add_checkbox(col))
        # 箱を用意してはる
        frame = QFrame(self) # Frame定義
        frame.setFrameShape(QFrame.StyledPanel) # 形状決定。パネル。
        vbox.addStretch(1) # 行間を定義
        frame.setLayout(vbox)
        return frame

    def add_checkbox(self,name):
        chxlist = QCheckBox(name, self)
        chxlist.stateChanged.connect(self.checklist_clicked)
        return chxlist

    #  クリックされたCheckboxをみてリストに追加 or 削除
    def checklist_clicked(self, state):
        checked_name = str(self.sender().text())
        
        try:
            self.clicked_list
        except:
            self.clicked_list = []

        if state == Qt.Checked: # if checked
            # add to the list
            self.clicked_list.append(checked_name)
        else: # if unchecked
            # remove from list
            self.clicked_list = [x for x in self.clicked_list if not x == checked_name]
        
        self.selected_columns = self.clicked_list
        self.plot_selected_columns()


    def add_label(self,text):
        # ラベルを作る
        label=QLabel(self)
        label.setText(text)
        label.adjustSize()
        return label


    def printItemText(self):
        # 選択したアイテムを表示
        items = self.listWidget.selectedItems()
        self.selected_columns = []
        for i in range(len(items)):
            self.selected_columns.append(self.listWidget.selectedItems()[i].text())

        self.plot_selected_columns()

    # Plotをアップデート
    def plot_selected_columns(self):
        self.axis.cla() # リセットを掛ける必要がある。
        df = self.df[self.selected_columns]
        df.plot(ax=self.axis)
        plt.grid(); 
        self.FigureCanvas.draw()


def main():
   app = QApplication(sys.argv)
   ex = Example("samplecsv.csv")
   sys.exit(app.exec_())

if __name__ == '__main__':
   main()

TODO

いくつか目処は立っていますが下記の要素を徐々に追加していくとそれっぽくなるものと思われます。

Python,PyQtで簡単にGUIを作る ① リスト表示とグラフPlot

モチベーション

身の回りのありとあらゆる便利パッケージにGUIがあったらな,と思うことが多いためやり方をメモします。

初回はひとまず

  • リストの複数を選択
  • matplotlibの図をリストの選択に応じて表示

ができるようにします。

サンプルを通した理解

わかりやすいサンプルコードを介して理解を深めるのが手っ取り早いです。 今回の目的には下記の内容が大部分で合致しているので参考にしました。

qiita.com

配置

構造としてはまずはQwidgetというキャンバスを用意して,QVboxlayoutというコンテナの中にグラフのプロットであったり,チェックボックスであったり具体的な要素を詰めていくという構造をとります。(理解がUpdateされ次第Updateします。)

f:id:ossyaritoori:20210717170350p:plain

超最低限に書くと下記のようになります。

# 初期化のサンプル
class Test(QtWidgets): # QWidgetを継承
    def __init__(self):
        super().__init__() # 継承元のInit
        vbox = QtWidgets.QVBoxLayout(self) #Vboxlayoutの定義。縦にオブジェクトを並べる。(QHboxなら横に) 
        vbox.addWidget( QtWidgets.QListWidget() ) # リスト表示Widgetを追加。
        vbox.addWidget( QtWidgets.QLabel(self) ) # ラベルWidgetを追加。(テキスト設定していないからこの段階では無)
        # ...このように追加していって
        self.setLayout(vbox) # VboxlayoutをWidgetにはめ込む

他にオブジェクトを配置する手法としては最後の部分はsetGeometryなどでゴリゴリ配置しても良さそうですし,

        # 配置
        self.setGeometry(0,0,900,600)
        self.FigureWidget.setGeometry(200,0,700,600)
        self.FileList.setGeometry(0,0,200,600)

配置をこりたい人は,Qt Designer というGUIがあるのでそれでレイアウトを作ったほうがいいと思います。

www7a.biglobe.ne.jp

自分は今はSplitterという機能を使って画面を分割して配置するようにしています。また次回にメモします。

イベントの設定

配置したオブジェクトたちにはイベントが起きたときにどうするかという設定をすることができます。

# インデント略
self.listWidget = QtWidgets.QListWidget()
self.listWidget.itemClicked.connect(self.printItemText) #クリックされたらprintItemText関数を呼ぶ

# 呼ばれる側の関数
    def printItemText(self):
        # 選択したアイテムを表示
        items = self.listWidget.selectedItems()
        x = []
        for i in range(len(items)):
            x.append(str(self.listWidget.selectedItems()[i].text()))
        print (x)

実行

Qtにおいては、QApplicationというクラスがメインのアプリケーションを定義するので, 最後に作ったWidgetクラスを表示する際には下記のようにします。

#アプリケーション本体のインスタンスを作る
app = QtWidgets.QApplication(sys.argv)    #アプリケーションのインスタンスを作る
Test.show()                        #作ったWidgetを表示する
app.exec()                          #アプリケーションのメッセージループを開始する

小ネタ

大体のオブジェクトは配置してから初期設定などをする必要があります。 そうするとaddWidgetでの配置が見づらくなるので関数化して一行で表せるようにしたほうがはかどります。

ラベルテキスト追加

ラベルオブジェクトを配置するならこうします。

    def add_label(self,text):
        # ラベルを作る
        label1=QtWidgets.QLabel(self)
        label1.setText(text)
        label1.adjustSize()
        return label1

リストでの複数選択

QListWidgetでSelectionModeを設定すると複数選択可能にできます。

毎回リセットするなら ExtendedSelection,トグルするなら MultiSelectionがおすすめです。 下記のように書きます。

        self.listWidget = QtWidgets.QListWidget()
        self.listWidget.setSelectionMode( #複数選択可能にする https://doc.qt.io/qt-5/qabstractitemview.html
            QtWidgets.QAbstractItemView.MultiSelection # QtWidgets.QAbstractItemView.ExtendedSelection 
        )

参考: QAbstractItemView Class | Qt Widgets 5.15.5

リストで番号を選択してPlotするサンプル

ということでリストで番号を選択してPlotするサンプルを下記に載せます。

f:id:ossyaritoori:20210717182134p:plain
選択した順番に下の図にPlotされます。

# -*- coding: utf-8 -*-
"""
Stackoverflowの文章より原型を拝借
QtのListwidgetで複数選択をしてPlotする。
"""

from PyQt5 import QtWidgets, QtCore
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas

class Test(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Test, self).__init__(parent)
        self.layout = QtWidgets.QVBoxLayout()
        # リスト作成
        self.listWidget = QtWidgets.QListWidget()
        self.listWidget.setSelectionMode( #複数選択可能にする
            QtWidgets.QAbstractItemView.MultiSelection
        )
        self.listWidget.setGeometry(QtCore.QRect(10, 10, 211, 291))
        for i in range(10):
            item = QtWidgets.QListWidgetItem("%i" % i)
            self.listWidget.addItem(item)
        self.listWidget.itemClicked.connect(self.printItemText)

        # Figureを作成
        self.Figure = plt.figure()
        self.FigureCanvas = FigureCanvas(self.Figure)  # FigureをFigureCanvasに追加
        self.axis = self.Figure.add_subplot(1,1,1) # axを持っておく

        self.layout.addWidget(self.add_label("Ctrlを押しながらで複数選択"))
        self.layout.addWidget(self.listWidget)
        self.layout.addWidget(self.FigureCanvas)
        self.setLayout(self.layout)


    def add_label(self,text):
        # ラベルを作る
        label1=QtWidgets.QLabel(self)
        label1.setText(text)
        label1.adjustSize()
        return label1


    def printItemText(self):
        # 選択したアイテムを表示
        items = self.listWidget.selectedItems()
        x = []
        for i in range(len(items)):
            x.append(int(self.listWidget.selectedItems()[i].text()))

        print(x)
        self.update_Figure(x)

    # Figure
    def update_Figure(self,x):
        self.axis.cla() # リセットを掛ける必要がある。
        self.axis.plot(x,'-o')
        plt.grid(); plt.legend(["selected number"])
        self.FigureCanvas.draw()

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    form = Test()
    form.show()
    sys.exit(app.exec_())

m1 mac 環境構築メモ① 入力の設定やRemap、基本操作など

3月末にM1 macを買ってウキウキだったものの、なんだかんだWSL2にハマってしまって放置していました。 とりあえずまともにタイピングできる環境を整えたのでメモします。

機体と環境について

  • 2021年度版 m1 macbook air
    • 16GB, 512GB SSD
    • JIS配列キーボード

電池の持ちが一昔前のガラケーレベルなのが最も顕著に恩恵を感じます。 今はGoogle Colabなどで作業環境共同化もできるので回線さえあれば外出時でもそれなりに色々できます。

記事内での設定内容

  • Terminalをショートカットで開くようにする
  • キーボード設定
    • ファンクションキーをFnなしで動かす
    • JISキーボードをUS入力風に変更する
    • 単語移動を変更
  • 装飾キーまわりのメモ

ターミナルをショートカットで開く。

⌘スペースでWin+rのように呼び出しメニューを出せるのでそこでターミナルとうてばデフォルトでもキーボードだけでターミナルを起動できます。

ただ、やっぱCtrl+Alt+T的なので一発で開きたい場合はちょっと回り道をする必要があります。

Automatorというアプリでサービスというのを作成してそれにショートカットを割り振るというのがよく使われているようです。

https://ry0.github.io/blog/2015/10/21/open-terminal-shortcut/#gsc.tab=0

コンフリクトがない限りいくつでもショートカットは設定できます。 まっさらな環境だと普通にcontrol+command+Tを割り振れました。

キーボード設定

基本的なところ

基本的なところは箇条書きにて失礼します。

KarabinerでJISキーボードをUSキーボードに設定

下記の記事の通りにKarabinerをインストールします。

https://qiita.com/canonrock16/items/0c090276b2f1fd8eb0ab

初回はキーボードの識別に時間を取られてちょっとビビります。 その後、権限を求められるのでそのあたりをきちんと設定したらOKです。

f:id:ossyaritoori:20210605164845p:plain
Karabinerの設定画面

私の設定は以下の通り。

変換前 JISの該当キー 変換後 USの該当キー
international1 grave_accent_and_tilde(`)
international3 backslash \
caps_lock 左下の奴 left_command 左⌘
right_command 右⌘ right_control 右^

右のシフトも使わない気がしているので適当に変換しようかなとは思っています。

Karabinerでその他ショートカット作成

Karabinerでは複数キーの組み合わせもRemapすることができます。 これはComplex modificationsという箇所から設定できインターネット上からインポートしてくることができます。

https://ke-complex-modifications.pqrs.org/?q=option%20%2B%20arrows

例えばWindowsでやっていた操作を一通りMacで動かせるようにするには下記の設定検索からWindows shortcuts on macOS というのを検索してImportすれば良いです。

自分の場合単語移動をcontrolでできるように下記を入れました。

Exchange control + arrows keys with option + arrows keys

装飾キーまわり

Macを使うにあたりちょっと混乱したのがWindowsであったCtrl/Alt/Winキーの挙動がMacではあまり対応していないことです。

⌘はAltとCtrlの機能を兼ねたような挙動を持っていますが肝心なときにどっちかわからなかったりします。
Pythonを雑に起動して終了しようと思ったら画面分割されたりとか、Chromeで新規タブは⌘Tで出すのにタブ移動は^Tabだったりするのでちょっと困ります。

単語移動関連

WindowsやLinudxではCtrlで単語移動できますが、macではOptionで移動する必要があります。(自分はKarabinerの設定で変更しました。)

とりあえずデフォルトでのvscodeでの挙動を下記メモします。

option 上下 行を保持して移動
option 左右 単語移動
command 左右 行頭行末移動
command 上下 文頭文末移動
control 左右 ワークスペース切り替え
control ウィンドウ選択
control 最近開いたファイル表示?

使いにくいと思った部分は混乱がない範囲でRemapして行けばいいと思います。

また、日本語の単語移動を実現するにはその都度設定が必要のようです。 VScodeの場合はJapanese Word Handerを入れれば良さそうでした。

スクリーンショット

スクリーンショット自分の場合、ファイルに保存する必要はなくてクリップボードに入れば良いことが多いです。 下記を参考にしました。

https://tamoc.com/mac-screenshot-window/#i-3

結論、特定領域・Windowをクリップボードにコピーするには下記のとおりやればいいです。

  • control ⌃+command ⌘+shift ⇧+4 で自由領域Cropを起動
  • Spaceを押してWindowの選択をする

controlを押さなければ普通にデスクトップに保存されるようです。

その他

プログラム関係 - ターミナルの名前を変更 - https://code-graffiti.com/how-to-change-the-prompt-display-on-the-mac-terminal/ - Xcodeをインストール - homebrewのインストール - VSCODE - https://stackoverflow.com/questions/30065227/run-open-vscode-from-mac-terminal

今後もなにかあれば追加していくと思います。

2021年4月のよかったもの

誘われたので。4月は特になにかしてないのであまり書くことはありません。 (以下からである調)

まんてん

下記の記事にも紹介されている「まんてん鮨」の日暮里店に食べに行った。

r.gnavi.co.jp

6000円コースを頼んだのだが結論から言うと超腹一杯うまい鮨を食える。 おまかせなのでメニューは選べないが私が行ったときはこんな感じ。恥を忍んでメモをとった。

個人的にはキンメダイの炙りがうまかった。

## 6000円コース 28品目 *付きはシャリ付き
しじみ汁
*サクラマス にぎり
宮城めかぶ
ほたて こぶじめ
かつお たまり醤油のくぐり
湯葉くず粉
あわび
*コハダ にぎり
*キンメダイ あぶり にぎり
みずだこ煮
*しろえび にぎり
*くるまえびにぎり
うめ茶碗蒸し
*赤みにぎり
*トロにぎり
子持ち昆布
ほたるイカあぶり
*いくら + 茎山葵(お椀)
たらこ西洋わさびつけ
*青森ムラサキウニ
*北海道バフンウニ
ひょうたん柴漬け
*ねぎとろ巻
しじみ味噌汁
*穴子にぎり
卵焼き
*かんぴょう巻
すいか

写真は控えていたがウニだけは初めてちゃんと食べたので写真を撮った。

f:id:ossyaritoori:20210502010907p:plain
(上)北海道産バフンウニ,(下)青森産ムラサキウニ。 バフンウニは癖が少なくムラサキウニは味が濃い。

注:予約しないと基本入れなさそう。カウンターで大将の握るのを待つ時間が長いので友達と行くべし。

葬送のフリーレン

サンデーで連載されている漫画。よくあるRPG世界観風ファンタジーかなと侮っていたが思っていた以上に良かった。

読むとすごく優しい気持ちになれる。

1話から魔王を倒した後のエピローグで始まる異色の構成だが, 今を生きる若者と旅しながらかつての旅の思い出を振り返る主人公の描写がアラサーには結構刺さる。これ少年向けか??

ジャンプの「チェンソーマン」「アンデッドアンラック」もそうだが,新しい世代の漫画はタメ回のようなものが少なく(特に序盤)ストーリー展開により無駄がない様に感じる。

感想として今の若手の子たち漫画うますぎない?となっている。

その他

書こうか迷っているが別に書くほどでもないなと思っているやつら。

  • m1 macbook air
    • 電池が持つだけでこんなにも使用感がいいのか!となる。現状開発はWSL2に寄ってるのでやや持て余し気味。
  • vivy(2021年4月開始 SFアニメ)
    • ターミネーター的な王道テーマをきれいな絵柄と丁寧な表情描写で描いているオリジナルアニメ。SFに飢えていたので今後に期待。
  • 鎌倉 オクシロモン
    • お洒落なカレー屋さん。うまい。以上。
    • カレーつながりだと日吉のホアホアというアジアンダイニングのカレーが滅茶うまい。
  • ふるさと納税全般
    • 美味い。神。ありがとう我がふるさと。
  • Oisix
    • 献立を考えないというのがいかに楽かというのがわかる。でも高いのでやめた。

GitHub ActionsとMarkdownで書類作成 ~ Asciidoc編~

概要

下記記事のGitHub Actions版だと思えば良いです。

ossyaritoori.hatenablog.com

下記に出来上がった書類のサンプルを載せます。

https://yoshiri.github.io/MarkdownToAsciidocToHTML_CI/sample.html

Markdownからの変換手法

ここではMarkdownで書いた文をAsciidocに変換します。

qiita.com

MarkdownからAsciidocへの変換はPandocとKramdocの2択があります。

  • Pandoc
    • 言わずとしれた万能変換ツール
    • 🙆数式などの変換がちゃんとしている
    • 🙅タイトルへの変換
  • Kramdoc
    • Asciidoctorの開発側が開発した変換ツール
    • 🙆章立てなどがきれいに変換される
    • 🙅数式や一部書式が正しく変換されない

ここではPandocを採用します。(数式の書きにくいドキュメントはありえないので。)

ここで,Pandocで用いるコマンドを下記に示します。(DockerコマンドなのでPandoc環境がある人は適当に読み替えてください)

wsl docker run --rm -v $(pwd):/data pandoc/core: sample.md --to asciidoctor -o sample_pandoc.adoc  --shift-heading-level-by=-1

引数の詳細などは下記を参照すると良いです。(本当は英語版サイトのほうがもっと良い。)

sky-y.github.io

変換前提のMarkdownの書き方について

基本的な書き方はうまいこと変換されますが,気をつけなければ行けないのは章立てです。

通常の書き方をすると下記のようになると思います。

# 1章
## 1-1節
## 1-2 節
# 2章
## 2-1節
### 2-1-1節
...

本記事で紹介する変換法を用いる場合文章は下記のように書くことを推奨します。 AsciidocのLevel1はHTMLのタイトル用であり,章がLevel2からスタートしていることに起因します。

# 文書タイトル
## 1章
### 1-1節
### 1-2 節
## 2章
...
このように一つずつずらす。

または,マークダウンで見た時に不格好になるのが気にならなければ下記のようにかくのも良いでしょう。

= 文書タイトル
## 1章
### 1-1節
### 1-2 節
## 2章
...
(上記は`--shift-heading-level-by=-1`のときの書き方)

理由は次で説明します。

--shift-heading-level-by=-1を用いた時の挙動について pandocで下記のようなマークダウンを変換した場合,レベルを1段下げます。

# 文書タイトル
## 1章
### 1-1節

下記のようにAsciidocでは=#のような役割を持ちます。

== 文書タイトル
=== 1章
==== 1-1節

これに対して--shift-heading-level-by=-1を引数に加えることでレベル下げをキャンセルできます。 できるのですがタイトルのブッキングを避けるために下記のような結果を生成します。

文書タイトル   <- 本当は"="から始まってほしい 
== 1章
=== 1-1節

これを避けるもっとも手っ取り早い方法は最初からタイトルの装飾を"#"から"="に書き換えて置くことです。

置換でタイトルの"#"を"="に変更

タイトルの装飾を"#"から"="に書き換えて置くことで上記の問題は解決しますが,Markdownでこれを書くのは面倒。

ということで,GithubなどのCIの時点でタイトルの装飾を変えれば良いです。

具体的には,sedコマンドを用いて 文章の最初に出てきた"# "のを"= "に変えます。(タイトルはほぼ文章の最初に来るはずなので)

コマンドはこちら。

sed -ie '0,/# / s/# /= /' sample.md

workflowの定義

サンプルリポジトリをおいておきます。

github.com

workflowは下記のような内容です。

# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run. 
on:
  # Triggers the workflow on push or pull request events but only for the master branch
  push:
    branches: [ master ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # asciidoc to html
  asciidoctor_job:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    name: Build AsciiDoctor
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - name: Check out code
      uses: actions/checkout@v2
    - name: Fix Title Level Issue
      run: sed -ie '0,/# / s/# /= /' sample.md
    - name: Markdown To Asciidoc
      uses: docker://pandoc/core:2.11
      with:
        args: -f markdown+east_asian_line_breaks sample.md --to asciidoctor -o sample.adoc --wrap=preserve --verbose  --shift-heading-level-by=-1
    # Output command using asciidoctor-action
    - name: Build AsciiDoc step
      id: documents
      uses: Analog-inc/asciidoctor-action@master
      with:
        shellcommand: "asciidoctor sample.adoc -r asciidoctor-diagram -a allow-uri-read -a data-uri -a toc=left" 
    # Use the output from the documents step
    - name: move deploy files
      run: |
        mkdir build/
        mv sample* build/
        ls build
    - name: deploy
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./build
        publish_branch: gh-pages

以上!

GitHub ActionsとMarkdownで静的サイト作成 ~ mkdocs-material 編~

目的

Markdownで書いた文書を静的サイトとしてまとめ上げたい。 そしてそのままそれを本番の文章として提出しちゃいたい。

なお,前回の記事はこちら。

ossyaritoori.hatenablog.com

要求:

  • 検索機能つきの静的なサイトが生成される
  • 表現が豊か(数式・図表・Admonitionなど)
  • PDFに出力可能

超参考になるサイト:

in-neuro.hatenablog.com

ここから,mdbook, (gitbook), mkdocsあたりを試していこうと思います。

mkdocs-material

Pythonで動く静的サイト作成手法mkdocsのmaterialというテーマ。

github.com

f:id:ossyaritoori:20210212170139p:plain
見た目はこんなかんじ。

Pros and Cons

  • 🙆Pros
    • 表現力が豊か(数式,PlantUML,Admonitionなど)
    • 日本語検索にデフォルトで対応
    • cssのカスタムテンプレートなどの数が多い
  • 🙅Cons
    • サーバーを立てないと検索窓が立たない(Github Actionsでは無問題)
    • 拡張機能が多く管理が面倒

機能性・拡張性では今の所mdbookよりも良さそうです。

Dockerを用いた動作

多くの人が入れているであろうPython環境で動くので自前でも動かせますが,Dockerイメージもあります。 自分はWSLでやっていますが,Docker for windowsの場合は適宜`pwd`”%CD%”にするなど置き換えてください。

とりあえず動かす分にはsquidfunk/mkdocs-materialが多く使われている感じがします。

hub.docker.com

  • 初期化
wsl docker run --rm -v `pwd`:/book/ squidfunk/mkdocs-material init
  • build
wsl docker run --rm -v `pwd`:/book/ squidfunk/mkdocs-material build

ざっくり生成されるファイル・フォルダを説明すると

  • book.toml : 書式や署名などを指定する
  • src/ : 文書のソースを置く場所
    • SUMMARY.md : リスト形式で文章のネスト構造を指定する
    • xxx.md: 本体の文章
  • book/ : 生成されたサイトがここに格納される。文章へはindex.htmlにアクセス。

Github Actionsを用いたドキュメントのHost

ひとまずテストプロジェクトを立ててみました。

github.com

なお,拡張機能をいろいろ盛り込んだので動作検証用のossyaritoori/mkdocs-materialにてイメージを公開しました。

したがって,実行コマンドは下記のような感じになります。

docker run --rm -v `pwd`:/docs ossyaritoori/mkdocs-material build

ファイル構造

ファイル構成はこんなかんじです。

mkdocsTest
├── .github
│  └── workflow
│     └── mkdocs.yml
│
├── mkdocs.yml # book setting
└── docs # Source folder
   ├── index.md # For top page (Not neccesary)
   └── < other .md files >

.github以下のActionsについて

Actionの流れは

  • ファイルをCIサーバーにUpload
  • build
  • docs以下をgh-pagesに展開

としました。

name: deploy mkdocs
on:
  push:
    branches:
      - main
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - name: Check out code
        uses: actions/checkout@v2
      # Prepare Python package
      - uses: actions/setup-python@v2
        with:
          python-version: 3.x
      - run: pip install mkdocs-material
      - run: pip install plantuml-markdown python-markdown-math mdx_truly_sane_lists mkdocs-git-revision-date-localized-plugin mkdocs-add-number-plugin
      # deploy
      - run: mkdocs gh-deploy --force

gh-pagesの設定

上記のFlowではgh-pagesブランチに生成物をUploadしています。

gh-pagesの設定は「設定」→「一般」から下記の項目で

f:id:ossyaritoori:20210212163623p:plain
一般の項から

TODO・その他

GithubActionについて

面倒でそのままpipを手打ちしていますがRequirement.txtとかを書いてそこからインストールしたほうが良さそうです。 Dockerfileもそれに準拠して書かないとなぁとは思っています。

PDF生成

こんな感じのPDFが拡張機能によって作れます。

github.com

インストール後,CSSファイルをコンパイルして作成することでそれっぽくできます。

ただ,日本語が文字化けしてしまったのでそのあたりはCSSを調整してみないと,といった感じです。

GitHub ActionsとMarkdownで静的サイト作成 ~ mdbook 編~

目的

Markdownで書いた文書を静的サイトとしてまとめ上げたい。 そしてそのままそれを本番の文章として提出しちゃいたい。

要求:

  • 検索機能つきの静的なサイトが生成される
  • 表現が豊か(数式・図表・Admonitionなど)
  • PDFに出力可能

超参考になるサイト:

in-neuro.hatenablog.com

ここから,mdbook, (gitbook), mkdocsあたりを試していこうと思います。

mdbook

開発が終了したGitbook v1のRust版後継です。

f:id:ossyaritoori:20210212165401p:plain
こんなかんじの見た目になります。

Pros and Cons

  • 🙆Pros
    • 動作が軽量。コンパイルが早い。
    • サーバを建てずともサイトの検索機能が動作する
  • 🙅Cons
    • 現状日本語検索に対応する気があまりなさそう…
    • 発展途上なので現状表現力は他の手法に劣る。

Dockerを用いた最小動作

Rust環境を建てても良いですが,Dockerで開発するのが無難です。 自分はWSLでやっていますが,Docker for windowsの場合は適宜`pwd`”%CD%”にするなど置き換えてください。

github.com

  • 初期化
wsl docker run --rm -v `pwd`:/book/ peaceiris/mdbook init
  • build
wsl docker run --rm -v `pwd`:/book/ peaceiris/mdbook build

ざっくり生成されるファイル・フォルダを説明すると

  • book.toml : 書式や署名などを指定する
  • src/ : 文書のソースを置く場所
    • SUMMARY.md : リスト形式で文章のネスト構造を指定する
    • xxx.md: 本体の文章
  • book/ : 生成されたサイトがここに格納される。文章へはindex.htmlにアクセス。

Github Actionsを用いたドキュメントのHost

ひとまずテストプロジェクトを立ててみました。

github.com

ファイル構造

ファイル構成はこんなかんじです。

mymdBook_Test
├── .github
│  └── workflow
│     └── mdBook.yml
│
├── book.toml
└── src
   ├── chapters
   │  ├── introduction.md
   │  └── <other .md files>
   └── SUMMARY.md

.github以下のActionsについて

Actionの流れは

  • ファイルをCIサーバーにUpload
  • build
  • book以下をgh-pagesに展開

としました。

name: github pages

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: Setup mdBook
        uses: peaceiris/actions-mdbook@v1
        with:
          mdbook-version: '0.4.6'
          # mdbook-version: 'latest'

      - run: mdbook build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./book

gh-pagesの設定

上記のFlowではgh-pagesブランチに生成物をUploadしています。

gh-pagesの設定は「設定」→「一般」から下記の項目で

f:id:ossyaritoori:20210212163623p:plain
一般の項から

TODO・その他

  • ちょっときになる点
    • 左のTitleが折り畳めない
  • PDF生成とか試しておきたい。