【markdown-it】markdownをパースするHTMLを作ってみた

要点

動機

これまで、 markdown ファイルは markdown viewer (Firefox Add-on)を使って表示していたのだが、 他人に見せるときには必ずしもそのアドオンがあるわけでなく、生のmarkdownが表示されることがあった。

そこで、markdownを表示するHTMLを作ってみることにした。

方法

方法は次の手順に分けて対応した。

  1. markdown を読み込む
  2. markdown をパースする

1. markdown を読み込む

markdown の読み込みには、Fetch API を使った。

developer.mozilla.org

Web素人なため、やりたいことがうまく説明できずたどり着くまでに時間がかかったが、 Fetch API により、同じフォルダにある markdown を読むことができた。

fetch('./index.md')
  .then(response => {
    return response.text();
  })
  .then(body => {
     document.body.innerHTML = body;
  });

2. markdown をパースする

markdown のパースには、marked と markdown-it の2つのライブラリが考えられた。

marked を先に知っていたので、まずはこちらでと考えていたが、 markdown-it が拡張性に富んでいるの点と、実際に使っていてオススメだという周囲の助言があったので、markdown-it を採用した。

fetch('./index.md')
  .then(response => {
    return response.text();
  })
  .then(body => {
     var md = window.markdownit();
     document.body.innerHTML = md.render(body);
  });

ライブラリは、環境構築を極力なくすため、 cdnjs から利用することにした。

<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/8.4.1/markdown-it.min.js"></script>

これでもそこそこパースできたが、タイトルがファイル名となってしまいかっこ悪かった。

動機の原点である markdown viewer では、最初のヘッダーがタイトルになっていたので、 これを実現することにした。

実現には、作者 Keith L Robertson 氏のリポジトリを参考とした。

github.com

結果

f:id:dNaga392:20180320011038p:plain

簡単な markdown を HTML に表示することができた。

