TyporaとDropboxで快適メモ環境構築
快適なメモ環境を探して1年弱... いろいろ探して試しみましたが, 自分にピッタリ合うものはなかなか見つからず...
とりあえず現状いい感じのメモ環境を構築出来たので共有したいと思います.
私がメモ環境に求める条件は以下の通り
とりあえず上記の全ての条件を満たした環境を構築したのでこの記事に書いていきたいと思います.
結論
Typora + Dropbox
です.
完成イメージ
環境
- Ubuntu 18.04 LTS
やり方
Typoraのインストール
Typora公式サイト からインストールします.
Dropboxのインストール
Dropbox 公式サイト からDropboxアプリケーションをインストールします.
markdownファイルの保存先ディレクトリをDropboxと同期されているディレクトリ内に作成する.
Ubuntuの場合は, デフォルトでは /home/user/Dropbox/
でした. 他のOSは多分適当に探せば見つかります.
Typoraの設定
設定を開き以下の項目を設定します.
起動時にデフォルトで開くディレクトリを先程作成したディレクトリに変更します.
次に自動保存を有効にします.
自動保存の保存間隔は"高度な設定"を開き, autoSaveTimer
の値を変更すれば変えることができる.
画像を保存するディレクトリを指定します.
これをすることで貼り付けた画像などが一箇所にまとまり, さらにはDropboxの自動同期の恩恵も受けられるので別のPCで開いた時に画像がなくなることがないです. また, 相対パスを使用するようにすることで他のPCで開いたときのパスの違いを受けにくくなるので, 画像をどの端末でも表示できます.
最後に表示の欄からファイルツリー表示に変更すれば完了です.
これで快適メモ環境が完成です!
感想
今回は今自分が使っているメモ環境と構築方法を紹介しました.
メモ環境論争はいつまで経っても正解が出ませんが, 自分はしばらくこれでいきます. 形式もMarkdownなので他のメモアプリを使うとなった場合でも移動しやすいと思います.
今回はTyporaに下書きを書いてからはてなブログにコピペするスタイルで記事を作成しましたが, 画像らへんや表示形式の違いなどで結局二度手間だった感があるので, ブログ記事の下書きにはあんまり向いてないのかなぁって気がしました.
2019年振り返りと2020年について
この記事を書いている時点で2020年になってから11日が経ってしまっていますが, そんなことは気にせず去年の振り返りと今年2020年の目標設定みたいなのをやっていきたいと思います.
前回こんな感じの1年振り返りの記事を書いたのは2月の誕生日だったので, 今年も誕生日に書くか〜って思っていましたが, やっぱり新年始まってすぐ書いた方がきりが良い気がするので今年以降は1月に書いていこうと思います.
ちなみに去年のやつはこれです.
そういえば去年の3月にTwitterのアカウント消えたので最初の四半期は何してるか詳しくわからないです.
1月
- Kaggle の Quoraコンペに参加した.
そういえば1年前はKaggleやってましたね... 確か銅メダルだった気がします.
ただ, 多くの参加者が最終サブミットをしなかったから銅メダル取れただけだし, なんなら殆どコピペだったので記事にはしてないです.
2月
- 3年への進級が確定した.
- Mix Leep Study # 33 に参加した
- Kaggleもくもく会 #7 @新大阪 に参加した
- 鳥羽商船学内ハッカソンに出た.
- 株式会社EBILAB の社員の方々と会食した.
- Mix Leep Study # 35 に参加した
- 株式会社Sportip にアルバイトとしてジョインした.
2月は何したかなぁってカレンダー見たら結構色々ありましたね.
この中で特筆すべきことは 学内ハッカソン と 株式会社Sportip (以下 Sportip と呼ぶ) にジョインしたことでしょうか.
学内ハッカソンの方では確か Globally and Locally Consistent Image Completion という論文を実装しようとして, 途中で学習に2ヶ月かかることに気づいて制作を途中で辞めた気がします. 結局GLCICは完成しませんでしたが, これをしたことにより, 論文から実際にコードとして実装する難しさ, 論文の読み方 などを知れたので, 目に見える結果は出なかったけどいい経験だったと思います.
結局, 学内ハッカソンに出したのは前日に作った小さなブラウザミニゲームですが, そこでGLCICの話をする機会があり, そこで 株式会社EBILAB賞 をいただき, その後 社員の方々と会食をご一緒させて頂きました.
Sportipについては, この前からお誘いを頂いており, 何回か面談をした後にジョインしました.
現在もSportipの方で業務に参加させてもらってます. 業務内容に関しては言えませんが, スタートアップの速度と, Sportipがどんどん成長していくのを感じています. 頑張らねば!
3月
- Mix Leep Study #36 に参加した
- Mix Leep Study #37 に参加した.
- めっちゃ業務やってた.
- Twitterアカウント消えた.
こうして見るとめっちゃ Mix Leep 参加してますね.
特に#37 の量子コンピュータの回が印象に残っています.
Twitterアカウントに関しては以下の記事に全て書いたのでこっち読んで.
4月
- 3年生になった.
- 基本情報落ちた.
1年生の時にやらかしたので4年目でやっと3年生になれました. 長かった...!
基本情報は 「まぁいけるやろ」って感じで舐めプしたら落ちました. アホですね.
5月
- Maker Faire Kyoto に参加した(一般)
- UdemyのAWS講座をやった.
- Computer Science (CS) に興味を持ち始める.
Maker Faire Kyoto は凄い作品多かったです(語彙力のNASA).
UdemyのAWS講座はめちゃくちゃわかりやすくて超おすすめしたいところですが, 今はもう販売していないのです. 残念.
6月
- N高の説明会行った
- LinkedInのリクルーターから連絡きた,
- 競プロ少し始めた.
N高については転校出来そうならしようと思ってたけど, 単位的に卒業までに2年必要らしいのでやめた.
LinkedInのリクルーターから連絡来たけど, アルバイトじゃなくて正社員にしたくて連絡したらしいので話は無くなった.
このへんから競プロ始めた気がする. けど, ここ数ヶ月全くしていないのでそろそろやらねば
7月
僕はだいたい3ヶ月に一回くらい学校辞める辞めるって言ってるんですけど, この月はかなり辞めたい欲が強かったです. 理由としては3年生になって授業数が多り, 専門科目も多くなり, 好きな情報工学系の勉強ができなくなっていたからです. 自分の学科は電子機械系なので情報系は基本的に独学です. 一応デジタル回路の授業があるので完全に独学ではないですが...
中国語は, 昔中国に4年住んでいたので, その当時はネイティブスピーカーだったのですが, 今では全く話せません. 10年も話していないと仕方ないね.
でも, 昨今の中国語需要に伴い自分も勉強しなおそうとしましたが, 生きるのに必須では無いので1週間で辞めちゃいました. 英語は普段からコミュニケーションや情報収集で使うのでモチベーションが保つのですが, 中国語は普段使わないのでモチベーションが続きませんでした.
8月
アメリカ行った!! (帰りに台湾も行った)
8月はとにかく「アメリカに行った」これに尽きます. アメリカに行った件は全て以下の記事に書いたので気になる人は読んでください.
9月
- Sportip本社で2週間働いてた.
- アズールレーンの2周年イベントに行った.
- ナガシマスパーランドに行った.
普段私はリモートで業務に参加しているのですが, 夏休みの後半2週間は東京で対面で働いてました. 世間ではリモートワークを賞賛する声が多いようですが, やはり対面で仕事をしたほうが仕事はしやすい気がします.
10月
低レイヤーに手を出す
この辺から低レイヤーに興味を持ち始めました. 特にシステムプログラミングなどに興味が湧いてきました. 最初に読んだ本は x86CPUエミュレータ を作る本なのですが, これが本当にわかりやすくてめちゃくちゃ楽しかったのを覚えています. この本をシステムプログラミングの最初の本として読んだのは幸運でした. システムプログラミングは今までブラックボックだったコンピュータという高度な機械をどんどん理解出来るようになっていく過程がとんでもなく楽しいです.
ロボコンも高専に3年半在学して初めて見に行きました. 自分はソフト系の人間なわけですが, それでもロボットが実際に動き, 競技を行っている姿には興奮しました. 私のクラスにもロボコン部の人間は数人いるわけですが, 本当に尊敬します.
授業に関しては, どうしても機械系科目に対して興味がでなくて辛かったです.
頭の中でCPUとかのことを考えながら過ごしてました. 微積の授業とデジタル回路の授業のおかげでメンタルは保たれました.
11月
- Rust始めた
- HHKBを手に入れた. 我流タイピングの矯正を始めた.
- 42TokyoのWebテストに合格してPicineに参加確定した.
Rustを始めたのはいいけど, チュートリアル終わってから触って無いので何も覚えてないです. てかRustってシステムプログラミングとかに使うのはわかるねんけど, もう少し敷居の低い手軽に作れるやつないん? Web鯖とかか?
42Tokyoは, プレスリリースを見た瞬間に会員登録して, その1週間後にはWebテストやりました. ちなみにPicineは3月参加予定です. 受かるといいなぁ.
12月
- CS を本気で勉強する決意をする
- 全裸監督 見た
- Turing Complete FM (TCFM) を発見した
- TOEIC 初受験 670点 を取った
- CPU制作のために論理ゲートなどをトランジスタで作った.
12月は1,2週間目はTOEICの勉強をしていた. おかげで初受験で670点というなかなかの成績が取れた. 個人的には800点以上は欲しいのでまた余裕が出来た時に受験してみます.
CSを本気で勉強する決意は, 前から勉強しなきゃって思ってたんですが, 以下のブログが決定的でした. 情報系の人は全員読んでほしい. 心に刺さりすぎて泣きたくなってくるので.
論理ゲートとか作ったりしたのは論理ゲートの仕組みを理解したかったためです. 一応DFFとかも作りたいなと思っています. CPU作るときにはIC使いますが, 論理ゲートなどがブラックボックなのもあれなのでとりあえず手を動かしてみた感じです.
TCFM は正直システムプログラミング初心者の僕には何言ってるか8割くらいわからないけれど, それでも聞いているとシステムプログラミング楽しそ〜〜って感じるのでたまに聞いてます.
2019総振り返り
気づいてたかもしれませんが, 今年は殆どハッカソン等のコンテストに参加していまんせん. なので, 対外的な成果や活動実績が殆ど無く「今年はあんまり何もしてないなぁ」って思ってましたが, こうして振り返ってみると対外的な成果は無くとも人間的にも技術者としても成長と経験を多く重ねることの出来た1年だったのではないかと思います.
学業の方に関しては, 専門科目が増えてきたことにより今まで気にならなかった学科と自分のやりたいこととのギャップに苦しんだ1年でした.
2019年で特筆すべきことはアメリカに行ったことでしょうか.
アメリカに行き多種多様な人々と会話したり,イベントに参加したりしたことにより, 自分の中の価値観が大きく変わりました. アメリカに行く前までは留年による同世代に対する遅れや高専に入って結構な年数が経ってしまい心理的に辞めるハードルが上がり辞めにくくなっていました. しかし, アメリカで 年齢,性別,人種などは関係なく, 大人でもやりたいことのために大学に入り直す人もいるし, アメリカでバスケをするためにアメリカの高校に通う同い年の日本人の子など多種多様な価値観の元で自由に生きて自分のやりたいことを追いかけている人達を沢山見ました. そこで自分も気づきました. 「自分の好きなように生きたらいいし, 世間の目なんか関係ない」 ということに. なのでこれからは僕の好きなように生きます. 学びたいことを学び, 行きたいところに行きます. たとえ中卒になろうが結婚できなかろうが悔いのない選択をしていきたいと思います.
アメリカに行ったことで自分の中の重荷が取れて楽になった気がします. アメリカに行かせてくれた私の両親に感謝の限りを捧げます.
2020年は...
2020年は人生の大きな転換期になる気がしています.
とりあえず今ある2020年の目標は
- CPU, コンパイラ, OSを作る (つまりはシステムプログラミング!)
- 自分のポートフォリオサイトを作成する.
- 毎日充実した日々を送り, 好きなことをいっぱい勉強する.
- いろんな分野をやってみて自分の進みたい道を探す
- 定期的に運動する.
- 42Tokyoに受かる!!
今年は低レイヤーなどを中心とした含めたCS系の基礎固めの年になりそうです.
プログラミングを初めて3年くらいが経とうとしていて, 最近になってやっとこCSといものを知りしっかりと勉強したいと思うようになりました. 今年は"勉強したい"ではなく"勉強する"年に出来たらいいなと思います. そして来年のこの時期に1年の振り返りをした時に今年よりも充実し満足し幸せな1年を送れたらいいなと思います.
なんだかポエムっぽい記事になっちゃいましたが, こんな感じでこの記事を締めさせていただきます. 今年もよろしくお願いします.
Python Logging SlackHandler の作り方
python で logging を使っていると, たまに 「このログをSlackに飛ばしたいなぁ」ってときがあります.
なのでログをSlackに投稿する Logging Handler 作りました.
Slack側の準備
Incoming Webhook アプリをSlackに導入します.
その他の管理項目 -> Appの管理 でワークスペースのアプリ設定ページにアクセス出来ます.
アプリの設定ページ内のAppディレクトリを検索して Incoming Webhook をSlackに追加します.
Slackに追加 を押したらどのチャンネルに送信するかというのが出てくるので ログを送信したいチャンネルを選んでください. 選んだらIncoming Webhook インテグレーションの追加 を押してください.
そうすると Incoming Webhook アプリの設定画面に飛ぶと思います.
その中にある Webhook URL をメモしておいてください.
試しに何かメッセージを送ってみましょう
import requests webhook_url = "先程メモしたWebHookURL" requests.post(webhook_url, json={"text": "Pythonから送信したテストメッセージ"})
これを実行すると先程指定したチャンネルにメッセージが来たと思います.
テキストの投稿以外にも Incoming Webhook が出来ることは沢山ありますが, 今回は扱いません. 気になる人は Slackワークスペース内にある Incoming Webhook アプリ の設定ページを見てください.
SlackHandler を作る
いよいよ本題です. SlackHandler を作っていきます.
SlackHandler を作成する前に Python Loggingモジュールにおける Handler とは何かを理解しないといけません.
Pythonの公式ドキュメント によると
ハンドラは、(ロガーによって生成された) ログ記録を適切な送信先に送ります。
らしいです.
では作っていきましょう.
handlers.py
というファイルを作って, 中身を以下の様にします.
import logging import requests class SlackHandler(logging.StreamHandler): def __init__(self, url): super(SlackHandler, self).__init__() self.url = url def emit(self, record): msg = self.format(record) self.send_message(msg) def send_message(self, text): message = { 'text': text, } requests.post(self.url, json=message)
コードの解説をします.
SlackHandler が継承している logging.StreamHandler
とは[Python公式ドキュメント] から引用すると
logging コアパッケージに含まれる StreamHandler クラスは、ログ出力を sys.stdout, sys.stderr あるいは何らかのファイル風 (file-like) オブジェクト (あるいは、より正確に言えば write() および flush() メソッドをサポートする何らかのオブジェクト) といったストリームに送信します。
つまりは, ログをどこかに送る 基礎的なHandlerです.
def __init__()
では スーパークラスである StreamHandler
のインスタンスメソッド __init__()
を呼び出して StreamHandler の各種変数やメソッドの初期化をします.
また, 引数 url で webhook_url をインスタンス変数に登録しています.
def emit(self, record)
は StreamHandler のメソッドで, 例によってPython公式ドキュメントから引用すると,
emit(record)
フォーマッタが指定されていれば、フォーマッタを使ってレコードを書式化します。 次に、レコードが終端記号とともにストリームに書き込まれます。 例外情報が存在する場合、 traceback.print_exception() を使って書式化され、 ストリームの末尾につけられます。
つまりは来たrecordをsetFormatterで設定したformat形式に変換してStreamに記録するというメソッドです. 今回の場合はSlackにログを送信したいのでこのメソッドをオーバーライドすればいいですね.
今回は来たrecordをformatの形式に変形するのはそのままに, その後Slackにメッセージを送信する関数 send_message()
を作り, それを使ってformat形式に変形されたログを送信しています.
SlackHandlerを使う
SlackHandler を使う時は他の Logging Handler とほとんど同じように使えます.
以下のコードを実行するとSlackにログが送信されます.
import logging from handlers import SlackHandler webhook_url = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # logger の作成 logger = logging.getLogger(__file__) logger.setLevel(logging.INFO) # Slack Handler の作成 slack_handler = SlackHandler(webhook_url) slack_handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s') slack_handler.setFormatter(formatter) logger.addHandler(slack_handler) def main(): logger.info("This is Logging Test!!") ans = 1 + 2 logger.info(f"1 + 2 = {ans}") if __name__ == "__main__": main()
SlackHandler (応用編)
SlackHandlerの改造
先程作成したSlackHandlerは最低限の機能を満たしていますが, 私が実際に使っているものはさらに改造を施したものです. 折角なのでご紹介します.
class SlackHandler(logging.StreamHandler): def __init__(self, url, username="", icon_emoji=":computer:"): super(SlackHandler, self).__init__() self.url = url self.username = username self.icon_emoji = icon_emoji def emit(self, record): msg = self.format(record) self.send_message(msg, record.levelno) def send_message(self, text, level_num): icon_emoji = self.icon_emoji if level_num > 30: # Warning 以上 # メンションを飛ばす text = "<@MEMBERID> " + text icon_emoji = ":exclamation:" message = { 'text': text, 'username': self.username, 'icon_emoji': icon_emoji } requests.post(self.url, json=message)
emoji
や username
に関しては Slackのワークスペース内の Incoming Webhookアプリの説明に書いてあるのでそっちを見てほしいです. また, SlackのIncoming Webhooksを使い倒す - Qiita もわかりやすくていいと思います.
それではそれ以外の部分について解説します.
send_message(self, text, levelno)
でlogging のレベルを渡しています. これにより, levelごとに処理を分けることが出来ます.
今回の場合は, WARNING以上のログはWebHookアプリのアイコンを❗に変更し, 私のアカウントに対してメンションを付けてログをSlackに送信しています. この部分をChannelIDに変えるとチャンネルに参加している全員に通知を飛ばせます.
メンションを飛ばす部分については SlackのIncoming Webhooksでメンションを飛ばす方法 - Qiita を見てください.
SlackHandler をもう少し高度に使う
先程の仕様例でもいいのですが, あれは少しスマートじゃないですし, 実際に使う際はHandlerやFormatを dictConfig
や fileConfig
で管理すると思います. 自分の場合は dictConfig
をいつも使っているので, 今回はそれで説明します.
私は dictConfig
を使って. 以下のように設定しています.
import logging import logging.config # logging LOGGING = { 'version': 1, 'disable_existing_loggers': True, 'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, }, 'handlers': { 'default': { 'level': 'INFO', 'formatter': 'standard', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', # Default is stderr }, # ファイル出力ハンドラー 'file': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': "test.log", 'formatter': 'standard', }, 'slack': { 'level': 'INFO', 'class': 'handlers.SlackHandler', 'url': os.environ.get("SLACK_WEBHOOK_URL"), 'username': 'Landing Classification Observer', } }, 'loggers': { '': { # root logger 'handlers': ['default', 'file', 'slack'], 'level': 'INFO', 'propagate': False }, } } logging.config.dictConfig(LOGGING) logger = logging.getLogger(__name__)
全ての設定を LOGGING
で管理出来るようになりました.
ここでのポイントは
SlackのWebHook URLを環境変数で管理している.
WebHook URLなどはバージョン管理などに入れたくない情報だと思うので, 環境変数に入れています. こちらのほうがセキュリティ的にもいいと思います.SlackHandler のインスタンス作成時に必要な情報(引数) を全て
LOGGING
内で完結している.
コードの見た目的にも綺麗です.
おまけ
ここからは SlackHandler に必要な訳ではないが, あると便利かも? しれないものを紹介します.
SlackHandler用に新しいレベルを作る
標準出力ハンドラー と ファイルハンドラー ではログを出力したいけど, SlackHandlerには出力しなくない,,,
だからといって SlackHandler のレベルを WARNING に設定したくはない...
ってなった時には 新しいレベルを作りましょう!!
ちなみに python の logging の標準で用意されているレベルは以下のようになっています. Python 公式ドキュメントより以下の様になっています
Level | Numeric value |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
今回は INFO ってほど気軽な感じじゃないけど, WARNINGを使うほどでもない... けど Slackに送信してほしいので INFO と WARNING の間を取って レベル25 の IMPORTANT というレベルを作りましょう.
# add new level called IMPORTANT IMPORTANT_LEVEL_NUM = 25 logging.addLevelName(IMPORTANT_LEVEL_NUM, "IMPORTANT") def important(self, message, *args, **kws): if self.isEnabledFor(IMPORTANT_LEVEL_NUM): # Yes, logger takes its '*args' as 'args'. self._log(IMPORTANT_LEVEL_NUM, message, args, **kws) logging.Logger.important = important
これで IMPORTANT
という新しいレベルが logging に登録されました.
使う際には以下の様に Handler などを設定する前に定義してから使います.
import logging import logging.config # logging settings # add new level called IMPORTANT IMPORTANT_LEVEL_NUM = 25 logging.addLevelName(IMPORTANT_LEVEL_NUM, "IMPORTANT") def important(self, message, *args, **kws): if self.isEnabledFor(IMPORTANT_LEVEL_NUM): # Yes, logger takes its '*args' as 'args'. self._log(IMPORTANT_LEVEL_NUM, message, args, **kws) logging.Logger.important = important # Define logging dict # logging LOGGING = { 'version': 1, 'disable_existing_loggers': True, 'formatters': { 'standard': { 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' }, }, 'handlers': { 'default': { 'level': 'INFO', 'formatter': 'standard', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', # Default is stderr }, # ファイル出力ハンドラー 'file': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': "test.log", 'formatter': 'standard', }, 'slack': { 'level': 'IMPORTANT', 'class': 'handlers.SlackHandler', 'url': os.environ.get("SLACK_WEBHOOK_URL"), 'username': 'Landing Classification Observer', 'formatter': 'standard', } }, 'loggers': { '': { # root logger 'handlers': ['default', 'file', 'slack'], 'level': 'INFO', 'propagate': False }, } } logging.config.dictConfig(LOGGING) logger = logging.getLogger(__name__) def main(): logger.info("This is Logging Test!!") ans = 1 + 2 logger.important(f"1 + 2 = {ans}") if __name__ == "__main__": main()
これを実行すると, 標準出力とファイル出力ハンドラーに対しては 2つのログが出力されます.
出力された test.log の中身は以下のようになっています.
2019-11-30 15:53:35,869 [INFO] __main__: This is Logging Test!! 2019-11-30 15:53:35,870 [IMPORTANT] __main__: 1 + 2 = 3
そして, 肝心のSlackの方ですが, 以下のように IMPORTANT のログのみが送信されています.
いい感じですね!!
画像をSlackに自動投稿する
これは色んな所で既出なのであんまりガッツリは書きません. 詳しい解説は他のサイトを見てください.
コードだけ貼ります.
import requests def upload_image(token, image, filename="", channels="logging", title="", initial_comment=''): """ 画像をSlackワークスペース内のチャンネルにアップロードする. 別に画像以外でもいける Parameters ---------- token : str Slack API Token. 取得方法はggって image : binary 画像のバイナリーデータ. ファイルから読み込む時は open("filename", "rb") np.ndarray形式で変数imgに保持している時は cv2.imencode('.jpg', img)[1].tostring() filename : str, optional slackに投稿される画像のファイル名, by default "" channels : str, optional 投稿先のチャンネル名, by default "logging" title : str, optional Slack上でのタイトル, by default "" initial_comment : str, optional コメント(メッセージ), by default '' """ files = {'file': image} params = { 'token': token, 'channels': channels, 'filename': filename, 'title': title, 'initial_comment': initial_comment } url = "https://slack.com/api/files.upload" requests.post(url=url, params=params, files=files)
上記のコードでわからなかった場合は以下の記事読んでください.
あとがき
今回は Slack にログを送信できる SlackHandler を作成しました.
そもそもこれを作ろうと思ったのは機械学習の学習の学習の経過をSlackに送信したかったからです. そこで方法を調べたのですが, 多くの記事が別に関数を作って実装していました. 個人的には ログ関係は全て Logging にまとめてしまいたかったので今回SlackHandlerを作成しました. 調べた感じSlackHandlerについて書いてある記事は 日本語,英語 共に無かったのでもしかして初かな? 初だと少し嬉しいかもですね.
また, おまけの 新しいレベルを作る 部分は正直必須ではないです. 昔結構苦労して作ったのに現在使っていなくて悲しいので作り方を忘れる前に残しておきたかったので書きました. またどこかで使うかもしれないのでね.
もし, 参考になったり, 使って便利だったらこの記事のリンクを貼ったり, 共有してくれたら嬉しいです. 結構書くの時間かかったので.
てか今思ったんですけど, Slack APP に WebHook と uploadfile の権限持たせれば1つに統合出来るやん.
HHKB をUbuntuで使う
俺「HHKB気になるなぁ〜触ってみたいなぁ〜」
後輩「私のHHKB, 数日貸しましょうか?」
俺「😘😘😘😘😘😘😘😘😘😘😘😘😘」
ってことで前から触ってみたいなぁって思ってたHHKB(US)を後輩から貸してもらいました. やっぱり持つべきものは可愛い後輩ですね.
で
UbuntuでHHKB(US) を使うのに結構苦労したのでブログに残しておきたいと思います.
環境
- HHKB Professional 2 US
- Ubuntu 18.04 LTS
また, 以下のソフトウェアはインストール済みだと想定しています.
- fcitx
- mozc
PID スイッチの設定
HHKBにはPIDというキーボードの出力信号をハードウェア的に変えれるのがあるらしいですね,
画像は 公式サイト より.
PIDスイッチは以下のようにしました.
PW1 | PW2 | PW3 | PW4 | PW5 | PW6 |
---|---|---|---|---|---|
OFF | ON | ON | ON | OFF | ON |
Macモードにしているのはこうしないと音量調整のショートカットキーが使えなくて不便だからです. UbuntuでMacモードを使っていても特に違和感は感じないのでMacモードで大丈夫だと思います.
このPIDスイッチスイッチの構成は以下のブログのものを使いました. 細かい説明などはそちらでわかりやすく解説してらっしゃるので気になる方は見てください
Ubutu キーボード設定
以下のコマンドでHHKB(US)を指定します.
$ dpkg-reconfigure keyboard-configuration
/etc/default/keyboard
の中身が先程設定したものになっているか
$ cat /etc/default/keyboard
こんな感じだったらOK
# KEYBOARD CONFIGURATION FILE # Consult the keyboard(5) manual page. XKBMODEL="hhk" XKBLAYOUT="us" XKBVARIANT="" XKBOPTIONS="" BACKSPACE="guess"
fcitxとmozc設定
/usr/share/ibus/component/mozc.xml
の中の <layout>default</layout>
を <layout>en</layout>
に変える
$ cat /usr/share/ibus/component/mozc.xml <component> <version>2.20.2673.102+dfsg-2ubuntu0.18.04.1</version> <name>com.google.IBus.Mozc</name> <license>New BSD</license> <exec>/usr/lib/ibus-mozc/ibus-engine-mozc --ibus</exec> <textdomain>ibus-mozc</textdomain> <author>Google Inc.</author> <homepage>https://github.com/google/mozc</homepage> <description>Mozc Component</description> <engines> <engine> <description>Mozc (Japanese Input Method)</description> <language>ja</language> <symbol>あ</symbol> <rank>80</rank> <icon_prop_key>InputMode</icon_prop_key> <icon>/usr/share/ibus-mozc/product_icon.png</icon> <setup>/usr/lib/mozc/mozc_tool --mode=config_dialog</setup> <layout>en</layout> <name>mozc-jp</name> <longname>Mozc</longname> </engine> </engines> </component>
mozcは直接入力と日本語入力の2種類の状態を持っているので, fcitxの設定で入力メソッドをmozcのみにします.
その下側にある歯車とキーボードアイコンでキーボードのレイアウトの設定が出来るのでUS配列に変更します
入力メソッドがmozc1つだけなので入力メソッド切り替えのショートカットキーは必要無いのでショートカットキーを無効にします
そして, US配列では全角半角キーを使った入力切り替えが出来ないので別のキーを割り当てます.
別のキーの割り当て方は, mozcの設定の中のキーマップのところから変更します.
ベースにするキーマップは MS-IME
を使います.
keymap style の中で 半角全角キーを使っている動作を変更します.
半角全角キーを使っている動作はIMEの 有効/無効 だけなのでこれに別のキーを割り当てます. Key部分を3回クリックすることでキーの変更画面が出てくるのでそこで変更出来ます.
自分は以下のように設定しました.
以上でUS配列で 日本語入力/英語入力 の切り替え, 入力が出来るようになったはずです
matplotlibで高画質のfigure画像をnumpy arrayで手に入れる
この記事の目的, 背景
matplotlibではプロットや画像の表示が出来ます. そしてそれを画像ファイルとして出力することも出来ます.
しかし, tensorboardへの記録やその後の画像の加工などがしたい場合には 画像ファイルでは無く numpy array 形式で欲しい時があります.
しかし, 調べて出てきたやり方では低画質になってしまうという欠点がありました.
なので, 高画質な状態で numpy array 形式に出力するやり方を考えたのでそれについて書きます.
ベースとなるプロット
今回は定番のsin波をベースにしたいと思います.
import numpy as np import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111) x = np.linspace(-np.pi, np.pi) ax.set_xlim(-np.pi, np.pi) ax.set_xlabel("x") ax.set_ylabel("y") ax.plot(x, np.sin(x), label="sin") ax.legend() ax.set_title("sin(x)")
プロットは以下のようになります.
従来の方法
savefig()
matplotlib の関数として savefig()
というものがあり, それを使うと 画像ファイルとして出力できます.
fig.savefig("sin.png")
高画質で素晴らしいのですが, 我々が今欲しいのは numpy array 形式なのでこれは却下です.
もちろん, 出力したファイルを読み込めば numpy array 形式で扱えますけどね. なんか違うんだよなぁ.
プロットを文字列にして, それを numpy で読み込む
私が検索した限り, この方法が多かったです.
fig.canvas.draw() data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8) data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))
このコードは上手く動作します!! figure を numpy array で入手できてるしいいんじゃない?
だがしかし, 私はやり方では満足出来ませんでした.
以下が 上記の data
変数を画像として出力したものです.
画質悪くない??
今回のプロットが簡単なのであまり気にならないのですが, これが大量のデータのプロットなどになるとプロットが潰れて見るに耐えないものになります.
今回の手法
従来の2つの手法はまとめると以下のようになります
savefig()
: 画質は良いが, 画像ファイルが出力されるため numpy array にするのに 出力画像を読み込まないといけないのでめんどくさい.np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
: figure をファイルI/O無しで numpy array として入手出来るのはいいが, 画質がいまいち.
そして, 今回の私の手法がこれです. 上記2つの手法を組み合わせたとも言えなくないかも?
import io import cv2 buf = io.BytesIO() # インメモリのバイナリストリームを作成 fig.savefig(buf, format="png", dpi=180) # matplotlibから出力される画像のバイナリデータをメモリに格納する. buf.seek(0) # ストリーム位置を先頭に戻る img_arr = np.frombuffer(buf.getvalue(), dtype=np.uint8) # メモリからバイナリデータを読み込み, numpy array 形式に変換 buf.close() # ストリームを閉じる(flushする) img = cv2.imdecode(img_arr, 1) # 画像のバイナリデータを復元する img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # cv2.imread() はBGR形式で読み込むのでRGBにする.
そして img
変数に入っている numpy array を画像として出力したものがこちらです
いい感じじゃないですか?
また, fig.savefig(buf, format="png", dpi=180)
の dpi
の値を変えることで画質を変えれます.
参考にしたリンク
【書評】x86本を読んだ
x86本, 正確には 「自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解!」を読んだのでそれの感想を書きます.
また, この本の想定環境はWindowsですが, 自分はLinux環境なので, Linux環境で実行する際に気をつけるべきポイントも書きました.
ちなみに, 書評を書くのはこれが初めてなので文章が多分下手くそです. 保険を掛けていく
各章の感想
Chapter 1 C言語とアセンブリ言語
この本の最初となる第1章では C言語と機械語, アセンブリ言語 の関連性や特徴についての説明されています.
また, アセンブリ言語の基本命令, 2進数, 16進数, アセンブル, ディスアセンブル(逆アセンブル)のやり方などのこの後の章を読む上でベースとなる要素を解説してくれます.
基本的にこの本はわからないことがあっても読み進めていくうちにわかるようになることがあるので, わからないところがあっても基本的にはそのまま読み進めれば良いのですが, 2進数と16進数だけは出来ると出来ないではこの後の章での理解度が変わってきますので必ず理解するべきだと思います.
Chapter 2 ポインタとアセンブリ言語
この章から本格的に CPUとメモリ, ポインタ について解説が入り, そしてエミュレータ作成が始まります.
前半はレジスタやメモリの解説, 後半ではポインタの復習とプログラムを実行する上でのポインタの役割などについて説明してくれます.
CPUとメモリ, ポインタの関係性がよく理解出来ました. また, 初めてエミュレータを使いバイナリファイルを動かした時には少し感動しました.
Chapter 3 CPUがプログラムを実行する仕組み
この章では章のタイトルにもあるとおり, 今まで学んできた ポインタやアセンブリ, CPU, メモリ, etc... などの各要素が1つに集まり実際にプログラムを構築し, 実行するのが理解出来る最高に興奮する章です.
3章内にある "3.3 プログラムの実行" というセクションでは x86アーキテクチャの代表的なCPU i386を対象に各レジスタの説明と, 演算処理, メモリと補助記憶装置のCPU側から見た違い, 機械語命令の構成など盛り沢山で大変読み応えがあり, 私が一番好きなセクションでもあります.
あと, "3.6 call命令とスタック", "3.9 フラグレジスタと条件分岐命令" もとても興奮しました. 各セクションごとに感想を書くと切りがないので書きませんが, きっと読めば解ると思います.
この3章は1,2章で学んできた知識がつながる一番楽しい章だと個人的には思います. この章を読み終わる頃にはC言語をコンパイルしバイナリファイルを生成し, そのバイナリファイルがどのようにCPUやメモリなどを使い動いているかがイメージ出来るようになっていると思います.
Chapter 4 BIOSの仕組みと実装
この章では BIOS の文字表示機能を実際にエミュレータに組み込み, アセンブリでソフトウエア割り込みを使ってBIOSの機能を呼び出し任意の文字列を出力するコードを作成し, 実際にそれを動かします. また, HDDやUSBなどの記憶媒体の記憶方式について解説し, 実際にUSBメモリのPBRを実際に見て, その後アセンブリ言語プログラムをPBRとして認識されるように作り 実機で動かします.
この章でパソコンの電源を押した瞬間からOS起動までの流れをつかむことが出来ました.
また, 割り込み処理の部分は 「なるほど,うまく出来てるなぁ〜」と感心しました.
全体を通しての感想
自分はアセンブリもCPUの各レジスタもなんもわからないレベルから プログラムが実行される際のCPUとメモリの動きや, ポインタの重要性を理解し, アセンブリも読めて, 少し書けるくらいになりました..
初めて低レイヤーに触ったのですが, 特に辛い思いをせず, 楽しく学べました. 私はこの本で初めて低レイヤーについて学びましたが, 読み終わった今では読む前よりも 低レイヤーに学びたい という気持ちが強まりました. そういう意味でも, 低レイヤーに興味がある人が初めて読むのにとても良いと思いました.
読み進めていくうちに各要素が繋がっていき, 段々理解出来るはやはり楽しかったです.
本自体も200ページ弱で, 大変読みやすいと思います. また, 図やソースコードが豊富であり, 各要素についての説明も丁寧に細かく書いてあるのでC言語の入門が終わってる人なら誰でも楽しめると思います.
LInux環境で実行する際の注意点
ここからは本の感想から少し離れて Linux環境で実行する際に注意した方がいいところをいくつか挙げます.
インストールするソフトウェア
NASM
$ sudo apt install nasm
この記事がいい感じにまとまってます. : Linuxでx86アセンブラ(道具編)
gcc
以下の記事見てください. linuxize.com
変更が必要な書籍の中で実行しているコマンド
いくつかのプログラムにて, make
が上手くが上手く行きません. 原因は Windows 用に Makefile
が作られており, その中でコマンドツールの指定の仕方が Windows 仕様だからです.
書籍内の多くの Makefile
はさほど複雑なことはしていないので, make
に失敗したら自分で Makefie
を少しイジるか, 自分で gcc
などを使ってコンパイルすればいいと思います.
3.7
p115のアセンブラとcのオブジェクトファイルを作成しリンクして実行ファイルを作るところ
$ nasm -f elf32 crt0.asm $ gcc -c -nostdlib -fno-asynchronous-unwind-tables -m32 -fno-pie -g -o test.o test.c $ ld --entry=start --oformat=binary -m elf_i386 -Ttext 0x7c00 -o test.bin crt0.o test.o
次の記事を参考にしました. ldコマンドの説明
4.5
このセクションでは dd コマンドと boots という著者が作成したツールを使い USBメモリのPBRを見たり書き込んだりします.
dd コマンドについては Linux に標準で搭載されているので良いのですが, boots の方が自分の環境では依存しているライブラリのインストールが出来ずに失敗しました.
誰か出来た人おしえてください.
完走した感想 (おまけ)
完走した感想ですが, 最後の章のUSBのPBRを見る部分が出来なかったのが少し心残りです. 誰かLinux環境で動かせた人はコメントか何かで教えてください.
ここ最近は流行りの技術を追いかけるのではなく, コンピュータサイエンスをしっかりやろうと思っており, その第一歩として低レイヤーに触り始めました.
低レイヤーに関してはこの本が本当に初めてで, ちゃんと読めるか少し不安でしたが, しっかり読めました. いきなり激ムズな本だと心が折れますからね.
今日においては 別に低レイヤーをやらなくても高級言語やライブラリを触るだけでプログラムは書けてしまうのですが, やはり低レイヤーをブラックボックスとして意識せずにコードを書くのと, ある程度理解してコードを書くのとではやっぱり違ってくると思います.
今回で低レイヤーのCPUでの演算や機械語の実行方法などがわかったわけですが, 実はまだ少しモヤっとしていて, それは CPUがなぜそのような動きをするのかがわからない ということです.
勿論今回の書籍で扱っているx86を始めとしたCPUアーキテクチャで決められているからというのはわかっていますが, そうではなく, CPUを回路レベルで知りたいのです.
ということで次はこの本をやります(予定)
この本も読み終わったら書評を書きたいと思っていますが, この本に関しては本で知識を得るだけではなく 実際にCPUを自ら作成するところまでやりたいと思っています. だいぶ時間が掛かりそうですね.
書評を完走した感想ですが, いかんせん日本語が下手くそですね. 小学生の頃に書いた作文の方が日本語上手い気がします. Twitterでオタク構文ばっかり使ってるからですかね.
あと, 本と書籍の2つのワードが混在しているのでどちらかに揃えたい気はします.
それでは次の記事でまたお会いしましょう. さよなら〜.
Pipenvメモ (随時更新)
Pipenv使ってたら時々躓いて,そんで解決して, メモしてって感じなのですが, 今回の記事はそのメモをコピペしただけです. まぁ, 要は備忘録です.
では本編どうぞ.
Packages
Pipfile からパッケージを一括インストール
pipenv install
開発用パッケージも一緒にインストール
pipenv install --dev
Pipfile を参照してシステムにパッケージをインストールする
pipenv install --system
新しいパッケージをpipenvにインストール
pipenv install <pkg_name>
開発時にしか使わないパッケージをpipenvにインストール
pipenv install --dev <pkg_name>
パッケージのアンインストール
pipenv uninstall <pkg_name>
インストールされているパッケージの一覧を確認
pipenv graph
インストールされているパッケージの一括バージョンアップ
pipenv update
パッケージ毎に個別でアップデート
pipenv update <pkg_name>
Pipfile.lock を用いることで安全で確実なインストールが可能だが、依存関係の計算に時間がかかるため、待てない場合は
--skip-lock
オプションを使うことで Pipfile.lock の計算をスキップできる. referencepipenv install --skip-lock
Pipfile.lock を作成
pipenv lock
VCS(gitとか) からインストール
ex (albumentations):
pipenv install --editable git+https://github.com/albu/albumentations.git#egg=albumentations
Run
pipenv で作成した環境に入る
pipenv shell
pipenv shell
に入っていない状態でpipenvの環境上でpythonスクリプトを動かすpipenv run python script.py
Other
pipenvの仮想環境のパスを確認
pipenv --venv
pipenv 仮想環境の削除の仕方
pipenv --rm
Anacondaを使っている環境上でpipenvを使う方法
Install pipenv
sudo apt install python3-pip
pip3 install pipenv
create pipenv environment on anaconda
conda create -n pipenv_test python=3.6
source activate pipenv_test
pipenv install --python=$(which python)
pipenv shell
参考リンク: Pipenv with Conda?
Error
pipenv が
Not Found
ってなるときはPATHが通っていないので 以下を.bashrc
とかに追記する pipenv command not found - stackoverflowbash PYTHON_BIN_PATH="$(python3 -m site --user-base)/bin" PATH="$PATH:$PYTHON_BIN_PATH"
Pipfileで指定したバージョンのPythonが無いとき以下のようなエラーが出る
Warning: Python 3.6 was not found on your system… You can specify specific versions of Python with: $ pipenv --python path/to/python
pyenvをインストールして、そのバージョンをインストールすればいい. pyenv-installer を使うと楽.
$ curl https://pyenv.run | bash
.bashrcに以下を追記
bash export PATH="/home/user/.pyenv/bin:$PATH" eval "$(pyenv init -)" eval "$(pyenv virtualenv-init -)"
pyenvで他のバージョンをインストールする際にいくつかライブラリが必要なのでインストールする(初回)
$ sudo apt-get install zlib1g-dev libssl-dev
pyenvについてのいくつかのリンク