拙い出来ではあるが、githubに公開した(https://github.com/dNaga392/md-index)。 興味があれば、参考にしたりマサカリ投げていただきたい。

参考

【matplotlib】散布図を3D描画する

要点

使用例

import matplotlib.pyplot as plt
import seaborn as sns
from mpl_toolkits.mplot3d import axes3d  # Axes3D のために必要

df = sns.load_dataset('iris')
xs = df['sepal_length']
ys = df['sepal_width']
zs = df['petal_length']

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')

ax.scatter(xs, ys, zs)

ax.set_title('Matplot 3d scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル
ax.set_zlabel('petal_length')  # Z軸ラベル

plt.show()

mpl-ax3d-scatter.png

特定のグループごとに描画する場合

hue = 'species'
labels = set(df[hue])
dataset = []
for x in labels:
    xs = df[df[hue]==x]['sepal_length']
    ys = df[df[hue]==x]['sepal_width']
    zs = df[df[hue]==x]['petal_length']
    dataset.append((xs, ys, zs))

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1, projection='3d')
for data, label in zip(dataset, labels):
    ax.scatter(xs, ys, zs, label=label)
ax.set_title('Matplot 3d scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル
ax.set_zlabel('petal_length')  # Z軸ラベル
ax.legend(loc=2, title='legend', shadow=True)  # 凡例

plt.show()

mpl-ax3d-scatter-hue.png

【matplotlib】散布図を描画する

要点

使用例

import matplotlib.pyplot as plt
import seaborn as sns

df = sns.load_dataset('iris')
x = df['sepal_length']
y = df['sepal_width']

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)

ax.scatter(x, y)
ax.set_title('Matplot scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル

plt.show()

mpl-ax-scatter.png

特定のグループごとに描画する場合

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)

hue = 'species'
labels = set(df[hue])
dataset = [(df[df[hue]==x]['sepal_length'], df[df[hue]==x]['sepal_width']) for x in labels]
for data, label in zip(dataset, labels):
    x, y = data
    ax.scatter(x, y, label=label)
ax.set_title('Matplot scatter plot')  # タイトル
ax.set_xlabel('sepal_length')  # X軸ラベル
ax.set_ylabel('sepal_width')  # Y軸ラベル
ax.legend(loc='best', title='legend', shadow=True)  # 凡例

plt.show()

mpl-ax-scatter-hue.png

実を言うとTab Groupsはもうだめです。

f:id:dNaga392:20170130224504p:plain

今朝 Firefox を開くと、Tab Groups からの last notify のページが開きました。

記事によると、これまでアドオンとして提供されていたTab Groupsの開発が中止されるとのことでした。

タブグループ機能がFirefox本体から廃止され、その代わりになったのがこのTab Groupsで、 これまではQuicksaver氏が開発を続けてきました。

Quicksaver氏のTab Groupsは、軽快で安定した動作の高品質なアドオンで、 私のインターネットライフをより良いものとしてくれました。

しかしながら、Mozillaの示したFirefoxの今後の方針は、 アドオン開発の継続に大きな負荷をかけるものでした。

Quicksaver氏によると、いくらか抵抗し意見を交わしたものの Mozzilaの方針は変わることはなかったそうです。

そのため、開発を続けることに限界を感じたQuicksaver氏は Tab Groupsを始め、彼のアドオンの開発をすべて終えることにしたということです。

愛用していたTab Groupsの開発終了は大変残念ですが、 これをきっかけにタブの整理を進めてみるのもいいかもしれません。

これまで高品質なアドオンを提供してくれたQuicksaver氏に深い感謝を。


追記

forest.watch.impress.co.jp

そこで従来型アドオンは、順次“WebExtensions”ベースのアドオンへと置き換えられる。具体的には、4月に「Firefox 53」がリリースされるタイミングで“addons.mozilla.org(AMO)”における従来型アドオンの受け付けが終了となり、11月の「Firefox 57」以降は従来型アドオンがサポートされなくなる。

11月がリミットっぽい

【Git】 fatal: not under version control, source=A.txt, destination=B.txt

Gitで表題のようなエラーが出た。

ファイル名をチェックすると、ファイル名の一部が大文字小文字で異なっていた。 これは git を介さずにファイル名を変更したためとのこと[1]。

解決法には以下の2つがある。

  • 元のファイル名に戻す。
  • ファイル名変更をgitに伝える。

ここでは後者の方法を提示する。 手順は次の通りである。

  1. 元のファイル名でgit mv コマンドを実行する。
> ls
a.txt
> mv a.txt A.txt
> ls
A.txt
> git mv A.txt B.txt
fatal: not under version control, source=A.txt, destination=B.txt
> ls
A.txt
> git mv a.txt B.txt
> ls
B.txt

あるいは、ファイル名自体を変更する方法もあるが、手間なのでお勧めはしない。

> ls
a.txt
> mv a.txt A.txt
> ls
A.txt
>ls
A.txt
> mkdir dirB
> git mv *.txt dirB
fatal: not under version control, source=A.txt, destination=dirB/A.txt
> mv A.txt _A.txt
>ls
_A.txt
> git add .
> git commit -m "rename temp filename"
> mv _A.txt A.txt
>ls
A.txt
> git add .
> git commit -m "rename true filename"
> git mv *.txt dirB
> cd dirB
> ls
A.txt

参考資料

[1] rcmdnk.github.io

【Qt】ウィジェットのスクリーンショット

メインウィンドウをスクリーンショットする

メインウィンドウのスクリーンショットを撮る場合、 QPixmap::grabWidget を利用する。

    // メインウィンドウのスクリーンショット
    pixmap = QPixmap::grabWidget( this );
    // クリップボードにビットマップをコピー
    QClipboard * clipboad = qApp->clipboard();
    clipboad->setPixmap( pixmap );

ただし、この場合ウィンドウフレームは含まれない。 これはウィンドウフレームがQtの処理範囲外のためである。

そのため、ウィンドウフレームを含める場合は、 より大きい範囲、スクリーンのスクリーンショットを撮ってトリミングをする。

    QDesktopWidget dw;
    QWidget * screen=dw.screen( dw.screenNumber(this) );
    QRect rect = geometry();
    WId wid = screen->winId();
    // タイトルバーの高さを取得
    QStyleOptionTitleBar options;
    options.initFrom(this);
    int tbh = this->style()->pixelMetric( QStyle::PM_TitleBarHeight, &options, this ) - 4;
    // QPixmap::grabWindow(WId,int,int,int,int) の実行に必要なアトリビュートを設定
    setAttribute( Qt::WA_TranslucentBackground );
    // ウィンドウフレーム描画の数値を設定
    // 以下の6変数はWin7の実測値のため、実行環境の影響を受ける
    const int fw = 8; ///< frame width
    const int fh = 5; ///< frame height
    const int cx = rect.x() - fw;                  ///< caption x
    const int cy = rect.y() - (tbh + fh - 3);      ///< caption y
    const int cw = rect.width() + fw * 2;          ///< caption width
    const int ch = rect.height() + (tbh + fh * 2); ///< caption height
    pixmap = QPixmap::grabWindow( wid, cx, cy, cw, ch );

    // スクリーン外の非表示部分も描画するため、
    // ウィジェット部分を再描画する。
    QPixmap pix_wdt = QPixmap::grabWidget( this );
    QPainter painter( &pixmap );
    painter.drawPixmap( fw, (tbh + fh - 3), pix_wdt );
    // クリップボードにビットマップをコピー
    QClipboard * clipboad = qApp->clipboard();
    clipboad->setPixmap( pixmap );

コメントに記載したようにリテラル整数は実測値のため、 環境によっては意図する結果が得られない場合がある。

MDIのサブウィンドウをスクリーンショットする

MDIサブウィンドウの場合、ウィンドウフレームもQtの処理範囲に含まれる。 そのため、QMdiSubWindow を QPixmap::grebWidget することでフレーム付きスクショが撮れる。

    // ウィンドウフレームを含むスクリーンショット
    QMdiSubWindow * activeWindow = mdiArea->activeSubWindow();
    if ( activeWindow == NULL)
    {
        return false;
    }
    Q_ASSERT( activeWindow );
    pixmap = QPixmap::grabWidget( activeWindow );
    // クリップボードにビットマップをコピー
    QClipboard * clipboad = qApp->clipboard();
    clipboad->setPixmap( pixmap );

ウィンドウフレームなしのスクリーンショットの場合、 QMdiSubWindow の widget を QPixmap::grebWidget することで得られる。

    // ウィンドウフレームを含まないスクリーンショット
    QMdiSubWindow * activeWindow = mdiArea->activeSubWindow();
    if ( activeWindow == NULL)
    {
        return false;
    }
    Q_ASSERT( activeWindow );
    pixmap = QPixmap::grabWidget( activeWindow->widget() );
    // クリップボードにビットマップをコピー
    QClipboard * clipboad = qApp->clipboard();
    clipboad->setPixmap( pixmap );

参考資料

【Qt】Sleep()の実装例

QtによるSleepメソッド実装方法を記します(ほぼ参考資料の転載です)。

Sleep は指定時間の間、処理の実行を中断するメソッドです。 実装により次の2種類が存在しています。

  • 秒指定
  • ミリ秒指定

前者には C++標準ライブラリ のSleepメソッドが、 後者には Windowライブラリ のSleepメソッドがあります。

本記事ではこの2種類を対象に実装例を示します。

Sleep(秒指定)

#include <QtCore/QThread>

void Sleep( unsigned long secs )
{
    QThread::sleep( secs );
}

Sleep(ミリ秒指定)

#include <QtCore/QEventLoop>
#include <QtCore/QTimer>

void Sleep( int msec )
{
    QEventLoop loop;
    QTimer::singleshot( msec, &loop, SLOT( quit() ) );
    loop.exec();
}

参考資料

以上