もちっとメモ もぐりのエンジニアが日々の中で試してみたことを気が向いたときに書き連ねていきます 2023-06-11T20:30:00+09:00 T-N-Clark Hatena::Blog hatenablog://blog/10328749687190547204 技術書典14に参加しました hatenablog://entry/820878482939344153 2023-06-11T20:30:00+09:00 2023-06-11T20:30:00+09:00 お久しぶりです。 このブログも実に2年ぶりの更新になるようです。 いったいどんだけサボってるんだ... さて、6/4(日)に技術書典14が終了してはや1週間がたちました。 無事終われてよかったです。 techbookfest.org 技術書典(ぎじゅつしょてん)は新しい技術に出会えるお祭りです。 技術書典は「いろんな技術の普及を手伝いたい」という想いではじまりました。技術書を中心として出展者はノウハウを詰め込み、来場者はこの場にしかないおもしろい技術書をさがし求める、そんな技術に関わる人のための場として我々は技術書典を開催しています。技術書典公式サイトより 弊サークルも参加しており、これで累計… <p>お久しぶりです。 このブログも実に2年ぶりの更新になるようです。 いったいどんだけサボってるんだ...</p> <p>さて、6/4(日)に技術書典14が終了してはや1週間がたちました。 無事終われてよかったです。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fevent%2Ftbf14" title="技術書典14" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/event/tbf14">techbookfest.org</a></cite></p> <blockquote><p>技術書典(ぎじゅつしょてん)は新しい技術に出会えるお祭りです。 技術書典は「いろんな技術の普及を手伝いたい」という想いではじまりました。技術書を中心として出展者はノウハウを詰め込み、来場者はこの場にしかないおもしろい技術書をさがし求める、そんな技術に関わる人のための場として我々は技術書典を開催しています。<a href="https://techbookfest.zendesk.com/hc/ja/articles/360050930552-%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8%E3%81%A8%E3%81%AF%E3%81%AA%E3%82%93%E3%81%A7%E3%81%99%E3%81%8B">技術書典公式サイトより</a></p></blockquote> <p>弊サークルも参加しており、これで累計3回目の参加になりました。</p> <p>ご購入いただいた方、またオフラインでスペースにお立ち寄りいただいた方、ありがとうございました。</p> <p>今年のオフラインの来場者数は2100人だったようです。 <blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8?src=hash&amp;ref_src=twsrc%5Etfw">#技術書典</a> 14オフライン会場は17時をもちまして閉幕いたしました。オフライン会場の来場者数は2,100人でした!次回、技術書典15のオフライン開催は11月12日(日)を予定しています。そして技術書典14のオンラインマーケットでの開催はまだまだ続きます!紙の本も送料無料📚️✨<a href="https://t.co/REXhs26mJt">https://t.co/REXhs26mJt</a> <a href="https://t.co/5Vn6943QfP">pic.twitter.com/5Vn6943QfP</a></p>&mdash; 技術書典 公式アカウント (@techbookfest) <a href="https://twitter.com/techbookfest/status/1660195494986133508?ref_src=twsrc%5Etfw">2023年5月21日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>コロナ前に比べるとさすがに劣りますが、着実に賑わいを取り戻しているようでうれしいです。 <figure class="figure-image figure-image-fotolife" title="参加者数の推移~技術書典スポンサー資料より~"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611162453.png" width="1022" height="569" loading="lazy" title="" class="hatena-fotolife" style="width:700px" itemprop="image"></span><figcaption>参加者数の推移~技術書典スポンサー資料より <a href="https://techbookfest.org/assets/tbf14/for-sponsors.pdf">https://techbookfest.org/assets/tbf14/for-sponsors.pdf</a>~</figcaption></figure></p> <p>やっぱり、直接お会いしてお気に入りの技術に関して話せるというのは楽しいですよね!</p> <p>弊サークルでは、今回は既刊に加えて2冊の新刊を頒布しました。</p> <p><figure class="figure-image figure-image-fotolife" title="お品書き"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611174357.png" width="849" height="1200" loading="lazy" title="" class="hatena-fotolife hatena-fotolife-height-only" style="height:500px" itemprop="image"></span><figcaption>お品書き</figcaption></figure></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2FwSCmsmFye1bL6xDWRT6vVK%3FproductVariantID%3Dk23bJJdV1bg5bWWvdKwbFq" title="今日から始めるSolrベクトル検索:もちっとカフェ" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/wSCmsmFye1bL6xDWRT6vVK?productVariantID=k23bJJdV1bg5bWWvdKwbFq">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F25jwQ28FuePFRVHcjJJHAw%3FproductVariantID%3D6Mdsp9HhPjM958NK6jPDyx" title="はじめてのPySpark~機械学習にも触れるよ~:もちっとカフェ" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/25jwQ28FuePFRVHcjJJHAw?productVariantID=6Mdsp9HhPjM958NK6jPDyx">techbookfest.org</a></cite></p> <p>その他既刊についてはサークルページをご覧くださいませ。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Forganization%2F6273623876698112" title="もちっとカフェ | 技術書典" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/organization/6273623876698112">techbookfest.org</a></cite></p> <h2 id="執筆の裏話">執筆の裏話</h2> <p>今回は新刊2冊出しており、1冊は私著でもう1冊は友人に書いてもらいました。</p> <p>PySpark本は、友人のTくんに書いてもらいました。 難易度的にもページ数的にも読みやすい1冊になっています。 また一人技術同人沼に引き込んでしまいましたが、執筆以外の全面サポートしたおかげか、また次も参加したいと言ってくれたのでよかったです。</p> <p>そして、私が執筆したのはもう1冊のSolr本です。 ひと言で言うと、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C1%B4%CA%B8%B8%A1%BA%F7%A5%A8%A5%F3%A5%B8%A5%F3">全文検索エンジン</a>であるSolrと使って、今はやりのベクトル検索をやろうという本です。</p> <p>そのページ数は、なんと驚異の318ページです。 なるべく多くの人に手に取ってもらいたいなと思い、書きたいことを全部詰め込んだ結果、こんなになってしまいました。 アホみたいなページ数ですよね。 出来上がりを見て自分でもびっくりしました。 こんなにも頭の悪いページ数のレビューをしてくれたSさんありがとう。 会場で直接お話ししたみなさんが口をそろえて、「分厚!」と言っていたのでトチ狂ったページ数であることは間違いなさそうです。</p> <p>その甲斐あってか、思いのほかいろいろな方に手に取っていただけたようで嬉しかったです。 「はじめに」にも書いた通り、ある程度<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B8%A1%BA%F7%A5%A8%A5%F3%A5%B8%A5%F3">検索エンジン</a>特にSolrの運用経験のある人をターゲットにしていたのですが、そうでない方にも興味を持っていただけました。</p> <p>実は、分厚すぎるということで、急きょ当初のターゲット層に絞ったコンパクト版も用意しておりました。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2FkeyJrT113fr10W65aKNRxT%3FproductVariantID%3DvP825RKua0vwphNtvm03Hm" title="今日から始めるSolrベクトル検索~コンパクト版~:もちっとカフェ" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/keyJrT113fr10W65aKNRxT?productVariantID=vP825RKua0vwphNtvm03Hm">techbookfest.org</a></cite></p> <p>今回は3か月前から執筆にとりかかったのですが、えらい時間がかかりました。</p> <p>ボリュームを考えたらそりゃそうだとと言われそうですが、実は内容量もさることながら、地味に表紙にも時間と手間がかかりました。 表紙制作に苦戦したため、入稿が3週間も後ろ倒しになってしまいました。 印刷所の方ごめんなさい。</p> <h3 id="表紙作成の苦労話">表紙作成の苦労話</h3> <p>これまで表紙は知り合いのデザイナーさんにお願いしていたのですが、今回は諸事情あって頼めなかったので自力で作りました。</p> <p>本編の執筆が押していた今回は、なるべく楽して描き上げたいということで、Stable Diffusion に手を出しました。 これが泥沼の始まりでした。</p> <p>ローカルに環境を用意して、UIの使い方を覚えるだけでは終わらず、モデルやLoraの選定、プロンプトのチューニングなど、まあ言ってしまえば薄い本が1冊かけるくらいの勉強が必要でした。 思い通りのキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ター、いわゆるうちの子が出せるようになるまで研究を重ねるのにも苦労しましたが、そこで終われなかったのが大変な所でした。</p> <p>私はデザインのプロではないですが、各種本の表紙を見ると、タイトルやサークル名を入れるスペースが確保されています。 ところが、Stable Diffusion で何も考えなしに画像を出力すると、枠全体にキャ<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%E9%A5%AF">ラク</a>ターが描画されてしまいます。 プロンプトの工夫で対処もできるんでしょうが、そのための研究とガチャを引き続ける根気は残っていませんでした。</p> <p>そこで結局 <a href="https://github.com/lllyasviel/ControlNet">ControlNet</a> を使って、アウトラインだけ書いてディテールはAIに任せようという方針にしました。</p> <p>ControlNetは <a href="https://github.com/lllyasviel/style2paints">Style2Paints</a> を作っていた研究チームが開発した、既存の画像を使って新たに生成する画像のポーズなどを指定できる技術です。</p> <p>ControlNet を使えば、ダイレクトに所望の構図が生成できるので、闇雲にガチャを引き続けるストレスからは解放されました。</p> <p><figure class="figure-image figure-image-fotolife" title="下書きから線画へ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611163329.png" width="900" height="1200" loading="lazy" title="" class="hatena-fotolife" style="width:300px" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611163406.png" width="720" height="960" loading="lazy" title="" class="hatena-fotolife" style="width:300px" itemprop="image"></span><figcaption>下書きから線画へ</figcaption></figure> <figure class="figure-image figure-image-fotolife" title="線がから着色をし、img2imgで微調整"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611163420.png" width="720" height="960" loading="lazy" title="" class="hatena-fotolife" style="width:300px" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611163654.png" width="848" height="1200" loading="lazy" title="" class="hatena-fotolife" style="width:300px" itemprop="image"></span><figcaption>線画から着色をし、img2imgで微調整</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="タイトルとサークル名、帯を入れて完成"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611192651.png" width="848" height="1200" loading="lazy" title="" class="hatena-fotolife" style="width:300px" itemprop="image"></span><figcaption>タイトルとサークル名、帯を入れて完成</figcaption></figure></p> <p>でも草案とはいえ、これだけデザインとプロンプトの研究が必要なんであれば、正直最初から自分で描いた方が早かったかもですね。 まあ、私は塗りや細かい書き込みが苦手なので、これでも少しは楽できたのかもしれません。</p> <p>文章面では、ChatGPTの力も借りようとしました。 上手いこと使いこなしているサークルさんもいらっしゃいましたが、私は結局自分の文体や強調したい部分のこだわりが強くて、上手く活用できませんでした。</p> <p>とはいえ、タイトル決めの壁打ち役としては活躍してくれました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611195310.png" width="713" height="167" loading="lazy" title="" class="hatena-fotolife" style="width:400px" itemprop="image"></span> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611195345.png" width="717" height="212" loading="lazy" title="" class="hatena-fotolife" style="width:400px" itemprop="image"></span> <span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611195450.png" width="728" height="591" loading="lazy" title="" class="hatena-fotolife" style="width:400px" itemprop="image"></span></p> <h2 id="オフライン当日の話">オフライン当日の話</h2> <p>オフライン参加はこれで2度目になるのですが、mochikoAsTechさんやミライ・ハッキング・ラボさんの持ち物リストを参考に準備をしました。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fnote.com%2Fmochikoastech%2Fn%2Fnf484f114855c" title="#技術書典 開催前に準備しておくといいもの|mochikoAsTech" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://note.com/mochikoastech/n/nf484f114855c">note.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fakademeia.info%2F%3Fp%3D22250" title="技術書典サークル参加のための持ち物リスト【ミライ・ハッキング・ラボ編】 | Security Akademeia【セキュリティアカデメイア】" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://akademeia.info/?p=22250">akademeia.info</a></cite></p> <p>幟やデモ機などの備品はなく、こじんまりとしたものだけなのでサークル入場15分前に着けば余裕をもって設営できました。 それでも、本の種類も増えてだいぶ賑やかになってきました。</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8?src=hash&amp;ref_src=twsrc%5Etfw">#技術書典</a> 14の設営完了しました!<br>本日は【い08】でお待ちしてます! <a href="https://t.co/PpScsq4b7i">pic.twitter.com/PpScsq4b7i</a></p>&mdash; さしみもち (@Sashimimochi343) <a href="https://twitter.com/Sashimimochi343/status/1660095086674460673?ref_src=twsrc%5Etfw">2023年5月21日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>お恥ずかしながら、後々知ったのですが、まさかお隣がプロの作家さんだったとは!</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fnote.com%2Fkaerukoakeno%2Fn%2Fn0323590cc2cc" title="『急な「売れ」に備える作家のためのサバイバル読本』を技術書典で頒布します|朱野帰子" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://note.com/kaerukoakeno/n/n0323590cc2cc">note.com</a></cite></p> <p>開場してからは、整理券のおかげで定期的な波はあるものの、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>に比べるとスムーズに移動できるのでよかったです。</p> <p>事前のチェックイン数がわからないので、どのくらいの方がスペースに来てくれるかドキドキでしたが、 知り合いの方を始めとしていろいろな方が来てくれました。 印刷所さんや出版社さん、運営の方々などにもスペースに来ていただいたのですが、ことごとく私が不在のタイミングですみませんでした。</p> <p>手に取っていただいた方とは、本の分厚さのことはもちろん、 「Solrの本、こと日本語だとぜんぜんないですよね」とか「うちでもちょうど導入を考えていて」といったお話ができて楽しかったです。 やっぱりSolrって<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C3%CE%CC%BE%C5%D9">知名度</a>あるのに、名著を除くと書籍があまり出回っていないというのは共通認識なんですね。 そんな方々に届いて役立つ1冊になっているなら幸いです。</p> <p>中には、「これから<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C1%B4%CA%B8%B8%A1%BA%F7">全文検索</a>を始めたいと思っています」という方もいらっしゃいました。 本の主題は<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C1%B4%CA%B8%B8%A1%BA%F7">全文検索</a>の次のステップとしてベクトル検索だったんですが、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C1%B4%CA%B8%B8%A1%BA%F7">全文検索</a>そのものに一定層の需要があるというのは意外でした。 いっそのことSolrの入門本の方が需要あったんだろうか?</p> <p>ありがたいことに既刊の物理本は無事完売しました。 売り切れた後も物理本を求めてくださる方がいたのですが、泣く泣く電子版をご案内したのは申し訳なかったです。 でも、印刷費を考えるとまとまった量の需要がないと重版の予定はないんですよね... う~む、悩ましい。</p> <p>お店番については、やっぱり2人態勢がおすすめです。 最初は1人で店番をやろうかと思っていたのですが、ダメもとで友人に頼んだら快諾してくれたので助かりました。 Tくんありがとう。</p> <p>ワンフロアの端から端まで回ろうとすると、スペースを離れる時間が長くなってしまうのでワンオペにならなくてよかったです。 これでもコンパクトになった方で、コロナ前は2フロアだったことを思い返すと、当時全スペース回ろうと考えると恐ろしい限りですね。</p> <p>あと、たま~に、現金払いを求められるのですが、 そろそろ現金対応を検討したほうがいいんですかね?</p> <p>ほかの方のスペースにお邪魔したときに感じたのですが、名刺はあってもよかったかなと思います。</p> <h2 id="戦利品">戦利品</h2> <p>事前にチェックしていたものに加えて、スペースでお話ししていたら、ついつい買ってしまいました。 みなさん、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>お上手ですね。</p> <p>ちょっとずつ消化していかないと。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2Ff1FZtw171uGapBJTeXq747%3FproductVariantID%3DduJ2UxRWSw91DhSYybz4Le" title="ボイボの本 ボイスドラマ自動生成システムをつくろう:飢餓狂爺" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/f1FZtw171uGapBJTeXq747?productVariantID=duJ2UxRWSw91DhSYybz4Le">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F7UuLXhhgRya3kV3pdu19rL%3FproductVariantID%3Dd3UdxEdsyhq8B4Zy0TWUWQ" title="明後日から使えるAI・機械学習入門:モウフカブール" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/7UuLXhhgRya3kV3pdu19rL?productVariantID=d3UdxEdsyhq8B4Zy0TWUWQ">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F78eKTUcpvU1W8EXUg5SsLP%3FproductVariantID%3D9u5hWckzkjJX4yr0RmBEib" title="Databricksの詰め合わせ! - 今、気になっている機能を深掘りしてみました-:赤煉瓦倉庫" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/78eKTUcpvU1W8EXUg5SsLP?productVariantID=9u5hWckzkjJX4yr0RmBEib">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2FeLYAWaL5cuXnMfB4V1YPK9%3FproductVariantID%3DeYn8y88qiJ4MuQSh9bk3W3" title="グリー技術書典部誌 2023年春号:グリー技術書典部" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/eLYAWaL5cuXnMfB4V1YPK9?productVariantID=eYn8y88qiJ4MuQSh9bk3W3">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F8CFiatcRSM9D88iAupz5LD%3FproductVariantID%3D7EwE78D2TwYBXHTxwRJwzc" title="AIアートの新時代:CLIPとStable Diffusionを活用した画像生成技術とその応用:じゅ~しぃ~すくりぷと" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/8CFiatcRSM9D88iAupz5LD?productVariantID=7EwE78D2TwYBXHTxwRJwzc">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2FjQ3JQFRTyRNaL83z9wMESw%3FproductVariantID%3DgF75ra5ndBLXMAHVieHKFv" title="まんがではじめるGitOps:おとうふ工房" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/jQ3JQFRTyRNaL83z9wMESw?productVariantID=gF75ra5ndBLXMAHVieHKFv">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F14ySaLbAw6YW2KUtqG02L8%3FproductVariantID%3Dfpb5NnJhFChUMgUMMmXa8y" title="東大院出て美大の先生やってるド阿呆が、流行りの画像生成AIを使って絵を描く方法を考えてみた本:osakana.factory" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/14ySaLbAw6YW2KUtqG02L8?productVariantID=fpb5NnJhFChUMgUMMmXa8y">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F4979039198511104%3FproductVariantID%3D5079713231929344" title="まんがではじめるKubernetes2:おとうふ工房" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/4979039198511104?productVariantID=5079713231929344">techbookfest.org</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechbookfest.org%2Fproduct%2F2J1U6XTv926wZqyYTyhjR6%3FproductVariantID%3DcnY5jFrB1HhRmv0JcLM0iX" title="ゼロから始めるお絵描きの書+:萌え萌えCompany" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://techbookfest.org/product/2J1U6XTv926wZqyYTyhjR6?productVariantID=cnY5jFrB1HhRmv0JcLM0iX">techbookfest.org</a></cite></p> <h2 id="今後に向けて">今後に向けて</h2> <p>お祭り自体は大盛況のなか閉幕しましたが、引き続きオンラインショップは随時稼働中です。 電子版でよければ頒布しているので、覗いていってください。 質問、感想お待ちしております。</p> <p>また、メールあんま見ないなや日別の頒布数の分析したいなという思いから、 サークルの方に届く購入通知メールをSlackに転送+グラフ化するツールを作りました。</p> <p><figure class="figure-image figure-image-fotolife" title="頒布通知"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611190747.png" width="611" height="81" loading="lazy" title="" class="hatena-fotolife" style="width:400px" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20230611/20230611190549.jpg" width="600" height="292" loading="lazy" title="" class="hatena-fotolife" style="width:300px" itemprop="image"></span><figcaption>頒布通知</figcaption></figure></p> <p>会期中に公開までは間に合いませんでしたが、次回までには公開したいと思います。</p> <p>さて、次回の技術書典15は11/11~とのことですが、テーマどうしましょうか? ベクトル検索を深堀するか、意外と<a class="keyword" href="https://d.hatena.ne.jp/keyword/%C1%B4%CA%B8%B8%A1%BA%F7">全文検索</a>に興味があるという方もそこそこいらっしゃったので、Solrの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>本にするか、それともまた違ったネタにするか... う~ん、現状これといったテーマがないんですよね。 もし、この記事を読んでくださった奇特な方がいらっしゃれば、要望もらえると助かります。 書く保証はできませんが、候補には確実に入りますので。</p> <p>本ブログの次の更新がいつになるかわかりませんが、また次の技術書典には出たいと思っているので、その時にでもお会いできれば!</p> T-N-Clark それでもはてなブログを自分のホームページに埋め込みたい hatenablog://entry/13574176438034169924 2021-11-18T21:00:00+09:00 2021-11-18T21:00:03+09:00 2021/1/31に、CORS制限回避のために使っていた https://github.com/Rob--W/cors-anywhere が悪用が多すぎるために利用を制限したようです。 ※教えてくださった読者さんありがとうございます github.com この関係で、前回の方法ではRSSの取得ができなくなってしまいました。 t-n-clark.hatenadiary.jp 下記記事にも書かれている通り、本来、フロントエンドから直接外部サービスのAPIを呼ぶのは邪道であって、自前でAPIを立てるのが正攻法のようです。 medium.com career.levtech.jp そこで、はてなブログ… <p>2021/1/31に、CORS制限回避のために使っていた <a href="https://github.com/Rob--W/cors-anywhere">https://github.com/Rob--W/cors-anywhere</a> が悪用が多すぎるために利用を制限したようです。 ※教えてくださった読者さんありがとうございます</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2FRob--W%2Fcors-anywhere%2Fissues%2F301" title="PSA: Public demo server (cors-anywhere.herokuapp.com) will be very limited by January 2021, 31st · Issue #301 · Rob--W/cors-anywhere" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/Rob--W/cors-anywhere/issues/301">github.com</a></cite></p> <p>この関係で、前回の方法では<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>の取得ができなくなってしまいました。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ft-n-clark.hatenadiary.jp%2Fentry%2F2019%2F05%2F30%2F113000" title="やっぱりはてなブログを自分のホームページに埋め込みたい - もちっとメモ" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://t-n-clark.hatenadiary.jp/entry/2019/05/30/113000">t-n-clark.hatenadiary.jp</a></cite></p> <p>下記記事にも書かれている通り、本来、フロントエンドから直接外部サービスの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を呼ぶのは邪道であって、自前で<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を立てるのが正攻法のようです。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fmedium.com%2F%40baphemot%2Funderstanding-cors-18ad6b478e2b" title="Understanding CORS" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://medium.com/@baphemot/understanding-cors-18ad6b478e2b">medium.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcareer.levtech.jp%2Fguide%2Fknowhow%2Farticle%2F400%2F" title="【翻訳記事】CORSを理解する" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://career.levtech.jp/guide/knowhow/article/400/">career.levtech.jp</a></cite></p> <p>そこで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%ED%A5%B0">はてなブログ</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を叩くバックエンドの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を用意して、それをフロントエンドから呼びに行くようにすることにしました。</p> <p>※ちなみにフロントエンド(React)から直接<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を呼び出そうともしてみましたが、同じくCORSエラーに遭遇しました。 そりゃそうですよね。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>、Herokuなど好きな場所に立てればいいのですが、サーバの管理が不要でデプロイが簡単な<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Apps">Google Apps</a> Script(以下、GAS)で作ることにしました。</p> <p>※GASの使い方などについては、さまざまな記事で紹介されているので、ここでは割愛します。</p> <p>まずは、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を使うための準備をします。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>は使用用途別にいろいろな種類が用意されているので、用途に合ったものを選択します。 <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fdeveloper.hatena.ne.jp%2Fja%2Fdocuments%2Fauth%2Fapis%2Foauth%2Fapis" title="OAuth 対応 API 一覧 - Hatena Developer Center" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth/apis">developer.hatena.ne.jp</a></cite> 使用する際は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CD%F8%CD%D1%B5%AC%CC%F3">利用規約</a>に気を付けましょう。 <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fdeveloper.hatena.ne.jp%2Flicense" title="API規約・ライセンス・商標について - Hatena Developer Center" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://developer.hatena.ne.jp/license">developer.hatena.ne.jp</a></cite></p> <p>今回はお試しなので、一番認証が簡単な<a class="keyword" href="http://d.hatena.ne.jp/keyword/Basic%C7%A7%BE%DA">Basic認証</a>を選びました。 <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fdeveloper.hatena.ne.jp%2Fja%2Fdocuments%2Fblog%2Fapis%2Fatom" title="はてなブログAtomPub - Hatena Developer Center" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://developer.hatena.ne.jp/ja/documents/blog/apis/atom">developer.hatena.ne.jp</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Basic%C7%A7%BE%DA">Basic認証</a>はDeveloper登録不要で使えます。</p> <p>より強力なセキュリティ認証である<a class="keyword" href="http://d.hatena.ne.jp/keyword/OAuth%C7%A7%BE%DA">OAuth認証</a>を使う場合はDeveloper登録が必要です。 登録方法は下記を参考にしてください。 <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fdeveloper.hatena.ne.jp%2Fja%2Fdocuments%2Fauth%2Fapis%2Foauth%2Fconsumer" title="Consumer key を取得して OAuth 開発をはじめよう - Hatena Developer Center" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth/consumer">developer.hatena.ne.jp</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Basic%C7%A7%BE%DA">Basic認証</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>キーはブログの管理画面から調べられます。 合わせてルートエンドポイントも確認しておきます。 <a href="http://blog.hatena.ne.jp/my/config/detail">http://blog.hatena.ne.jp/my/config/detail</a></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>の使い方がわかったので、続いて、GASで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を叩いて必要情報を返却する自前<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>の実装をしていきます。</p> <p>GASから取得するサンプルはこの記事が参考になります。 といってもただ単純に<code>fetch</code>しているだけです。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.kotanin0.work%2Fentry%2F2019%2F12%2F26%2F230000" title="GASではてなブログAPIから情報取得!ファイル保存までやっちゃうよ。 - Binary Diary" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.kotanin0.work/entry/2019/12/26/230000">www.kotanin0.work</a></cite></p> <p>取得結果の細かい仕様は下記の記事が参考になります。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fkorosuke613.hatenablog.com%2Fentry%2F2019%2F06%2F27%2F223528" title="はてなブログのAPIを叩いて最新記事一覧を取得する[Javascript] - cangoxina" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://korosuke613.hatenablog.com/entry/2019/06/27/223528?utm_source=feed">korosuke613.hatenablog.com</a></cite></p> <p>例えば、こんな感じで取ってこれます。 <code>your_root_endpoint</code>と<code>your_api_key</code>のところには先ほど管理画面で確認したルートエンドポイントと<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>キーが入ります。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synIdentifier">function</span> fetchHatenaFeeds() <span class="synIdentifier">{</span> <span class="synStatement">const</span> url = <span class="synConstant">&quot;your_root_endpoint/entry&quot;</span>; <span class="synStatement">const</span> username = <span class="synConstant">&quot;your_username&quot;</span> <span class="synStatement">const</span> apiKey = <span class="synConstant">&quot;your_api_key&quot;</span> <span class="synStatement">const</span> options = <span class="synIdentifier">{</span> <span class="synConstant">&quot;method&quot;</span>: <span class="synConstant">&quot;GET&quot;</span>, <span class="synConstant">'headers'</span> : <span class="synIdentifier">{</span><span class="synConstant">'Authorization'</span> : <span class="synConstant">'Basic '</span> + Utilities.base64Encode(username + <span class="synConstant">':'</span> + apiKey)<span class="synIdentifier">}</span> <span class="synIdentifier">}</span> <span class="synIdentifier">var</span> xml = UrlFetchApp.fetch(url,options).getContentText(); <span class="synStatement">return</span> xml; <span class="synIdentifier">}</span> </pre> <p>変数名からわかるように<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>から取得できる情報は<a class="keyword" href="http://d.hatena.ne.jp/keyword/XML">XML</a>形式になっています。 このままだと扱いにくいので、必要な情報だけ抜き出しで<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>形式に変換してしまいましょう。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synIdentifier">function</span> parseXmlContent(xml)<span class="synIdentifier">{</span> <span class="synIdentifier">var</span> contentList = <span class="synIdentifier">[]</span>; <span class="synIdentifier">var</span> entries = xmlToJson(xml).feed.entry; entries.map((entry)=&gt;<span class="synIdentifier">{</span> <span class="synStatement">if</span>(entry.control.draft.Text === <span class="synConstant">&quot;no&quot;</span>)<span class="synIdentifier">{</span> contentList.push(<span class="synIdentifier">{</span> <span class="synConstant">&quot;title&quot;</span>: entry.title.Text, <span class="synConstant">&quot;pubDate&quot;</span>: entry.published.Text, <span class="synConstant">&quot;link&quot;</span>: entry.link<span class="synIdentifier">[</span>1<span class="synIdentifier">]</span>.href, <span class="synIdentifier">}</span>) <span class="synIdentifier">}</span> <span class="synIdentifier">}</span>) <span class="synStatement">return</span> contentList <span class="synIdentifier">}</span> </pre> <p>こんな感じで<code>xmlToJson()</code>でエントリーを変換して、所望のカラムを取り出して<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CF%A2%C1%DB%C7%DB%CE%F3">連想配列</a>に格納します。 この例では、</p> <ul> <li><code>title</code></li> <li><code>pubDate</code></li> <li><code>link</code></li> </ul> <p>を最終的に返す仕様になっています。</p> <p><code>xmlToJson()</code>の中身は以下の通りです。 こちらのサンプルコードをそのまま使わせてもらいます。 <script src="https://gist.github.com/erickoledadevrel/6b1e9e2796e3c21f669f.js"> </script><cite class="hatena-citation"><a href="https://gist.github.com/erickoledadevrel/6b1e9e2796e3c21f669f">gist.github.com</a></cite></p> <p>データ取得~返却用の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CF%A2%C1%DB%C7%DB%CE%F3">連想配列</a>に変換までの流れをまとめて1つの関数にしてしまいましょう。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synIdentifier">function</span> getHatenaFeeds()<span class="synIdentifier">{</span> <span class="synIdentifier">var</span> xml = fetchHatenaFeeds(); <span class="synIdentifier">var</span> contentList = parseXmlContent(xml); <span class="synStatement">return</span> contentList; <span class="synIdentifier">}</span> </pre> <p>最後にGETリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トの受け付け関数を用意します。 GASの場合は<code>doGet</code>という名前の関数がエンドポイントになります。</p> <p>GASではあらかじめ登録された名前の関数を作ることで、 イベントをトリガーに<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を実行できる シン<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%EB%A5%C8">プルト</a>リガーという仕組みがあります。</p> <p>今回の場合は、フロントエンドからのGETリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トがイベントになります。</p> <p>上記で作った<code>getHatenaFeeds()</code>で取得した情報を<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>形式に変換して返しています。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synIdentifier">function</span> doGet(e)<span class="synIdentifier">{</span> <span class="synIdentifier">var</span> data = getHatenaFeeds(); <span class="synIdentifier">var</span> payload = JSON.stringify(data); <span class="synIdentifier">var</span> output = ContentService.createTextOutput(); output.setMimeType(ContentService.MimeType.JSON); output.setContent(payload); <span class="synStatement">return</span> output; <span class="synIdentifier">}</span> </pre> <p>これら一連の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>をデプロイして公開すれば、自分の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報が取得できる<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>が完成です。</p> <p>あとは、前回の実装でRSSParserを使っていたところをaxiosに取り替えてやれば、OKです。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Faxios" title="axios" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.npmjs.com/package/axios">www.npmjs.com</a></cite></p> <p>前回</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink>urls.map((url) =&gt; <span class="synIdentifier">{</span> rssParser.parseURL(url) .then((feed) =&gt; <span class="synIdentifier">{</span> <span class="synComment">// 新たに取得したfeedを取り出す</span> </pre> <p>今回</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink>urls.map((url) =&gt; <span class="synIdentifier">{</span> axios.get(url) .then((feed) =&gt; <span class="synIdentifier">{</span> <span class="synComment">// 新たに取得したfeedを取り出す</span> </pre> <p>これで同じ状態に復旧できました。やった~!</p> <p>※ホームページお引越ししました。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fsashimimochi.netlify.app%2F" title="柏の葉非公式パソコン部" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://sashimimochi.netlify.app/">sashimimochi.netlify.app</a></cite></p> T-N-Clark ポッドキャスト始めてみたくてReact触ってみた hatenablog://entry/26006613630344309 2020-09-21T02:09:56+09:00 2020-09-21T02:09:56+09:00 経緯とか 最近、Tech系のポットキャストを趣味で聴き始めたのですが、そんな話を仲間内でしたら「自分たちでもやってみない?」ということになったのでReact使って公開用のページを作ってみました。とはいえ、React初心者が作ったものなので、クオリティはお察しです。 今回の目標は、 - Youtubeの動画を埋め込む - 毎回の放送をカードで表示する - カードの内容をGoogle Sprede Sheetから取ってくる - Twitterのタイムラインを埋め込む ができれば最低限の体裁はできるかなと言うことで、取っ付きやすそなのから順番に試して意図通りに動いたら即採用。なので、もっといい方法は… <h2>経緯とか</h2> <p>最近、Tech系のポットキャストを趣味で聴き始めたのですが、そんな話を仲間内でしたら「自分たちでもやってみない?」ということになったのでReact使って公開用のページを作ってみました。とはいえ、React初心者が作ったものなので、クオリティはお察しです。 今回の目標は、 - <a class="keyword" href="http://d.hatena.ne.jp/keyword/Youtube">Youtube</a>の動画を埋め込む - 毎回の放送をカードで表示する - カードの内容を<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Sprede Sheetから取ってくる - <a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>のタイムラインを埋め込む</p> <p>ができれば最低限の体裁はできるかなと言うことで、取っ付きやすそなのから順番に試して意図通りに動いたら即採用。なので、もっといい方法はきっとあると思いますが、そこは今後追求します。</p> <h2>環境構築etc.</h2> <p>React初心者なので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ構成とかBabelとか言われてもちんぷんかんぷんなので、その辺りをよしなにしてくれる<a href="https://github.com/facebook/create-react-app">create-react-app</a>を使うことにします。なんでも<a class="keyword" href="http://d.hatena.ne.jp/keyword/facebook">facebook</a>が提供しているお手軽プロジェクトビルダーだとか。インストールにはこちらを参考にしました。 <a href="https://qiita.com/willow-c/items/ad5c1a15d8b32d75d927">Windows+VSCode上でReact開発環境を構築</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>の場合は私はこんな感じでインストールしました。 <a href="https://sashimimochi.qrunch.io/logs/HXGsLVYwmd7vmJLG">MacでReactの環境を作ろうと思ってコケた</a> あとはこのあたりを参考にざっと斜め読みして、書き方のお作法を勉強しました。 <a href="https://nulab-inc.com/ja/blog/typetalk/how-to-make-website-with-react-static/">jQueryを卒業したかった僕がReact StaticでReactをイチから学んでWebサイトを作った話</a></p> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/Youtube">Youtube</a>の動画を埋め込む</h2> <p><a href="https://www.npmjs.com/package/react-player">React Player</a> 日本語の参考ページがみたい方はこちら <a href="https://qiita.com/reiji1020/items/a65d84980b67f13449e1">Reactアプリケーションで動画や音楽ファイルを扱うために「react-player」を使用する</a> ページにしたがって、</p> <pre class="code" data-lang="" data-unlink>$ npm install react-player --save</pre> <p>でインストールする。今回は音声ファイルは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Youtube">Youtube</a>にあげるつもりなので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Youtube">Youtube</a>の動画を再生できればOKにします。React Playerはいろんな形式の動画を再生できそうなんだけど、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Youtube">Youtube</a>は専用のPlayerが用意されているようなので、そいつを使うことに。これも公式ページの通りに組んでみると、ちゃんと再生できたし、シークバーも表示されました。純粋なReact Playerだとシークバーは出ませんでした。</p> <h2>毎回の放送をカードで表示する</h2> <p>そろそろBootstrapを卒業しようと思うので、Reactと相性がいいらしい、Material UIを使ってみることにします。 <a href="http://rennnosukesann.hatenablog.com/entry/2018/05/21/235627">【React】Material-UIでReactアプリケーションをマテリアルデザイン化する</a> もっと凝ったUIを作りたい場合は公式ページにソース付きでデモがあるのでお試しあれ。 <a href="https://material-ui.com/demos/cards/">Material-UI Cards</a> 他の候補としてはこの辺りがおすすめぽいです。 <a href="https://uxmilk.jp/76081">無料のマテリアルデザインフレームワーク10選</a></p> <h2>カードの内容を<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Sprede Sheetから取ってくる</h2> <p>これが正直一番難しかったです。最終的に取った手段が 1. <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Spread Sheetを<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Apps">Google Apps</a> Script(GAS)で取得&amp;<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>形式で返す 1. Reactで<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>を読み込んで所望の形式に変換して表示</p> <p>でした。 まず、こんな感じのSpread Sheet を用意します。</p> <table> <thead> <tr> <th style="text-align:left;">id</th> <th style="text-align:left;">title</th> <th style="text-align:left;">url</th> <th style="text-align:left;">description</th> </tr> </thead> <tbody> <tr> <td style="text-align:left;">No.1</td> <td style="text-align:left;">Title1</td> <td style="text-align:left;"><a href="https://www.youtube.com/watch?v=***">https://www.youtube.com/watch?v=***</a> </td> <td style="text-align:left;">その回の説明を書く</td> </tr> <tr> <td style="text-align:left;">No.2</td> <td style="text-align:left;">Title2</td> <td style="text-align:left;"><a href="https://www.youtube.com/watch?v=***">https://www.youtube.com/watch?v=***</a> </td> <td style="text-align:left;">説明2</td> </tr> <tr> <td style="text-align:left;">No.3</td> <td style="text-align:left;">Title3</td> <td style="text-align:left;"><a href="https://www.youtube.com/watch?v=***">https://www.youtube.com/watch?v=***</a> </td> <td style="text-align:left;">説明3</td> </tr> </tbody> </table> <p>これをGASで<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>形式でGETリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トに対して返してあげるようにします。こちらを参考に実装しました。 <a href="https://liginc.co.jp/179788">Google App Scriptを用いてGoogleスプレッドシートからJSONを生成してみよう</a> 実践編の通りにGASを書いて1行目をkeyにしてこんな感じの<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>を取得できるようにしました。</p> <pre class="code" data-lang="" data-unlink>[{&#34;id&#34;:&#34;No.1&#34;,&#34;title&#34;:&#34;Title1&#34;,&#34;url&#34;:&#34;https://www.youtube.com/watch?v=***&#34;,&#34;description&#34;:&#34;その回の説明を書く&#34;},{&#34;id&#34;:&#34;No.2&#34;,&#34;title&#34;:&#34;Title2&#34;,&#34;url&#34;:&#34;https://www.youtube.com/watch?v=***&#34;,&#34;description&#34;:&#34;説明2&#34;},{&#34;id&#34;:&#34;No.3&#34;,&#34;title&#34;:&#34;Title3&#34;,&#34;url&#34;:&#34;https://www.youtube.com/watch?v=***&#34;,&#34;description&#34;:&#34;説明3&#34;}]</pre> <p>セキュリティ的にはガバガバですが、まあ、これで半分クリアです。残りは、React側での受け取り処理を作ります。今回は<a href="https://github.com/axios/axios">axios</a>を使います。Reactから手軽にHTTPリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トが送れるライブラリのようです。 例によって参考ページはこちらです。 <a href="https://qiita.com/dyoshikawa/items/c8b09cde728388c8feec">React+axiosでLaravel APIからJSON取得</a> axiosで取得して、先ほどのCardの中に流し込みます。</p> <pre class="code" data-lang="" data-unlink>{// 一部抜粋 } import axios from &#39;axios&#39;; class App extends Component { constructor(props) { super(props); this.state = { contents: [] }; this.getData = this.getData.bind(this); } getData() { axios .get(&#39;GASの公開URL&#39;) .then(results =&gt; { const data = results.data; console.log(data); this.setState({ contents: [...data] }); }); } render() { const classes = this.props.classes; console.log(classes); const contents = this.state.contents.map(content =&gt; { return &lt;Card className={classes.card}&gt; &lt;CardContent&gt; &lt;h2 key={content.id}&gt;{content.title}&lt;/h2&gt; &lt;div align=&#34;center&#34; key={content.id}&gt; &lt;YoutubePlayer url={content.url} controls /&gt; &lt;/div&gt; &lt;p key={content.id}&gt;{content.description}&lt;/p&gt; &lt;/CardContent&gt; &lt;/Card&gt; }); return ( &lt;Fragment&gt; &lt;div className=&#34;App&#34;&gt; &lt;button onClick={this.getData}&gt;getData&lt;/button&gt; {contents} &lt;/div&gt; &lt;/Fragment&gt; ); } } export default withStyles(styles)(App); </pre> <p>基本的にはこれでできたのですが、このままだとユーザーが毎回ボタンを押さないとGASから読み込みが行われないので、ページロード時点で読み込めるようにします。やりたいこととしては<code>onLoad</code>のようなことをしたいのですが、Reactの場合は、<code>componentDidMount</code>をよく初期化に使うと聞いたので、これを使います。本当は使い方気をつけないと無限ループとは発生して危ないらしいのですが、冒頭の通り動いているからまあいっかの精神で書きます。 <a href="https://www.daily-dev.net/entry/2018/05/02/React%E3%81%AE%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%A8%E3%81%9D%E3%81%AE%E4%BD%BF%E3%81%84%E6%89%80%E3%83%BB%E4%BD%BF%E3%81%84">Reactのライフサイクルメソッドとその使いドコロのまとめ - ajax callをするのに最も適した場所は?</a> 先ほどのコードで<code>getData</code>の部分を全て<code>componentDidMount</code>に置換します。<code>&lt;button&gt;</code>の部分はいらないので削除します。これでリロードした度にGASから取得して表示してくれるようになりました。コンテンツ量が増えると崩壊しそうなので、そこはページを繰る度にロードするようにするなど要改善です。</p> <p>その他参考になりそうな記事はこちら。</p> <h3>参考文献</h3> <ul> <li><a href="https://qiita.com/numanomanu/items/7bb6542961a2c567f05c">3分で API を作って世の中にデプロイするライブコーディング今日から君もスピードスターエンジニア</a></li> <li><a href="https://qiita.com/ksyunnnn/items/e45733b18a9f40f19d78">Googleスプレッドシートのデータを表示させてリッチな静的サイトを作る</a></li> <li><a href="https://qiita.com/ksh-fthr/items/2daaaf3a15c4c11956e9">[axios] axios の導入と簡単な使い方</a></li> <li><a href="https://qiita.com/gcyagyu/items/4d186df2e90c53228951">ReactでAxiosを使ってGoogleAPIを叩いてみた</a></li> <li><a href="https://qiita.com/gaku3601/items/062a52748acc5e368453">react-reduxでページ読み込み時、actionを呼び出す方法</a></li> </ul> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>のタイムラインを埋め込む</h2> <p><a href="https://www.npmjs.com/package/react-twitter-widgets">React Twitter Wigets</a> ページの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>にしたがって、</p> <pre class="code" data-lang="" data-unlink>$ npm i react-twitter-widgets</pre> <p>で落としてきて埋め込みます。なんとお手軽で、<code>screenName</code>と<code>username</code>に埋め込みたいアカウント名(@で始まるやつ)を書くだけ。たぶん<code>sourceType</code>を変えれば表示内容も変えられると思われる。今回は自分のアカウントのツイートを表示したいだけなので、デフォルトの<code>profile</code>にしてます。</p> <h3>今回実装した全体のコード</h3> <p>以上、これで当初の目的は達成できました。 全体のコードを晒しておきます。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8">コンポーネント</a>化は度外視なので全部<code>App.js</code>に書いてます。 肝心の放送内容はこれから収録します...近いうちに投稿したいなあ...もし、無事投稿できたらそちらもよろしくお願いします。</p> <pre class="code" data-lang="" data-unlink>import React, { Component, Fragment } from &#39;react&#39;; import axios from &#39;axios&#39;; import ReactPlayer from &#39;react-player&#39;; import YoutubePlayer from &#39;react-player/lib/players/YouTube&#39;; import { withStyles } from &#39;@material-ui/core/styles&#39;; import Button from &#39;@material-ui/core/Button&#39;; import AppBar from &#39;@material-ui/core/AppBar&#39;; import Toolbar from &#39;@material-ui/core/Toolbar&#39;; import Typography from &#39;@material-ui/core/Typography&#39;; import Card from &#39;@material-ui/core/Card&#39;; import CardAction from &#39;@material-ui/core/CardActions&#39;; import CardContent from &#39;@material-ui/core/CardContent&#39;; import { Timeline } from &#39;react-twitter-widgets&#39;; import logo from &#39;./logo.svg&#39;; import &#39;./App.css&#39;; import { getConfig } from &#39;react-player/lib/utils&#39;; const styles = { card: { margin: 20, height: 580 }, }; class App extends Component { constructor(props) { super(props); this.state = { contents: [] }; this.componentDidMount = this.componentDidMount.bind(this) } componentDidMount() { axios .get(&#39;GASの公開URL&#39;) .then(results =&gt; { const data = results.data; console.log(data); this.setState({ contents: [...data] }); }); } render() { const classes = this.props.classes; console.log(classes); const contents = this.state.contents.map(content =&gt; { return &lt;Card className={classes.card}&gt; &lt;CardContent&gt; &lt;h2 key={content.id}&gt;{content.title}&lt;/h2&gt; &lt;div align=&#34;center&#34; key={content.id}&gt; &lt;YoutubePlayer url={content.url} controls /&gt; &lt;/div&gt; &lt;p key={content.id}&gt;{content.description}&lt;/p&gt; &lt;/CardContent&gt; &lt;/Card&gt; }); return ( &lt;Fragment&gt; &lt;div className=&#34;App&#34;&gt; &lt;AppBar position=&#34;static&#34; color=&#34;default&#34;&gt; &lt;Toolbar&gt; &lt;Typography variant=&#34;title&#34; color=&#34;inherit&#34;&gt; Page Title &lt;/Typography&gt; &lt;/Toolbar&gt; &lt;/AppBar&gt; &lt;header className=&#34;App-header&#34;&gt; &lt;h1&gt; Welcome to Our Site &lt;/h1&gt; &lt;p&gt; Abstract about this page. &lt;/p&gt; &lt;/header&gt; {contents} &lt;Timeline dataSource={{ sourceType: &#39;profile&#39;, screenName: &#39;Sashimimochi_343&#39; }} options={{ username: &#39;Sashimimochi_343&#39;, width: &#39;50%&#39;, height: &#39;400&#39; }} onLoad={() =&gt; console.log(&#39;Timeline is loaded!&#39;)} /&gt; &lt;/div&gt; &lt;/Fragment&gt; ); } } export default withStyles(styles)(App);</pre> <h2>その他の参考文献</h2> <ul> <li><a href="https://qiita.com/chibicode/items/8533dd72f1ebaeb4b614">Facebook公式のcreate-react-appコマンドを使ってReact.jsアプリを爆速で作成する</a></li> <li><a href="https://blog.bitsrc.io/how-to-use-sass-and-css-modules-with-create-react-app-83fa8b805e5e">How to use Sass and CSS Modules with create-react-app</a></li> <li><a href="https://whimsical.co/?ref=hyperpixel.io">The Visual Workspace</a> デザインの参考に</li> <li><a href="https://blog.ymyzk.com/2016/05/portfolio-renewal/">ポートフォリオをリニューアルしました</a></li> <li><a href="https://qiita.com/naoiwata/items/c590667765143c41d87a">今更聞けない JSX のコメントアウトの構文</a></li> </ul> T-N-Clark WindowsでもAlfredが使いたい hatenablog://entry/26006613630185056 2020-09-20T19:37:29+09:00 2022-02-09T11:43:12+09:00 何がしたいのか? ご時世も相まって、家で作業することが増えてきました。 普段仕事では、Macを使っているのですが、プライベートで使っているデスクトップはWindowsなので、Windowsで作業する機会も増えました。 Macでの作業に慣れてくると、その便利さゆえ、Windows環境でもSpotlightやAlfredのような、ランチャーがほしくなってきました。 果たしてそんな都合の良いアプリがあるのかと探したところ、ドンピシャなものがありました。 Keypirinha keyprinha さすがに完全互換とまでは行きませんが、Chromeのブックマーク検索など個人的に使いたい機能は概ね使えたの… <h2>何がしたいのか?</h2> <p>ご時世も相まって、家で作業することが増えてきました。 普段仕事では、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>を使っているのですが、プライベートで使っているデスクトップは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>なので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>で作業する機会も増えました。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>での作業に慣れてくると、その便利さゆえ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>環境でもSpotlightや<a href="https://www.alfredapp.com/">Alfred</a>のような、ランチャーがほしくなってきました。</p> <p>果たしてそんな都合の良いアプリがあるのかと探したところ、ドンピシャなものがありました。</p> <p><a href="http://keypirinha.com/">Keypirinha</a> <figure class="figure-image figure-image-fotolife" title="keyprinha"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20200920/20200920194410.gif" alt="f:id:T-N-Clark:20200920194410g:plain" width="800" height="420" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>keyprinha</figcaption></figure></p> <p>さすがに完全互換とまでは行きませんが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Chrome">Chrome</a>のブックマーク検索など個人的に使いたい機能は概ね使えたので満足しています。</p> <p>インストールVerとポータブルVerがありますが、公式はポータブルVerを推奨しています。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>製らしく、外部パッケージもあり、自作もできるようです。</p> <hr /> <h3>2022/2/9追記</h3> <p>お恥ずかしながら最近知ったのですが、PowerToysというツールが<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%A4%A5%AF%A5%ED%A5%BD%A5%D5%A5%C8">マイクロソフト</a>からリリースされているようです。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fmicrosoft%2FPowerToys" title="GitHub - microsoft/PowerToys: Windows system utilities to maximize productivity" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/microsoft/PowerToys">github.com</a></cite></p> <p>PowerToysの中にSpotlight検索機能が内包されているみたいです。 使ったことがないので使い勝手の比較はできませんが、Spotlight以外にもたくさんの機能をもっているようなので、こちらを使ってもいいかもしれません。</p> <p>各種機能についてはこちらの記事でわかりやすく日本語で解説されています。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fichii731%2Fitems%2F7db600158a858b12fb9c" title="DevToysが便利だと思ったら一緒に「PowerToys」も使ってみよう! - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/ichii731/items/7db600158a858b12fb9c">qiita.com</a></cite></p> <hr /> <h2>セットアップ</h2> <p>インストールは不要で、OSのバージョンに合わせて適当なファイルをダウンロードしてくるだけで使えます。ダウンロードした圧縮ファイル内の<code>keyprinha.exe</code>を実行するだけで起動します。 <a href="http://keypirinha.com/download.html">http://keypirinha.com/download.html</a> ※圧縮形式が<code>.7z</code>なので、必要に応じて<a href="https://sevenzip.osdn.jp/">7-Zip</a>などの解凍ソフトを使って解凍してください。</p> <p>PC再起動のたびに毎回Keyprinhaを起動するのも面倒なので、タスクマネージャーなどでスタートアップに追加しておくと便利です。</p> <p>Keypirinhaの設定は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/GUI">GUI</a>設定画面もなければ、基本的な情報は英語のみなので、ちょっと敷居が高いようにも感じます。 とは言え、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DE">プログラマ</a>であればそこまで身構える必要はないと思います。</p> <ol> <li>まず、Keypirinhaのランチャーを起動して、<code>Configure</code>で設定ファイルを開きます。 デフォルトだと<code>Ctrl+Win+K</code>がランチャー起動のショートカットキーとして設定されています。</li> <li>すると、ウィンドウが2つ起動します。 左が読み取り専用のオリジナルで、右がユーザー設定用です。</li> <li>これを編集して、設定を変更します。</li> <li>編集が終ったら、ウィンドウを閉じてKeypirinhaランチャーで<code>Reload Configuration</code>と入力すれば変更が反映されます。</li> </ol> <p>最低限、ショートカットキーの変更と<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>検索あたりは設定しておくと便利です。 詳しい設定方法は以下が参考になります。</p> <ul> <li><a href="https://www.shibuya24.info/entry/keypirinha_hotkey">Windows KeypirinhaでAlfredのランチャー感を再現する - 渋谷ほととぎす通信</a></li> <li><a href="https://loumo.jp/wp/archive/20170211120000/">Windows で利用できる Alfred ライクで高速なランチャー Keypirinha | Lonely Mobiler</a></li> </ul> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/Microsoft">Microsoft</a> Store でインストールしたアプリを起動する</h2> <p>デフォルトの検索範囲にはないので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Microsoft">Microsoft</a> Store でインストールしたアプリはKeypirinhaのランチャーで検索してもヒットしません。</p> <p>そこで、以下の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>を追加します。 <a href="https://github.com/ueffel/Keypirinha-WindowsApps">https://github.com/ueffel/Keypirinha-WindowsApps</a></p> <p>そのためには、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>の追加ができるようにPackageControlを使えるようにする必要があります。 <a href="https://github.com/ueffel/Keypirinha-PackageControl">https://github.com/ueffel/Keypirinha-PackageControl</a></p> <h3>手順</h3> <p><a href="https://github.com/ueffel/Keypirinha-PackageControl">README</a>の案内にしたがって</p> <ol> <li>Keypirinhaランチャーを起動して<code>Keypirinha: Console</code>または (Shortcut: F2)と入力する</li> <li>起動したコンソール画面下部の入力欄に以下のコマンドを入力して<code>Enter</code>で決定する (SublimetextのPackageControlと同じような感じ)</li> </ol> <pre class="code Console" data-lang="Console" data-unlink>import keypirinha as kp,keypirinha_net as kpn,os;p=&#34;PackageControl.keypirinha-package&#34;;d=kpn.build_urllib_opener().open(&#34;https://github.com/ueffel/Keypirinha-PackageControl/releases/download/1.0.2/&#34;+p);pb=d.read();d.close();f=open(os.path.join(kp.installed_package_dir(),p),&#34;wb&#34;);f.write(pb);f.close()</pre> <ol> <li>再度Keypirinhaランチャーを起動して、<code>Install Package</code>と入力すると<code>PackageControl: Install Package</code>が選択できるようになっているので選択する</li> <li><code>Keypirinha-WindowsApps</code>を選択してインストールする</li> <li>再度Keypirinhaランチャーを起動して、所望のアプリを入力すると検索結果にアプリが表示されるようになる</li> </ol> <h2>その他参考文献</h2> <ul> <li><a href="http://www.ellinikonblue.com/blosxom/Computer/SW/20180201Keypirinha.html">Keypirinha をさらに便利にする - Ellinikonblue.com Weblog</a></li> <li><a href="https://qiita.com/hirano00o/items/d503194749420ab13236#keypirinha">Windows機を購入したので開発環境作り(WSL2) - Qiita</a></li> </ul> T-N-Clark Macでもエアロスナップが使いたい hatenablog://entry/26006613630184063 2020-09-20T19:34:37+09:00 2020-09-20T19:34:37+09:00 はじめに エアロスナップとは、Windowsユーザーならご存知であろう、Windows7から搭載された機能です。 画面サイズに合わせてブラウザやアプリなどのウィンドウサイズを画面半分などに自動で調節してくれる機能です。 aerosnap 常にデュアルディスプレイにできるのであれば不要な機能かもしれませんが、画面の制約や真横に並べて見比べたいときなどは重宝します。 ブラウザで調べものしながら、エディターで編集するときなどはとても便利です。 個人的には便利だと思っているですが、好き嫌いは分かれるようで、あえて無効にしている人も少なくないようです。 プライベートはWindowsで、仕事ではMacを使… <h2>はじめに</h2> <p>エアロスナップとは、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>ユーザーならご存知であろう、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows7">Windows7</a>から搭載された機能です。 画面サイズに合わせてブラウザやアプリなどのウィンドウサイズを画面半分などに自動で調節してくれる機能です。</p> <p><figure class="figure-image figure-image-fotolife" title="aerosnap"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20200920/20200920193308.gif" alt="f:id:T-N-Clark:20200920193308g:plain" title="f:id:T-N-Clark:20200920193308g:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>aerosnap</figcaption></figure></p> <p>常に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%E5%A5%A2%A5%EB%A5%C7%A5%A3%A5%B9%A5%D7%A5%EC%A5%A4">デュアルディスプレイ</a>にできるのであれば不要な機能かもしれませんが、画面の制約や真横に並べて見比べたいときなどは重宝します。 ブラウザで調べものしながら、エディターで編集するときなどはとても便利です。 個人的には便利だと思っているですが、好き嫌いは分かれるようで、あえて無効にしている人も少なくないようです。</p> <p>プライベートは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>で、仕事では<a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>を使っている私は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>での作業中もこのエアロスナップが使いたくなる場面がしばしばありました。 一応、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>のデフォルト機能であるSplit Viewを使えば、それっぽいことはできるのですが、キーボード操作だけで調節できない、ウィンドウがフルスクリーンモードになってしまうなど、少々使い勝手に満足いかない部分がありました。 <a href="https://support.apple.com/ja-jp/HT204948">https://support.apple.com/ja-jp/HT204948</a></p> <p>そこで、2つのアプリを試してみました。</p> <h2><a href="https://apps.apple.com/jp/app/gridsutra-lite/id1195876099?mt=12">Gridsutra Lite</a></h2> <ul> <li>Lite版は無料、フルサイズ版は有料($4.99)</li> <li>Lite版はブラウザ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C6%A5%AD%A5%B9%A5%C8%A5%A8%A5%C7%A5%A3%A5%BF">テキストエディタ</a>、プレビューアプリのみ対応</li> <li>ショートカットキー対応</li> </ul> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Apple%20Store">Apple Store</a>からお手軽にインストールというところで試してみました。 決して悪くはなかったのですが、Lite版だと物足りなかったので、フルサイズ版にするか悩みましたが、次に紹介するアプリで事足りてしまったので、結局不採用。</p> <h2><a href="https://www.spectacleapp.com/">Spectacle</a></h2> <p><figure class="figure-image figure-image-fotolife" title="spectacle"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20200920/20200920193316.gif" alt="f:id:T-N-Clark:20200920193316g:plain" title="f:id:T-N-Clark:20200920193316g:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>spectacle</figcaption></figure></p> <ul> <li>基本無料</li> <li>任意のウィンドウに対してリサイズが可能</li> <li>ショートカットキーに対応</li> </ul> <p>機能はシンプルですが、ほぼエアロスナップと同等のことができます。</p> <h2>おわりに</h2> <p>Spectacleのおかげで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>のときと同じ使用感で<a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>でも作業できるようになりました。 みなさんも、私と同じようなもやもやを抱えているようであれば、一度お試しあれ。</p> <p>ちなみに、試してはいませんが、ほかにも以下のような類似アプリもあるようです。</p> <ul> <li><a href="https://apps.apple.com/jp/app/bettersnaptool/id417375580?mt=12">「BetterSnapTool」をMac App Storeで</a></li> </ul> T-N-Clark 始めて技術同人誌を執筆しました hatenablog://entry/26006613493576340 2020-01-06T11:30:00+09:00 2020-04-16T10:29:55+09:00 技術同人誌を執筆しました 先日の冬コミ(C97)にて、人生で初めて技術同人誌というものを書いて頒布しました。 https://webcatalog-free.circle.ms/Circle/14808053 頒布した本は 『新しい挑戦をしたい時に見る本 Vol.2』 といい、BOOTHで基本無料でお求めいただけます。 booth.pm 基本無料というのは以下のような理念で頒布しているからです。 『新しい挑戦をしたいときに見る本』シリーズとは、まんてらスタジオに集まっているクリエイター達が、好き勝手に自分の書きたい物を提示して、好き勝手に技術書を書き、それを我々で一つの本にして出版しているプロ… <h2>技術同人誌を執筆しました</h2> <p>先日の<a href="https://www.comiket.co.jp/info-a/C97/C97info.html">冬コミ</a>(C97)にて、人生で初めて技術同人誌というものを書いて頒布しました。 <a href="https://webcatalog-free.circle.ms/Circle/14808053">https://webcatalog-free.circle.ms/Circle/14808053</a></p> <p>頒布した本は 『新しい挑戦をしたい時に見る本 Vol.2』 といい、BOOTHで<strong>基本無料</strong>でお求めいただけます。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fbooth.pm%2Fja%2Fitems%2F1940409" title="新しい挑戦をしたい時に見る本 Vol.2.5 - まんてらスタジオ - BOOTH" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://booth.pm/ja/items/1940409">booth.pm</a></cite></p> <p><strong>基本無料</strong>というのは以下のような理念で頒布しているからです。</p> <blockquote><p>『新しい挑戦をしたいときに見る本』シリーズとは、まんてらスタジオに集まっているクリエイター達が、好き勝手に自分の書きたい物を提示して、好き勝手に技術書を書き、それを我々で一つの本にして出版しているプロジェクトの事です。 このプロジェクトの最大の特徴として、挑戦本は無料で頒布しております。この本を本当に求めている人からはお金を頂かずに、このプロジェクトを応援したい人から支援を頂いて活動をしております。</p></blockquote> <p>本誌は<a href="https://twitter.com/manntera">@manntera</a>さん主催のまんてらスタジオに集まった計10人のエンジニア/デザイナーによる合同誌の第2弾です。 私はChapter10の「対話型日記<a class="keyword" href="http://d.hatena.ne.jp/keyword/BOT">BOT</a>開発を通じて学ぶ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%AB%C1%B3%B8%C0%B8%EC%BD%E8%CD%FD">自然言語処理</a>入門」を担当しました。</p> <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【<a href="https://twitter.com/hashtag/C97?src=hash&amp;ref_src=twsrc%5Etfw">#C97</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%C5%DF%A5%B3%A5%DF">冬コミ</a>情報】<br><br>『新しい挑戦をしたいときに見る本』の紹介!<br><br>Web系企業<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DE">プログラマ</a><a href="https://twitter.com/Sashimimochi343?ref_src=twsrc%5Etfw">@Sashimimochi343</a> さんの、<br><br>対話型日記<a class="keyword" href="http://d.hatena.ne.jp/keyword/BOT">BOT</a>開発を通じて学ぶ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%AB%C1%B3%B8%C0%B8%EC%BD%E8%CD%FD">自然言語処理</a>入門<br><br>を紹介します!<a href="https://twitter.com/hashtag/%E6%8C%91%E6%88%A6%E6%9C%AC?src=hash&amp;ref_src=twsrc%5Etfw">#挑戦本</a><a href="https://twitter.com/hashtag/%E3%81%BE%E3%82%93%E3%81%A6%E3%82%89%E3%82%B9%E3%82%BF%E3%82%B8%E3%82%AA?src=hash&amp;ref_src=twsrc%5Etfw">#まんてらスタジオ</a><a href="https://twitter.com/hashtag/DeepLearning?src=hash&amp;ref_src=twsrc%5Etfw">#DeepLearning</a> <a href="https://twitter.com/hashtag/%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E%E5%87%A6%E7%90%86?src=hash&amp;ref_src=twsrc%5Etfw">#自然言語処理</a><a href="https://twitter.com/hashtag/%E3%82%B3%E3%83%9F%E3%82%B197?src=hash&amp;ref_src=twsrc%5Etfw">#コミケ97</a> <a href="https://twitter.com/hashtag/C97%E3%81%8A%E5%93%81%E6%9B%B8%E3%81%8D?src=hash&amp;ref_src=twsrc%5Etfw">#C97お品書き</a> <a href="https://twitter.com/hashtag/%E3%82%B3%E3%83%9F%E3%83%83%E3%82%AF%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%83%E3%83%8897?src=hash&amp;ref_src=twsrc%5Etfw">#コミックマーケット97</a> <a href="https://t.co/FCsILqgn6i">pic.twitter.com/FCsILqgn6i</a></p>&mdash; まんてら@<a class="keyword" href="http://d.hatena.ne.jp/keyword/VTuber">VTuber</a>再準備中 (@manntera) <a href="https://twitter.com/manntera/status/1211213489403920386?ref_src=twsrc%5Etfw">December 29, 2019</a></blockquote> <p> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p> <h2>経緯</h2> <p>最近は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DF%A5%B1">コミケ</a>に加えて技術書展の盛り上がりにより良質な技術同人誌がとても増えてきています。 私も何度か一般参加で会場に足を運びましたが、出版社発行の書籍として世に出回っていないTipsやネット上に散らばっている知見を体系的に学べるので毎回楽しみにしています。 単純に共通の趣味を持った人たちが集まって話しているという雰囲気を味わうだけでもよい刺激がもらえます。 こういった光景を見ているうちに、自分も一筆認めてみたいという衝動に駆られました。 そんな折、まんてらスタジオさんのほうからお声掛けをいただきこの度、参加することにしました。</p> <h2>本誌(自分の担当章)の概要</h2> <p>対話形式で日記が書けるボット開発を通じて<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%AB%C1%B3%B8%C0%B8%EC%BD%E8%CD%FD">自然言語処理</a>の基礎とちょっとしたその応用について書いています。</p> <p><img src="https://s3.qrunch.io/b592967c1be3f8a5462e07355a43f4cb.png" width="40%" alt="サンプル画像1"> <img src="https://s3.qrunch.io/2229dc0477aa5a3722a8ea96e181db9c.png" width="40%" alt="サンプル画像2"></p> <p>本誌のタイトル通り、読者の方に本誌の内容をキッカケに新しい挑戦をしてもらえるように導入から丁寧に解説しました。</p> <p>私自身、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%AB%C1%B3%B8%C0%B8%EC%BD%E8%CD%FD">自然言語処理</a>を学び始めて1年弱の初心者ではありますが、そんな人でも1年あればこれくらいならできるんだと感じていただき、これから始めようか迷っている人が後押しができればという思いで執筆しました。</p> <p>せっかく書くなら単なるやってみたで終わらせたくなかったので、理論と実装の両面から個人的な勘所をたくさん詰め込みました。 その結果、全体の約1/3のページを占める大作になってしまいました。</p> <p><figure class="figure-image figure-image-fotolife" title="表紙ページ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20200105/20200105160858.png" alt="f:id:T-N-Clark:20200105160858p:plain:w400" title="f:id:T-N-Clark:20200105160858p:plain:w400" class="hatena-fotolife" style="width:400px" itemprop="image"></span><figcaption>表紙ページ</figcaption></figure></p> <p>また、本誌の中で紹介しているデモボットも公開しています。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2FSashimimochi%2Fdiarybot-Momo" title="Sashimimochi/diarybot-Momo" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/Sashimimochi/diarybot-Momo">github.com</a></cite></p> <p>もし、少しでも興味を持っていただけたなら、ぜひお手に取っていただけると幸いです、 その際は、感想をお聞<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%AB%A4%BB%A4%A4">かせい</a>ただけると大変喜びます。 それでは、また。</p> T-N-Clark 2019年個人的注目アドベントカレンダー(随時更新) hatenablog://entry/26006613462263152 2019-11-21T11:45:09+09:00 2019-12-27T20:59:41+09:00 企業系 Retty 富士通 今年もDeepLearing100選あるのかな? はてな Yahoo Japan サイバーエージェント 情報検索・検索エンジン Advent Calendar 2019 開発・研究系 Discord 最近趣味の開発テーマの主軸なので ←12/24分に参加いたしました NLP NLP2 こちらも最近の個人開発のもうひとつの軸 公開1日でほぼほぼ埋まるほど人気のテーマ!? Deep Learning論文紹介 Advent Calendar 2019 今年読んだ一番好きな論文2019 <h2>企業系</h2> <ul> <li><a href="https://qiita.com/advent-calendar/2019/retty">Retty</a></li> <li><a href="https://qiita.com/advent-calendar/2019/fujitsu">富士通</a> 今年もDeepLearing100選あるのかな?</li> <li><a href="https://qiita.com/advent-calendar/2019/hatena">はてな</a></li> <li><a href="https://qiita.com/advent-calendar/2019/yahoojapan">Yahoo Japan</a></li> <li><p><a href="https://adventar.org/calendars/3981">サイバーエージェント</a></p></li> <li><p><a href="https://qiita.com/advent-calendar/2019/search">情報検索・検索エンジン Advent Calendar 2019</a></p></li> </ul> <h2>開発・研究系</h2> <ul> <li><a href="https://qiita.com/advent-calendar/2019/discord">Discord</a> 最近趣味の開発テーマの主軸なので ←12/24分に参加いたしました</li> <li><a href="https://qiita.com/advent-calendar/2019/nlp">NLP</a></li> <li><a href="https://qiita.com/advent-calendar/2019/nlp2">NLP2</a> こちらも最近の個人開発のもうひとつの軸 公開1日でほぼほぼ埋まるほど人気のテーマ!?</li> <li><a href="https://qiita.com/yu4u/items/c5aeff77758bebbe982f">Deep Learning論文紹介 Advent Calendar 2019</a></li> <li><a href="https://adventar.org/calendars/4489">今年読んだ一番好きな論文2019</a></li> </ul> T-N-Clark Herokuでpython+mecab+ffmpegを使う hatenablog://entry/26006613445281857 2019-10-07T11:30:00+09:00 2019-10-07T11:30:01+09:00 はじめに 最近、ちまちまとdiscord上で動くchatbotを作っているのですが、やっぱり公開サーバーで動かさないと日常的に使えないのでHerokuにデプロイすることにしました。 ただ、無料枠のHerokuで動かそうとするといろいろと解決しないといけない課題が多くてひたすら逃げていたのですが、ようやく重い腰を上げてデプロイしたので、手順をメモがてら残してみたいと思います。 必要な要件 python (3.5以上) mecab+mecab-ipadic FFmpeg open-jtalk を使っているのでこれらが動かせる環境が必要。 Herokuでpython+mecabの環境づくりは下記の記… <h2>はじめに</h2> <p>最近、ちまちまとdiscord上で動くchatbotを作っているのですが、やっぱり公開サーバーで動かさないと日常的に使えないのでHerokuにデプロイすることにしました。 ただ、無料枠のHerokuで動かそうとするといろいろと解決しないといけない課題が多くてひたすら逃げていたのですが、ようやく重い腰を上げてデプロイしたので、手順をメモがてら残してみたいと思います。</p> <h2>必要な要件</h2> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a> (3.5以上)</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/mecab">mecab</a>+<a class="keyword" href="http://d.hatena.ne.jp/keyword/mecab">mecab</a>-ipadic</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/FFmpeg">FFmpeg</a></li> <li>open-jtalk</li> </ul> <p>を使っているのでこれらが動かせる環境が必要。</p> <p>Herokuで<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>+<a class="keyword" href="http://d.hatena.ne.jp/keyword/mecab">mecab</a>の環境づくりは下記の記事を参考にしました。 <a href="https://qiita.com/kenchin110100/items/6f1c84ac8858525fffc5">herokuでpython+django+scikit-learn+mecab(1)</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/ffmpeg">ffmpeg</a>の環境づくりはこちらを参考にしました。 <a href="https://note.mu/akaness_note/n/nc466d2a00ead">DiscordBotでyoutubeの音声をボイスチャットに流す</a></p> <h2>いざ、構築</h2> <p>condaとherokuの複数のビルドパックを使うので<a href="https://github.com/heroku/heroku-buildpack-multi">heroku-buildpack-multi</a>でアプリを作成する必要があります。気になるのはこの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>がメンテナンスを終了していることでしょうか。</p> <blockquote><p><strong>This buildpack is no longer actively maintained.</strong> The associated functionality exists natively on the Heroku platform. Please refer to <a href="https://devcenter.heroku.com/articles/buildpacks">https://devcenter.heroku.com/articles/buildpacks</a> and <a href="https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app">https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app</a> for documentation.</p></blockquote> <p>まず、ローカルで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>を作ります。</p> <pre class="code bash" data-lang="bash" data-unlink>$ git init $ heroku create --buildpack https://github.com/heroku/heroku-buildpack-multi</pre> <p>使用するビルドパックは以下の通りです。</p> <pre class="code bash:.buildpacks" data-lang="bash:.buildpacks" data-unlink>https://github.com/Sashimimochi/conda-buildpack.git https://github.com/sunny4381/heroku-buildpack-linuxbrew.git https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git https://github.com/Crazycatz00/heroku-buildpack-libopus.git</pre> <p><code>scikit-learn</code>などのC<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%E9">コンパイラ</a>を必要とするライブラリも使いたいのでcondaのビルドパックがいるのですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>のバージョンを固定したいので以下の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>をforkして自前で用意することにしました。 <a href="https://elements.heroku.com/buildpacks/teamupstart/conda-buildpack">https://elements.heroku.com/buildpacks/teamupstart/conda-buildpack</a> minicondaのバージョン一覧はこちらから見れます。 <a href="https://repo.continuum.io/miniconda/">https://repo.continuum.io/miniconda/</a></p> <p>ちなみに、ものによっては、以下のような最近のpipでは廃止されたオプションが付いていてビルドエラーを起こすので注意。</p> <pre class="code bash" data-lang="bash" data-unlink>pip install -r requirements.txt --exists-action=w --allow-all-external | indent</pre> <ul> <li><a href="https://github.com/heroku-python/conda-buildpack/issues/36">https://github.com/heroku-python/conda-buildpack/issues/36</a></li> <li><a href="https://stackoverflow.com/questions/57546079/no-such-option-allow-all-external-when-deploying-a-django-app-to-heroku-wit">https://stackoverflow.com/questions/57546079/no-such-option-allow-all-external-when-deploying-a-django-app-to-heroku-wit</a></li> </ul> <p>上記の<code>.buildpacks</code>で<code>heroku-buildpack-linuxbrew</code>を入れたので<code>brew</code>が使えるようになっています。<code>brew</code>で入れたいパッケージを<code>.cellar</code>で指定します。</p> <pre class="code bash:.cellar" data-lang="bash:.cellar" data-unlink>mecab mecab-ipadic open-jtalk</pre> <p><code>conda</code>および<code>pip</code>でインストールするライブラリは<code>conda-requirements.txt</code>や<code>requirements.txt</code>に書いておきます。</p> <p>Herokuに<a href="https://pypi.org/project/mecab-python/">mecab-python</a>をインストールしようとすると案の定、エラーが出ました。 <a href="https://qiita.com/kenchin110100/items/6f1c84ac8858525fffc5">herokuでpython+django+scikit-learn+mecab(1)</a>では手動でインストールしていましたが、今回は、python3系を使うわけですし、<a href="https://pypi.org/project/mecab-python3/">mecab-python3</a>ならはいったので、代わりにこちらを入れます。 <a href="https://ja.stackoverflow.com/questions/48649/heroku-mecab%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%99%82%E3%81%AB%E3%82%A8%E3%83%A9%E3%83%BC">Heroku mecabインストール時にエラー</a></p> <p>あとはパスを指定してデプロイします。</p> <pre class="code bash" data-lang="bash" data-unlink>$ heroku config:add LD_LIBRARY_PATH=/app/.linuxbrew/lib $ heroku config:set MECAB_PATH=/app/.linuxbrew/lib/libmecab.so $ git add . $ git commit -m &#39;initial&#39; $ git push heroku master</pre> <p>滞りなく、一連のデプロイが進んで無事うまくいったように思えたのですが、最後のメッセージであえなく撃沈。</p> <pre class="code bash" data-lang="bash" data-unlink>-----&gt; Compressing... ! Compiled slug size: 3009.8M is too large (max is 500M). ! See: http://devcenter.heroku.com/articles/slug-size ! Push failed</pre> <p><a href="https://devcenter.heroku.com/articles/slug-compiler#slug-size">https://devcenter.heroku.com/articles/slug-compiler#slug-size</a> 上記のメッセージにもある通り、Herokuの無料枠のストレージサイズが500MBなのでデプロイ時にこの範囲内に収める必要があります。</p> <p>容量を食っているのが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B5%A1%B3%A3%B3%D8%BD%AC">機械学習</a>モデルだったので、Herokuでこれを動かすにはモデルのサイズを圧縮しておく必要がありました。 中でも一番のファイルサイズが大きいのがWord2Vecだったので、こちらのライブラリを使ってモデルサイズを圧縮しました。 50,000語程度にまで絞れば約60MBまで圧縮できます。 <a href="https://github.com/yagays/minify_w2v">https://github.com/yagays/minify_w2v</a> 重要語は以下から選択しました。 <a href="http://tatsuma2010.web.fc2.com/">日本語を読むための語彙データベース Ver. 1.1</a> その他にもcacheを削除するなどいろいろ工夫の余地はありそうです。</p> <ul> <li><a href="https://masutaka.net/chalow/2018-12-21-1.html">https://masutaka.net/chalow/2018-12-21-1.html</a></li> <li><a href="https://github.com/heroku/heroku-repo#purge_cache">https://github.com/heroku/heroku-repo#purge_cache</a></li> </ul> <p>これで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>の容量をかなり圧縮できました。</p> <p>大量のライブラリを使ってしまったせいで、これでもデプロイ時に<code>pip install</code>までしてしまうと上限の500MBを超えてしまったので、起動時に<code>pip install</code>が走るようにしておきました。</p> <pre class="code shell:Procfile" data-lang="shell:Procfile" data-unlink>bot: bash run.sh</pre> <pre class="code shell:run.sh" data-lang="shell:run.sh" data-unlink>pip install -r requirements.txt python app.py</pre> <p>以上で、なんとか無事に動きました。</p> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%E9%A5%D6%A5%EB%A5%B7%A5%E5%A1%BC%A5%C6%A5%A3%A5%F3%A5%B0">トラブルシューティング</a></h2> <h3>Heroku<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>にログイン</h3> <p>Herokuにログインしてマニュアル操作するときは以下のようにすればログインして<code>bash</code>コマンドが使えます。</p> <pre class="code bash" data-lang="bash" data-unlink>heroku run bash</pre> <h3>ログの確認</h3> <p>デプロイ後にアプリケーションのログが見たければ以下のコマンドで確認できます。</p> <pre class="code bash" data-lang="bash" data-unlink>$ heroku logs</pre> <p><a href="http://info-i.net/heroku-log">Herokuアプリケーションのログについて</a></p> T-N-Clark やっぱりはてなブログを自分のホームページに埋め込みたい hatenablog://entry/17680117127149552003 2019-05-30T11:30:00+09:00 2021-11-26T01:07:32+09:00 長年お世話になっていたYQLがサービスを停止したので、ReactでRSSフィードを取得して表示する方法を考えてみました。 <p>※2021/1/18に複数のサイトから<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>を取得したい場合のやり方を末尾に追記しました。</p> <h2>経緯</h2> <p>長年お世話になっていた<a class="keyword" href="http://d.hatena.ne.jp/keyword/YQL">YQL</a>が残念ながら2019年1月に無期限のサービス停止を発表されました。Yahooさん、今までありがとうございました。</p> <blockquote class="twitter-tweet" data-lang="ja"> <p dir="ltr" lang="en">On Jan. 3, 2019, <a class="keyword" href="http://d.hatena.ne.jp/keyword/YQL">YQL</a> service at <a href="https://t.co/g4W9RhdMLk">https://t.co/g4W9RhdMLk</a> will be retired. <a class="keyword" href="http://d.hatena.ne.jp/keyword/YQL">YQL</a> based services that use <a href="https://t.co/g4W9RhdMLk">https://t.co/g4W9RhdMLk</a>, including users of <a href="https://t.co/5IkUaEykdl">https://t.co/5IkUaEykdl</a>, will no longer operate. Yahoo Weather <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> users see the <a class="keyword" href="http://d.hatena.ne.jp/keyword/tweet">tweet</a> below for info about continuing your service.</p> — Y! Developer Network (@ydn) <a href="https://twitter.com/ydn/status/1079785891558653952?ref_src=twsrc%5Etfw">2018年12月31日</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <p>したがって、再び新しい方法を考える必要が出てきました。<br /> 私情ではありますが、最近ようやくReactを勉強し始めましたので、今回はReactを使った<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS%A5%D5%A5%A3%A1%BC%A5%C9">RSSフィード</a>の取得・表示方法を考えてみました。<br /> 着手当初はどうしようと戦々恐々だったのですが、比較的簡単に行けそうだったのでご紹介してみます。<br /> やり方さえ分かれば、30分もかからずにできてしまうと思います。(私は調べる過程を含めると半日くらいかかりました。)<br /> ※Reactの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E5%A1%BC%A5%C8%A5%EA%A5%A2%A5%EB">チュートリアル</a>すらまともに理解しているか怪しい初心者がトライ&amp;エラーで考えた方法なので、ベテランの方からすれば滅茶苦茶な実装になっているかもしれません。ご容赦ください。</p> <h2>とりあえず<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報を取得して表示する</h2> <p>何はともあれ、まずは完成形をお見せします。無事、ブログのタイトルとリンクを取得して飛べるようになりました。<br /> 見てくれ部分は私のUIセンスの問題なので、これなんかよりいくらでも格好よくできると思うので、ぜひ各々アレンジしてみてください。<br /> (というか、格好いいデザインができたら教えてください)</p> <p><figure class="figure-image figure-image-fotolife" title="完成図"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004403.gif" alt="f:id:T-N-Clark:20190530004403g:plain" width="591" height="320" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>完成図</figcaption></figure></p> <p>環境は</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ create-react-app <span class="synSpecial">-V</span> <span class="synConstant">2</span>.<span class="synConstant">1</span>.<span class="synConstant">1</span> </pre> <p>を使っています。 で、肝心の実装ですが、あれこれ調べてみたところ、手っ取り早く実現できそうなものにRSSParserというライブラリがありました。<br /> ということで今回は、このRSSParserを使います。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Frss-parser" title="rss-parser" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.npmjs.com/package/rss-parser">www.npmjs.com</a></cite></p> <p>ライブラリのインストールは</p> <pre class="code lang-sh" data-lang="sh" data-unlink>npm i rss-parser </pre> <p>とかで出来ます。</p> <p>ひとまず、<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報を取ってきてコンソールに表示させてみます。<br /> 取り急ぎ動作確認したいなら<code>create-react-app</code>でプロジェクト?を作ってApp.jsに書き込めば動作確認は出来ます。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">import</span> React from <span class="synConstant">'react'</span>; <span class="synComment">//中略</span> <span class="synStatement">const</span> RssParser = require(<span class="synConstant">'rss-parser'</span>); <span class="synComment">//importではなくrequireで呼びます</span> <span class="synStatement">const</span> url = <span class="synConstant">'http://t-n-clark.hatenadiary.jp/feed'</span> <span class="synStatement">const</span> rssParser = <span class="synStatement">new</span> RssParser(); rssParser.parseURL(url) .then((feed) =&gt; <span class="synIdentifier">{</span> console.log(feed); <span class="synIdentifier">}</span>); .<span class="synStatement">catch</span>((error) =&gt; <span class="synIdentifier">{</span> console.error(<span class="synConstant">'Get Failed'</span>, error); <span class="synIdentifier">}</span>); <span class="synStatement">class</span> App <span class="synStatement">extends</span> Component <span class="synIdentifier">{</span> render(props) <span class="synIdentifier">{</span> <span class="synStatement">const</span> classes = <span class="synIdentifier">this</span>.props.classes; <span class="synComment">//...後略</span> </pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Chrome">Chrome</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D9%A5%ED%A5%C3%A5%D1">デベロッパ</a>ーツールで確認すると、ちゃんとコンソールに取得した<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報が出ています。</p> <div class="images-row mceNonEditable"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004713.jpg" alt="f:id:T-N-Clark:20190530004713j:plain" width="546" height="372" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004716.jpg" alt="f:id:T-N-Clark:20190530004716j:plain" width="540" height="561" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004723.jpg" alt="f:id:T-N-Clark:20190530004723j:plain" width="539" height="516" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></div> <p>ざっと見たところ、記事のタイトルとかの情報はこのitemsに入っているみたいです。</p> <p>ちなみに、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%ED%A5%B0">はてなブログ</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報は</p> <pre class="code lang-sh" data-lang="sh" data-unlink>各ブログのトップURL/rss </pre> <p>もしくは</p> <pre class="code lang-sh" data-lang="sh" data-unlink>各ブログのトップ/feed </pre> <p>で取得出来ます。 例えば、このブログの<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>は<br /> <a href="https://t-n-clark.hatenadiary.jp/feed">https://t-n-clark.hatenadiary.jp/feed</a><br /> で取れます。 ※HTTPでも取れそうな気がするのですが、HTTPだと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%DE%A5%DB">スマホ</a>ではちゃんと取れなかった表示されなかったので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/HTTPS">HTTPS</a>をおすすめします。 (端末ではなく、ブラウザの問題かもしれませんが... これが関係するのでしょうか?) <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fbookmark.hatenastaff.com%2Fentry%2F2019%2F05%2F28%2F141208" title="はてなブックマークのすべてのページで HTTPS が使われるようになりました - はてなブックマーク開発ブログ" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://bookmark.hatenastaff.com/entry/2019/05/28/141208">bookmark.hatenastaff.com</a></cite></p> <p>Qiitaであれば、</p> <pre class="code lang-sh" data-lang="sh" data-unlink>https://qiita.com/アカウント名/feed.atom </pre> <p>で取得出来ます。 例えば、こんな感じ<br /> <a href="https://qiita.com/Sashimimochi/feed.atom">https://qiita.com/Sashimimochi/feed.atom</a><br /> ただ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/HTTPS">HTTPS</a>の場合はクロス<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C9%A5%E1%A5%A4%A5%F3">ドメイン</a>制約があるので、URLの前にCORS(cross-origin resource sharing)PROXYをつけてあげる必要があります。<br /> 上記のRSSParserのページを読んだところ、こんな感じで行けそうです。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">const</span> CORS_PROXY = <span class="synConstant">&quot;https://cors-anywhere.herokuapp.com/&quot;</span> <span class="synComment">//はてなブログならこっち</span> <span class="synStatement">const</span> url = CORS_PROXY + <span class="synConstant">'https://t-n-clark.hatenadiary.jp/feed'</span> <span class="synComment">//Qiitaならこっち</span> <span class="synStatement">const</span> url = CORS_PROXY + <span class="synConstant">'https://qiita.com/Sashimimochi/feed.atom'</span> </pre> <p>あとは取得した情報をstateに格納してJSX内で呼び出してみます。 先ほど<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D0%A5%C3%A5%B0">デバッグ</a>用に出していたConsoleへの出力はもういらないので消しても大丈夫です。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">import</span> React from <span class="synConstant">'react'</span>; <span class="synStatement">import</span> <span class="synIdentifier">{</span> Component <span class="synIdentifier">}</span> from <span class="synConstant">'react'</span>; <span class="synStatement">import</span> PropTypes from <span class="synConstant">'prop-types'</span>; <span class="synStatement">import</span> <span class="synConstant">'./App.css'</span>; <span class="synComment">/*RSSParserのインスタンス生成*/</span> <span class="synStatement">const</span> RssParser = require(<span class="synConstant">'rss-parser'</span>); <span class="synStatement">const</span> url = <span class="synConstant">&quot;http://t-n-clark.hatenadiary.jp/feed&quot;</span> <span class="synStatement">const</span> rssParser = <span class="synStatement">new</span> RssParser(); <span class="synStatement">class</span> App <span class="synStatement">extends</span> Component <span class="synIdentifier">{</span> <span class="synComment">/*コンストラクターの定義*/</span> constructor(props) <span class="synIdentifier">{</span> <span class="synStatement">super</span>(props); <span class="synIdentifier">this</span>.state = <span class="synIdentifier">{</span> contents: <span class="synIdentifier">[]</span>, <span class="synIdentifier">}</span>; <span class="synIdentifier">this</span>.componentDidMount = <span class="synIdentifier">this</span>.componentDidMount.bind(<span class="synIdentifier">this</span>) <span class="synIdentifier">}</span> <span class="synComment">/*stateにrssParserの結果をバインド*/</span> componentDidMount() <span class="synIdentifier">{</span> rssParser.parseURL(url) .then((feed) =&gt; <span class="synIdentifier">{</span> <span class="synStatement">const</span> data = feed.items; console.log(data); <span class="synIdentifier">this</span>.setState(<span class="synIdentifier">{</span> contents: <span class="synIdentifier">[</span>...data<span class="synIdentifier">]</span> <span class="synIdentifier">}</span>); <span class="synIdentifier">}</span>) .<span class="synStatement">catch</span>((error) =&gt; <span class="synIdentifier">{</span> console.error(<span class="synConstant">'Get Failed'</span>, error); <span class="synIdentifier">}</span>) <span class="synIdentifier">}</span> render(props) <span class="synIdentifier">{</span> <span class="synStatement">const</span> classes = <span class="synIdentifier">this</span>.props.classes; <span class="synComment">/*表示するコンテンツの形に合わせてstateの中身を書きだす(mapで拡張forループみたいに使う)*/</span> <span class="synStatement">const</span> contents = <span class="synIdentifier">this</span>.state.contents.map(content =&gt; <span class="synIdentifier">{</span> <span class="synStatement">return</span> &lt;div&gt; <span class="synIdentifier">{</span>content.title<span class="synIdentifier">}</span> <span class="synComment">/*{content.pubDate} 公開日(Qiitaなら{content.published})*/</span> <span class="synComment">/*{content.link} ページリンク*/</span> &lt;/div&gt; <span class="synIdentifier">}</span>); <span class="synStatement">return</span> ( &lt;div className=<span class="synConstant">&quot;App&quot;</span>&gt; &lt;header className=<span class="synConstant">&quot;App-header&quot;</span>&gt; <span class="synIdentifier">{</span>contents<span class="synIdentifier">}</span> <span class="synComment">/*ここに出力*/</span> &lt;/header&gt; &lt;/div&gt; ); <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> App.propTypes = <span class="synIdentifier">{</span> classes: PropTypes.object.isRequired, <span class="synIdentifier">}</span>; <span class="synStatement">export</span> <span class="synStatement">default</span> App; </pre> <p>これで無事<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報を取得して表示できるようになりました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004740.jpg" alt="f:id:T-N-Clark:20190530004740j:plain" width="1200" height="649" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>あとは、これを自分なりに装飾していくことになります。</p> <h2>見てくれを整える</h2> <p>参考になるかはわかりませんが、冒頭にあった私なりの装飾を紹介してみます。 主にMaterial-Ul(のCard)とreact-moment(※momentではありません)を使っています。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fmaterial-ui.com%2F" title="MUI: The React component library you always wanted" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://material-ui.com/">material-ui.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Freact-moment" title="react-moment" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.npmjs.com/package/react-moment">www.npmjs.com</a></cite></p> <p>×こっちではありません。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fmoment" title="moment" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.npmjs.com/package/moment">www.npmjs.com</a></cite></p> <p>それぞれ</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synComment"># Material-UI (@4.0.1)</span> $ npm install @materail-ui/core <span class="synComment"># react-moment (@0.9.2)</span> $ npm i react-moment </pre> <p>でインストールできます。 まずは、ほぼほぼMaterial-Ulの公式ドキュメントにあるCardのサンプルに先程の<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>データを流し込みます。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">import</span> React from <span class="synConstant">'react'</span>; <span class="synStatement">import</span> <span class="synIdentifier">{</span> Component <span class="synIdentifier">}</span> from <span class="synConstant">'react'</span>; <span class="synStatement">import</span> PropTypes from <span class="synConstant">'prop-types'</span>; <span class="synStatement">import</span> Card from <span class="synConstant">'@material-ui/core/Card'</span>; <span class="synStatement">import</span> Button from <span class="synConstant">'@material-ui/core/Button'</span>; <span class="synStatement">import</span> Avatar from <span class="synConstant">'@material-ui/core/Avatar'</span>; <span class="synStatement">import</span> red from <span class="synConstant">'@material-ui/core/colors/red'</span>; <span class="synStatement">import</span> <span class="synIdentifier">{</span> withStyles <span class="synIdentifier">}</span> from <span class="synConstant">'@material-ui/core/styles'</span>; <span class="synStatement">import</span> Grid from <span class="synConstant">'@material-ui/core/Grid'</span>; <span class="synStatement">import</span> <span class="synConstant">'./App.css'</span>; <span class="synStatement">import</span> <span class="synIdentifier">{</span> CardHeader, CardContent <span class="synIdentifier">}</span> from <span class="synConstant">'@material-ui/core'</span>; <span class="synStatement">const</span> RssParser = require(<span class="synConstant">'rss-parser'</span>); <span class="synStatement">const</span> url = <span class="synConstant">&quot;http://t-n-clark.hatenadiary.jp/feed&quot;</span> <span class="synStatement">const</span> rssParser = <span class="synStatement">new</span> RssParser(); <span class="synStatement">const</span> styles = theme =&gt; (<span class="synIdentifier">{</span> blog: <span class="synIdentifier">{</span> margin: 20, padding: 20, marginTop: 10, <span class="synIdentifier">}</span>, blogcontent: <span class="synIdentifier">{</span> overflowY: <span class="synConstant">'scroll'</span>, height: <span class="synConstant">'200px'</span>, <span class="synIdentifier">}</span>, avatar: <span class="synIdentifier">{</span> backgroundColor: red<span class="synIdentifier">[</span>500<span class="synIdentifier">]</span>, <span class="synIdentifier">}</span>, <span class="synIdentifier">}</span>); <span class="synStatement">class</span> App <span class="synStatement">extends</span> Component <span class="synIdentifier">{</span> constructor(props) <span class="synIdentifier">{</span> <span class="synStatement">super</span>(props); <span class="synIdentifier">this</span>.state = <span class="synIdentifier">{</span> contents: <span class="synIdentifier">[]</span>, <span class="synIdentifier">}</span>; <span class="synIdentifier">this</span>.componentDidMount = <span class="synIdentifier">this</span>.componentDidMount.bind(<span class="synIdentifier">this</span>) <span class="synIdentifier">}</span> componentDidMount() <span class="synIdentifier">{</span> rssParser.parseURL(url) .then((feed) =&gt; <span class="synIdentifier">{</span> <span class="synStatement">const</span> data = feed.items; console.log(data); <span class="synIdentifier">this</span>.setState(<span class="synIdentifier">{</span> contents: <span class="synIdentifier">[</span>...data<span class="synIdentifier">]</span> <span class="synIdentifier">}</span>); <span class="synIdentifier">}</span>) .<span class="synStatement">catch</span>((error) =&gt; <span class="synIdentifier">{</span> console.error(<span class="synConstant">'Get Failed'</span>, error); <span class="synIdentifier">}</span>) <span class="synIdentifier">}</span> render(props) <span class="synIdentifier">{</span> <span class="synStatement">const</span> classes = <span class="synIdentifier">this</span>.props.classes; <span class="synStatement">const</span> contents = <span class="synIdentifier">this</span>.state.contents.map(content =&gt; <span class="synIdentifier">{</span> <span class="synStatement">return</span> &lt;Card&gt; &lt;CardHeader avatar=<span class="synIdentifier">{</span>&lt;Avatar arial-label=<span class="synConstant">&quot;Blog&quot;</span> className=<span class="synIdentifier">{</span>classes.avatar<span class="synIdentifier">}</span>&gt;も&lt;/Avatar&gt;<span class="synIdentifier">}</span> subheader=<span class="synIdentifier">{</span>content.pubDate<span class="synIdentifier">}</span> title=<span class="synIdentifier">{</span>content.title<span class="synIdentifier">}</span> /&gt; &lt;CardContent key=<span class="synIdentifier">{</span>content.id<span class="synIdentifier">}</span>&gt; &lt;Button size=<span class="synConstant">&quot;small&quot;</span> color=<span class="synConstant">&quot;primary&quot;</span> href=<span class="synIdentifier">{</span>content.link<span class="synIdentifier">}</span>&gt;Read More&lt;/Button&gt; &lt;/CardContent&gt; &lt;/Card&gt; <span class="synIdentifier">}</span>); <span class="synStatement">return</span> ( &lt;div className=<span class="synConstant">&quot;App&quot;</span>&gt; &lt;header className=<span class="synConstant">&quot;App-header&quot;</span>&gt; &lt;Grid container&gt; &lt;Grid item xs=<span class="synIdentifier">{</span>12<span class="synIdentifier">}</span> sm=<span class="synIdentifier">{</span>7<span class="synIdentifier">}</span>&gt; &lt;div className=<span class="synIdentifier">{</span>classes.blog<span class="synIdentifier">}</span>&gt; &lt;h3&gt;はてなブログ更新情報&lt;/h3&gt; &lt;div className=<span class="synIdentifier">{</span>classes.blogcontent<span class="synIdentifier">}</span>&gt;<span class="synIdentifier">{</span>contents<span class="synIdentifier">}</span>&lt;/div&gt; &lt;/div&gt; &lt;/Grid&gt; &lt;/Grid&gt; &lt;/header&gt; &lt;/div&gt; ); <span class="synIdentifier">}</span> <span class="synIdentifier">}</span> App.propTypes = <span class="synIdentifier">{</span> classes: PropTypes.object.isRequired, <span class="synIdentifier">}</span>; <span class="synStatement">export</span> <span class="synStatement">default</span> withStyles(styles)(App); </pre> <p>今回はタイトル(<code>title</code>)と日付(<code>pubDate</code>)とリンク(<code>link</code>)のみを使っていますが、<br /> スペースとの相談次第では、サマリー(<code>contentSnippet</code>)とか入れてもいいかもしれません。<br /> (RSSParserってサムネイル画像も取れているのでしょうか?もし取れているならサムネイル画像を使うのもありだと思います。)</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>のfeed側の仕様なのか、RSSParser側の仕様なのか確かめてませんが、デフォルトだと最新30件?分の<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報が取れているみたいです。<br /> なので、それをそのまま表示すると中々悲惨なことになります。<br /> Material-UIにもbootstrapのようなグリッドシステム的なものがありますので、Gridで幅調整をすることが出来ます。<br /> Gridを使えば多少のレスポンシブデザインに対応出来ます。<br /> あとは<code>overflowY: 'scroll'</code>で表示枠のサイズを指定したりしてみました。</p> <p><figure class="figure-image figure-image-fotolife" title="左:Gridなし,右:Gridあり"><div class="images-row mceNonEditable"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004747.jpg" alt="f:id:T-N-Clark:20190530004747j:plain" width="548" height="739" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004757.jpg" alt="f:id:T-N-Clark:20190530004757j:plain" width="869" height="314" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></div><figcaption>左:Gridなし,右:Gridあり</figcaption></figure></p> <p>これで個人的には、おおよその見た目は及第点くらいにはなったのですが、時刻表示が世界標準のままで見にくいです。<br /> そこで、<code>react-moment</code>というライブラリを使ってこれを整形します。<br /> RSSParserで取ってきた日付をフォーマットを指定してMomentタグで挟んであげればいいみたいです。<br /> 例えば、<code>YYYY/MM/DD</code>にしたければ、</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">import</span> Moment from <span class="synConstant">'react-moment'</span> &lt;Moment format=<span class="synConstant">&quot;YYYY/MM/DD&quot;</span>&gt;<span class="synIdentifier">{</span>content.pubDate<span class="synIdentifier">}</span>&lt;/Moment&gt; </pre> <p>とやれば、</p> <pre class="code text" data-lang="text" data-unlink># 変換前 2019-03-01T02:30:00.000Z # 変換後 2019/03/01</pre> <p>に出来ました。</p> <p><figure class="figure-image figure-image-fotolife" title="こんな感じ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004802.jpg" alt="f:id:T-N-Clark:20190530004802j:plain" width="857" height="298" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>こんな感じ</figcaption></figure></p> <p>他にもいろいろあるみたいなので、公式のサンプルを参考にお好みのフォーマットにしてみてください。</p> <p>ちなみにですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>の埋め込みなら、専用のライブラリ(私はreact-<a class="keyword" href="http://d.hatena.ne.jp/keyword/twitter">twitter</a>-widgetsを使っています)を使えばもっとお手軽にできてしまいます。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Ftimeline" title="timeline" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.npmjs.com/package/timeline">www.npmjs.com</a></cite></p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20190530/20190530004811.jpg" alt="f:id:T-N-Clark:20190530004811j:plain" width="478" height="401" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h2>あとがき</h2> <p>以上で、React限定&amp;初心者によるメチャメチャ実装ではありますが、ひとまず再びブログの更新情報を埋め込めるようになりました。<br /> この方法はあと何年使えるんだろう…?</p> <p>先日、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>から発表されたiframeに代わるPortalsタグを使えば、現在稼働中のクラシカルなページでもSPA並みにサクサク遷移できるようになるみたいです。<br /> まあ、まだearly stageみたいなので私は試していませんが… <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.google.com%2Fdocument%2Fd%2F1ITizGVUmfFGktOOynHFhx87cnJ__7EXy-4uMpOE0OAg%2Fedit" title="Portals" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://docs.google.com/document/d/1ITizGVUmfFGktOOynHFhx87cnJ__7EXy-4uMpOE0OAg/edit">docs.google.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fblog.uskay.io%2Farticle%2F002-hands-on-portals" title="話題の Portals を使った画面遷移UXの未来 | ウェブボウズ" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://blog.uskay.io/article/002-hands-on-portals">blog.uskay.io</a></cite></p> <p>私事ですが、今回<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS%A5%D5%A5%A3%A1%BC%A5%C9">RSSフィード</a>の実現方法を変える必要があったので、この機にいっそのことホームページの方も一新しようかなと思っています。<br /> 今見返すと一昔前のような見た目ですし。無事、一新したら紹介するかもしれません。 暫定版→<a href="http://clarkphys.html.xdomain.jp/index.html">http://clarkphys.html.xdomain.jp/index.html</a><cite class="hatena-citation"><a href="http://clarkphys.html.xdomain.jp/index.html">clarkphys.html.xdomain.jp</a></cite></p> <p>それではまた。</p> <h2>2021/1/18追記</h2> <p>効率が良いかは自信がありませんが、複数のサイトから<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報を取得する場合は以下のように<code>url</code>と<code>componentDidMount()</code>を書き換えればできます。 ポイントは次の2つです。</p> <ol> <li><code>setState</code>で情報を更新するときに、既存のstateと新規取得情報を結合しておく</li> <li>サイト内では順番通りになっているが結合したときに日付がバラバラなので、sortしておく</li> </ol> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">const</span> RssParser = require(<span class="synConstant">'rss-parser'</span>); <span class="synStatement">const</span> CORS_PROXY = <span class="synConstant">&quot;https://cors-anywhere.herokuapp.com/&quot;</span> <span class="synComment">// 取得したいURLをListで定義しておく</span> <span class="synStatement">const</span> urls = <span class="synIdentifier">[</span> CORS_PROXY + <span class="synConstant">'https://t-n-clark.hatenadiary.jp/feed'</span>, CORS_PROXY + <span class="synConstant">'https://qiita.com/Sashimimochi/feed.atom'</span> <span class="synIdentifier">]</span> <span class="synStatement">const</span> rssParser = <span class="synStatement">new</span> RssParser(); <span class="synStatement">class</span> Profile <span class="synStatement">extends</span> Component <span class="synIdentifier">{</span> constructor(props) <span class="synIdentifier">{</span> <span class="synStatement">super</span>(props); <span class="synIdentifier">this</span>.state = <span class="synIdentifier">{</span> contents: <span class="synIdentifier">[]</span>, <span class="synIdentifier">}</span>; <span class="synIdentifier">this</span>.componentDidMount = <span class="synIdentifier">this</span>.componentDidMount.bind(<span class="synIdentifier">this</span>) <span class="synIdentifier">}</span> componentDidMount() <span class="synIdentifier">{</span> urls.map((url) =&gt; <span class="synIdentifier">{</span> rssParser.parseURL(url) .then((feed) =&gt; <span class="synIdentifier">{</span> <span class="synComment">// 新たに取得したfeedを取り出す</span> <span class="synStatement">const</span> data = feed.items; <span class="synComment">// 既存のstateを取り出しておく</span> <span class="synIdentifier">var</span> feeds = <span class="synIdentifier">this</span>.state.contents <span class="synComment">// 既存のstateに新規で取得したfeedを1つずつ追加する</span> data.map((d) =&gt; <span class="synIdentifier">{</span> feeds.push(d) <span class="synIdentifier">}</span>) <span class="synComment">// 日付でsortする</span> feeds.sort(<span class="synIdentifier">function</span> (a, b) <span class="synIdentifier">{</span> <span class="synStatement">if</span> (a.pubDate &lt; b.pubDate) <span class="synIdentifier">{</span> <span class="synStatement">return</span> 1; <span class="synIdentifier">}</span> <span class="synStatement">if</span> (a.pubDate &gt; b.pubDate) <span class="synIdentifier">{</span> <span class="synStatement">return</span> -1; <span class="synIdentifier">}</span> <span class="synStatement">return</span> 0 <span class="synIdentifier">}</span>); <span class="synComment">// stateをupdateする</span> <span class="synIdentifier">this</span>.setState(<span class="synIdentifier">{</span> contents: feeds <span class="synIdentifier">}</span>); <span class="synIdentifier">}</span>) .<span class="synStatement">catch</span>((error) =&gt; <span class="synIdentifier">{</span> console.error(<span class="synConstant">'Get Failed'</span>, error); <span class="synIdentifier">}</span>) <span class="synIdentifier">}</span>) <span class="synIdentifier">}</span> </pre> <h2>次回</h2> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ft-n-clark.hatenadiary.jp%2Fentry%2F2021%2F11%2F18%2F210000" title="それでもはてなブログを自分のホームページに埋め込みたい - もちっとメモ" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://t-n-clark.hatenadiary.jp/entry/2021/11/18/210000">t-n-clark.hatenadiary.jp</a></cite></p> T-N-Clark PythonでDiscordのTRPG用のダイスボットを自作してみた hatenablog://entry/17680117126983707302 2019-03-01T11:30:00+09:00 2019-03-01T11:30:01+09:00 始まり 始めに断っておきますが、私はTRPGもDiscordもにわか勢です。 最近、身内でTRPG(主にCoC)をぼちぼちやるのですが、毎回ダイス振って判定するのってメンドくさいよねって話になりました。 セッション環境はdiscordを使い始めたので、discord上で動くbotにしようということになりました。正直なところ、既に先人のお方が作られた素晴らしいものがあるので「discord-bcdicebot使えばよいのでは?」というのが賢い選択だと思います。 ですが、ポンコツの私には使い方がわからなかったので、勉強も兼ねて友人に手伝ってもらいながら作ってみました。 Python + Googl… <h2>始まり</h2> <p>始めに断っておきますが、私は<a class="keyword" href="http://d.hatena.ne.jp/keyword/TRPG">TRPG</a>もDiscordもにわか勢です。</p> <p>最近、身内で<a class="keyword" href="http://d.hatena.ne.jp/keyword/TRPG">TRPG</a>(主にCoC)をぼちぼちやるのですが、毎回ダイス振って判定するのってメンドくさいよねって話になりました。 セッション環境はdiscordを使い始めたので、discord上で動く<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>にしようということになりました。正直なところ、既に先人のお方が作られた素晴らしいものがあるので「<a href="https://shunshun94.github.io/discord-bcdicebot/index.html">discord-bcdicebot</a>使えばよいのでは?」というのが賢い選択だと思います。 ですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DD%A5%F3%A5%B3%A5%C4">ポンコツ</a>の私には使い方がわからなかったので、勉強も兼ねて友人に手伝ってもらいながら作ってみました。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a> + <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a> (+ Heroku)で実現しています。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>全体は<a href="https://github.com/Sashimimochi/discord_dice_bot">GitHub</a>に晒しておきます。</p> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>から<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>にアクセスする</h2> <p>ダイスを振るだけなら不要なのですが、技能の成否判定も自動でしたいので<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AD%A5%E3%A5%E9%A5%AF%A5%BF%A1%BC%A5%B7%A1%BC%A5%C8">キャラクターシート</a>を<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>に作って判定することにしました。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>の操作方法はこちらを参考にしました。</p> <ul> <li><a href="https://qiita.com/Hidekazu-Karino/items/5201fce7249693357602">Google スプレッドシートをpythonで操作する</a></li> </ul> <p>こちらを参考にOAuth用のクライアントIDを作成して<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>ファイルをダウンロードするところまで進めます。 セキュリティを度外視するなら<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Apps">Google Apps</a> ScriptでHTTPリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを受け付けるという方法もあるかとは思います。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>の仕様は適当でこんな感じです。</p> <table> <thead> <tr> <th style="text-align:left;">技能</th> <th style="text-align:left;">合計値</th> <th style="text-align:left;">純減</th> <th style="text-align:left;">初期値</th> <th style="text-align:left;">職業P</th> <th style="text-align:left;">興味P</th> </tr> </thead> <tbody> <tr> <td style="text-align:left;">HP</td> <td style="text-align:left;">9</td> <td style="text-align:left;">0</td> <td style="text-align:left;">9</td> <td style="text-align:left;">0</td> <td style="text-align:left;">0</td> </tr> <tr> <td style="text-align:left;">MP</td> <td style="text-align:left;">7</td> <td style="text-align:left;">0</td> <td style="text-align:left;">7</td> <td style="text-align:left;">0</td> <td style="text-align:left;">0</td> </tr> <tr> <td style="text-align:left;">SAN</td> <td style="text-align:left;">60</td> <td style="text-align:left;">0</td> <td style="text-align:left;">60</td> <td style="text-align:left;">0</td> <td style="text-align:left;">0</td> </tr> <tr> <td style="text-align:left;">db</td> <td style="text-align:left;">1d4</td> <td style="text-align:left;">0</td> <td style="text-align:left;">1d4</td> <td style="text-align:left;">0</td> <td style="text-align:left;">0</td> </tr> <tr> <td style="text-align:left;">こぶし</td> <td style="text-align:left;">50</td> <td style="text-align:left;">0</td> <td style="text-align:left;">50</td> <td style="text-align:left;">0</td> <td style="text-align:left;">0</td> </tr> <tr> <td style="text-align:left;">図書館</td> <td style="text-align:left;">50</td> <td style="text-align:left;">0</td> <td style="text-align:left;">25</td> <td style="text-align:left;">25</td> <td style="text-align:left;">0</td> </tr> </tbody> </table> <p>初期値の決め方は下記を参照しました。</p> <ul> <li><a href="https://www.kya.site/entry/2018/06/26/%E3%82%AF%E3%83%88%E3%82%A5%E3%83%95%EF%BC%B4%EF%BC%B2%EF%BC%B0%EF%BC%A7%E6%8A%80%E8%83%BD%E4%B8%80%E8%A6%A7%EF%BC%88%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93%E8%A7%A3%E8%AA%AC">クトゥルフTRPG技能一覧(かんたん解説</a></li> <li><a href="http://www.geocities.jp/fematerials/CoC/01_CharacterMaking.html">キャラクター作成ルール</a></li> </ul> <p>この中で使うのは<strong>技能</strong>と<strong>合計値</strong>のカラムです。これを以下のような感じで<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>から取得します。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">get_gs</span>(): scopes = [<span class="synConstant">'https://www.googleapis.com/auth/spreadsheets'</span>] json_file = <span class="synConstant">'./hoge.json'</span><span class="synComment">#OAuth用クライアントIDの作成でダウンロードしたjsonファイル</span> credentials = ServiceAccountCredentials.from_json_keyfile_name(json_file, scopes=scopes) http_auth = credentials.authorize(Http()) <span class="synComment"># スプレッドシート用クライアントの準備</span> doc_id = <span class="synConstant">'doc_id'</span><span class="synComment">#これはスプレッドシートのURLのうちhttps://docs.google.com/spreadsheets/d/以下の部分です</span> gs = gspread.authorize(credentials) gfile = gs.open_by_key(doc_id) <span class="synComment">#読み書きするgoogle spreadsheet</span> <span class="synStatement">return</span> gfile </pre> <p>ユーザーごとにでシートを切り替えられるようにします。シート名はなんでも良いのですが、私はdiscordから自動でシートを切り替えられるようにdiscordのIDをシート名にしています。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">get_charactor</span>(sheet_name): gfile = get_gs() worksheet = gfile.worksheet(sheet_name) charactor = {} <span class="synComment">#技能名カラム</span> cell_keys = worksheet.col_values(<span class="synConstant">1</span>) <span class="synComment">#合計値カラム</span> cell_values = worksheet.col_values(<span class="synConstant">2</span>) <span class="synStatement">for</span> k,v <span class="synStatement">in</span> <span class="synIdentifier">zip</span>(cell_keys, cell_values): charactor[k] = v <span class="synStatement">return</span> charactor </pre> <p>ここまでで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>の設定は終了です。</p> <h2>Discord側の<a class="keyword" href="http://d.hatena.ne.jp/keyword/BOT">BOT</a>を作る</h2> <p>次に<a href="https://discordapp.com/">Discord</a>の設定をしていきます。</p> <h3><a class="keyword" href="http://d.hatena.ne.jp/keyword/Bot">Bot</a>作成</h3> <p>Discordの<a class="keyword" href="http://d.hatena.ne.jp/keyword/BOT">BOT</a>の作り方はこちらを参考にさせていただきました</p> <ul> <li><a href="https://blog.cover1sea.net/prg/2120/">DiscordでBOT作成メモ w/ discord.py</a></li> <li><a href="https://graffitinote.hatenadiary.jp/entry/2017/12/26/004657">PythonでDiscord botを作る 【discord.py解説】</a></li> <li><a href="https://qiita.com/penguin-knight/items/3f245bf0b52f2f25845f">DiscordのBotをPythonで作ってみた</a></li> <li><a href="https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f">Pythonで実用Discord bot(discord.py解説)</a></li> </ul> <h3>メッセージを取る</h3> <p>discordのメッセージ欄に特定の入力があったらダイスを振るようにします。入力形式は以下の仕様にします。ダイスを振るトリガーは<code>dice</code>にします。</p> <pre class="code" data-lang="" data-unlink>dice 1d100 技能名</pre> <p>これで受け取ってメッセージを返す待機<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>の処理を実装します。</p> <pre class="code lang-python" data-lang="python" data-unlink>client = discord.Client() client_id = conf[<span class="synConstant">'client_id'</span>] <span class="synPreProc">@</span><span class="synIdentifier">client.event</span> async <span class="synStatement">def</span> <span class="synIdentifier">on_ready</span>(): <span class="synIdentifier">print</span>(<span class="synConstant">'Logged in'</span>) <span class="synIdentifier">print</span>(<span class="synConstant">'-----'</span>) <span class="synPreProc">@</span><span class="synIdentifier">client.event</span> async <span class="synStatement">def</span> <span class="synIdentifier">on_message</span>(message): <span class="synComment"># 開始ワード</span> <span class="synStatement">if</span> message.content.startswith(<span class="synConstant">'dice'</span>): <span class="synComment"># 送り主がBotではないか</span> <span class="synStatement">if</span> client.user != message.author: info = parse(<span class="synConstant">'dice {}d{} {}'</span>, message.content) <span class="synStatement">if</span> info: <span class="synStatement">if</span> info[<span class="synConstant">1</span>].isdecimal() <span class="synStatement">and</span> info[<span class="synConstant">0</span>].isdecimal(): dice_num = <span class="synIdentifier">int</span>(info[<span class="synConstant">0</span>]) dice_size = <span class="synIdentifier">int</span>(info[<span class="synConstant">1</span>]) key = info[<span class="synConstant">2</span>] <span class="synComment"># メッセージを書きます</span> m = message.author.name + <span class="synConstant">' '</span> <span class="synStatement">if</span> key == <span class="synConstant">'一時的狂気'</span>: m = temp_madness() <span class="synStatement">elif</span> key == <span class="synConstant">'不定の狂気'</span>: m = ind_madness() <span class="synStatement">elif</span> key == <span class="synConstant">'dice'</span>: m = simple_dice(dice_size, dice_num) <span class="synStatement">else</span>: chara = get_charactor(<span class="synIdentifier">str</span>(message.author)) msg, result = judge(chara, key, dice_size, dice_num) m += msg <span class="synStatement">if</span> result: d = damage(chara, key) <span class="synStatement">else</span>: d = <span class="synIdentifier">None</span> <span class="synStatement">if</span> d <span class="synStatement">is</span> <span class="synStatement">not</span> <span class="synIdentifier">None</span>: m += <span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">ダメージ: '</span> + <span class="synIdentifier">str</span>(np.<span class="synIdentifier">sum</span>(d)) + <span class="synConstant">' = '</span> + <span class="synIdentifier">str</span>(d) <span class="synComment"># メッセージが送られてきたチャンネルへメッセージを送ります</span> await client.send_message(message.channel, m) client.run(client_id) </pre> <h3>ダイス</h3> <p>ダイス振る部分を実装します。単純に入力したダイスを振るものと成否判定をするものの2種類用意します。 まず、<code>1〜dice_size</code>までの一様整数乱数を1つ生成します。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">dice</span>(dice_size): num = np.random.randint(<span class="synConstant">1</span>, <span class="synIdentifier">int</span>(dice_size)) <span class="synStatement">return</span> num </pre> <p>単純にダイスを振る場合は次のようにしています。上記のダイスを<code>dice_num</code>回分振ります。<code>2d6</code>なら<code>1d6</code>のダイスを2回振っています。あとでメッセージ表示の際に個別のダイス結果も見たいのでダイス結果は<code>numpy.array</code>にしています。合計値は<code>np.sum</code>で計算します。<code>msg</code>はdiscordに返すメッセージです。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">simple_dice</span>(dice_size, dice_num): dice_val = np.array([], dtype=np.int64) <span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(dice_num): dice_val = np.append(dice_val, dice(dice_size)) msg = <span class="synConstant">'dice: '</span> + <span class="synIdentifier">str</span>(np.<span class="synIdentifier">sum</span>(dice_val)) + <span class="synConstant">' = '</span> + <span class="synIdentifier">str</span>(dice_val) <span class="synStatement">return</span> msg </pre> <p>discord上ではこんな感じになります。</p> <p><img src="https://qiita-image-store.s3.amazonaws.com/0/199063/c829e901-872a-6157-13a6-e6263ce8041e.jpeg" alt="daa0ec9db009ee3a418450bc5e781ead.jpg" /></p> <p>成否判定をするときも<code>simple_dice</code>をベースに実装します。</p> <h3>成否判定</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>から引っ張ってきた情報を参照して成否判定をさせます。 取得した<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>の情報は辞書型にして持たせておきます。</p> <pre class="code lang-python" data-lang="python" data-unlink>charactor = { <span class="synConstant">'HP'</span>: <span class="synConstant">9</span>, <span class="synConstant">'MP'</span>: <span class="synConstant">7</span>, <span class="synConstant">'SAN'</span>: <span class="synConstant">60</span>, <span class="synConstant">'こぶし'</span>: <span class="synConstant">50</span>, <span class="synConstant">'図書館'</span>: <span class="synConstant">50</span> } </pre> <p>と言っても、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>生成とかしているわけではないので、判定のたびに<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>からデータを参照しているので、たぶん非効率的です。</p> <p>処理の流れは</p> <ol> <li>入力メッセージからダイスのサイズと数を取得する</li> <li>ダイスを振ってダイス値を取得する</li> <li>技能値をダイス値を比較する</li> </ol> <p>といった感じです。ちなみに卓ルールで5以下でクリティカル、96以上で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%F3%A5%D6%A5%EB">ファンブル</a>にしています。<code>return</code>のbool値はダメージ判定時に使用します。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">judge</span>(charactor, key, dice_size, dice_num): dice_val = np.array([], dtype=np.int64) <span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(dice_num): dice_val = np.append(dice_val, dice(dice_size)) <span class="synStatement">if</span> <span class="synIdentifier">int</span>(charactor[key]) &gt;= np.<span class="synIdentifier">sum</span>(dice_val): msg = key + <span class="synConstant">' '</span> + <span class="synIdentifier">str</span>(charactor[key]) + <span class="synConstant">' &gt;= '</span> + <span class="synIdentifier">str</span>(np.<span class="synIdentifier">sum</span>(dice_val)) + <span class="synConstant">' = '</span> + <span class="synIdentifier">str</span>(dice_val) <span class="synStatement">if</span> np.<span class="synIdentifier">sum</span>(dice_val) &lt;= <span class="synConstant">5</span>: msg += <span class="synConstant">' 【クリティカル】'</span> msg += <span class="synConstant">' Success'</span> <span class="synStatement">return</span> msg, <span class="synIdentifier">True</span> <span class="synStatement">else</span>: msg = key + <span class="synConstant">' '</span> + <span class="synIdentifier">str</span>(charactor[key]) + <span class="synConstant">' &lt; '</span> + <span class="synIdentifier">str</span>(np.<span class="synIdentifier">sum</span>(dice_val)) + <span class="synConstant">' = '</span> + <span class="synIdentifier">str</span>(dice_val) <span class="synStatement">if</span> np.<span class="synIdentifier">sum</span>(dice_val) &gt;= <span class="synConstant">96</span>: msg += <span class="synConstant">' 【ファンブル】'</span> msg += <span class="synConstant">' Fail'</span> <span class="synStatement">return</span> msg, <span class="synIdentifier">False</span> </pre> <p>discord上ではこのように見えます。</p> <p><img src="https://qiita-image-store.s3.amazonaws.com/0/199063/eab7db76-3050-102f-74e4-3dc5f16ce8a0.jpeg" alt="0c4638e5b209ab36f55615788c200e2c.jpg" /></p> <h3>ダメージ判定</h3> <p>特定の<code>技能名</code>で技能判定が成功した際に自動でダメージロールを振るようにしました。トリガーとなる<code>技能名</code>はあらかじめダメージがわかっている<strong>こぶし</strong>,<strong>頭突き</strong>,<strong>キック</strong>だけに絞って実装します。マーシャルアーツは考慮していません。 ダメージボーナスがあればマイナスも含めて追加しています。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">damage</span>(charactor, key): d = np.array([], dtype=np.int64) <span class="synStatement">if</span> key == <span class="synConstant">'こぶし'</span>: d = np.append(d, dice(<span class="synConstant">3</span>)) <span class="synStatement">elif</span> key == <span class="synConstant">'頭突き'</span>: d = np.append(d, dice(<span class="synConstant">4</span>)) <span class="synStatement">elif</span> key == <span class="synConstant">'キック'</span>: d = np.append(d, dice(<span class="synConstant">6</span>)) <span class="synStatement">else</span>: <span class="synStatement">return</span> <span class="synIdentifier">None</span> <span class="synStatement">if</span> <span class="synConstant">'d'</span> <span class="synStatement">in</span> charactor[<span class="synConstant">'db'</span>]: result = parse(<span class="synConstant">'{}d{}'</span>, charactor[<span class="synConstant">'db'</span>]) dice_size = <span class="synIdentifier">int</span>(result[<span class="synConstant">1</span>]) dice_num = <span class="synIdentifier">int</span>(result[<span class="synConstant">0</span>]) <span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(np.<span class="synIdentifier">abs</span>(dice_num)): <span class="synStatement">if</span> dice_num &lt; <span class="synConstant">0</span>: d = np.append(d, -dice(dice_size)) <span class="synStatement">else</span>: d = np.append(d, dice(dice_size)) <span class="synStatement">return</span> d </pre> <ul> <li>成功<br/> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/8a146206-1058-9da6-b563-e209979c6dc4.jpeg" alt="0f66094ef3ff2cfbb2c6899fa6dbc468.jpg" /></li> <li>失敗<br/> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/d0e727fb-5758-04a3-a6d8-c4b854b1d0a0.jpeg" alt="f3519de16b5129f665654e4e0334d1b7.jpg" /></li> <li>ダメージボーナスあり<br/> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/5b40f42a-4aec-7bda-3d52-1ae01ea7ef52.jpeg" alt="5c9890fa428b2d87386954e0c80a6fa5.jpg" /></li> <li>ダメージボーナスあり(マイナス)<br/> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/c09d8e6a-30f9-89a4-4c88-a2c24f9b5e2e.jpeg" alt="720e61ae3edcb3ae1cb08a82a5eec24a.jpg" /></li> </ul> <h3>狂気表</h3> <p>discord上で次のように入力した場合に狂気票を振るようにします。</p> <pre class="code" data-lang="" data-unlink>dice 1d10 狂気の種類</pre> <p><code>狂気の種類</code>は<code>一時的発狂</code>と<code>不定の狂気</code>の2種類が振れます。<code>1d10</code>はフォーマットの統一の為につけていますが、実質使っていません。 一瞬、狂気表も<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>に書こうかと思いましたが、処理速度をあげる為にハードコーディングしてます<del>(正直、面倒臭かったので)</del>。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">temp_madness</span>(): roll = {} roll[<span class="synConstant">1</span>] = <span class="synConstant">'鸚鵡返し(誰かの動作・発言を真似することしか出来なくなる)'</span> <span class="synComment">#(中略)</span> roll[<span class="synConstant">20</span>] = <span class="synConstant">'過信(自分を全能と信じて、どんなことでもしてしまう)'</span> msg = roll[dice(<span class="synConstant">20</span>)] msg += <span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">一時的狂気('</span> + <span class="synIdentifier">str</span>(dice(<span class="synConstant">10</span>)+<span class="synConstant">4</span>) + <span class="synConstant">'ラウンドまたは'</span> + <span class="synIdentifier">str</span>(dice(<span class="synConstant">6</span>)*<span class="synConstant">10</span>+<span class="synConstant">30</span>) + <span class="synConstant">'分)'</span> <span class="synStatement">return</span> msg <span class="synStatement">def</span> <span class="synIdentifier">ind_madness</span>(): roll = {} roll[<span class="synConstant">1</span>] = <span class="synConstant">'失語症(言葉を使う技能が使えなくなる)'</span> <span class="synComment">#(中略)</span> roll[<span class="synConstant">10</span>] = <span class="synConstant">'殺人癖(誰彼構わず殺そうとする) '</span> msg = roll[dice(<span class="synConstant">10</span>)] msg += <span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">不定の狂気('</span> + <span class="synIdentifier">str</span>(dice(<span class="synConstant">10</span>)*<span class="synConstant">10</span>) + <span class="synConstant">'時間)'</span> <span class="synStatement">return</span> msg </pre> <ul> <li>一時的狂気<br/> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/0d36b1e1-45ed-2f15-de75-7ed2104b2dba.jpeg" alt="1c237209fa36504fbeda0ac9d5a32475.jpg" /></li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C9%D4%C4%EA">不定</a>の狂気<br/> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/7afd15d2-2c4f-f9f8-da67-1e71850ffe0f.jpeg" alt="eebbceb517ec43a602ee1a6e9de05c8c.jpg" /></li> </ul> <h2>Herokuにデプロイする</h2> <p>そのままではローカルで<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>を起動している時しか使えないので不便です。そこでサーバーを立てて常時使えるようにします。今回は<a href="https://jp.heroku.com/">Heroku</a>を使います。</p> <h3>Procfile</h3> <p>まずは、Heroku上で動かす実行<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を作成します。今回は<code>trpg_bot.py</code>を実行するだけなので</p> <pre class="code" data-lang="" data-unlink>woker: python trpg_bot.py</pre> <p>とします。</p> <h3>requirement.txt</h3> <p><code>requirement.txt</code>に<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>ファイル内で使っているライブラリを記入します。今回は以下のライブラリを使用しています。</p> <pre class="code" data-lang="" data-unlink>oauth2client httplib2 gspread discord parse numpy</pre> <h3>デプロイ</h3> <p>基本的にはHerokuのアカウントを作ってCreate New AppしてHeroku Gitにしたがって進めれば良いです。</p> <pre class="code" data-lang="" data-unlink># Herokuにログインする $ heroku login # リポジトリをクローンする $ heroku git:clone -a trpg_dice_bot $ cd trpg_dice_bot</pre> <p>cloneした<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>に作成したアプリケーションなどを格納します。</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/trpg">trpg</a>_<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>.py #メインアプリケーション</li> <li>config.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a> #設定ファイル</li> <li>requirement.txt # さっき作ったやつ</li> <li>Procfile #さっき作ったやつ</li> <li>oauth.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a> #<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%D7%A5%EC%A5%C3%A5%C9%A5%B7%A1%BC%A5%C8">スプレッドシート</a>を作った時にダウンロードしたoauthの<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>ファイルです</li> </ul> <pre class="code" data-lang="" data-unlink>$ git add . $ git commit -am &#34;make it better&#34; $ git push heroku master</pre> <p>無事デプロイに成功すると</p> <pre class="code" data-lang="" data-unlink>remote: Verifying deploy... done.</pre> <p>のようなメッセージが表示されます。 デプロイした時点ではまだdiscord上では<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>はオフラインのはずです。</p> <p><img src="https://qiita-image-store.s3.amazonaws.com/0/199063/651fb2a9-e6df-1a8f-25c9-ccf90845f1f2.jpeg" alt="コメント 2019-02-27 220947.jpg" /></p> <p>有効にするには、Resourcesからアプリを起動すればdiscord上で<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>がオンラインになるはずです。 ちなみにHerokuの無料枠は550時間です。</p> <p><img src="https://qiita-image-store.s3.amazonaws.com/0/199063/99980738-162d-8a83-c8f4-346511bc86bc.jpeg" alt="Heroku" /></p> <p>無事、オンラインになりました。</p> <p><img src="https://qiita-image-store.s3.amazonaws.com/0/199063/162bf664-5f13-ccab-cc51-5125566e5f19.jpeg" alt="コメント 2019-02-27 220928.jpg" /></p> <p>このあたりを参考にさせていただいています。</p> <ul> <li><a href="https://qiita.com/sgigagaeru/items/ec25932729eb7c23085e">いろいろワカラなくていいから取り急ぎPythonのWebアプリをherokuに置きたいときにやること</a></li> <li><a href="https://qiita.com/Yoshi_T/items/48fd4bee8a12d432de5b">DISCORDの千枝ちゃんBOTを24時間起動にしたみた</a></li> </ul> <p>これで快適な<a class="keyword" href="http://d.hatena.ne.jp/keyword/TRPG">TRPG</a>ライフが送れるはず。 少しでも誰かの参考になれば幸いです。</p> <h2>その他のリファレンス</h2> <ul> <li><a href="https://seesaawiki.jp/trpg_tool_guide/d/Discord%CD%D1%A5%C0%A5%A4%A5%B9%A5%DC%A5%C3%A5%C8">TRPGオンラインセッションツールガイド(仮)Discord用ダイスボット</a></li> </ul> T-N-Clark UnityEditorのバージョン管理をしたい hatenablog://entry/10257846132685843045 2018-12-17T11:30:00+09:00 2018-12-17T11:30:01+09:00 本記事は関東ゲーム制作部 Advent Calendar 2018 16日目の記事です。 adventar.org 経緯 昨日12/14にUnity 2018.3がリリースされましたね。prtimes.jp個人的には長らく切望していたNested Prefab機能が実装されたと聞いて早速使ってみたいと思いました。ですが、Unity使いの皆様ならよくあると思うのですが、「Unityのバージョンアップすると過去のが動かなくなるから上げたくないんだよなぁ...」ということがあるかと思います。デフォルトではUnityは1環境に1バージョンしか存在できないっぽい?ので、特に共同開発なんかしていると周りと… <p>本記事は関東ゲーム制作部 Advent Calendar 2018 16日目の記事です。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fadventar.org%2Fcalendars%2F3482" title="東ゲ部 Advent Calendar 2018 - Adventar" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://adventar.org/calendars/3482">adventar.org</a></cite></p> <h2>経緯</h2> <p>昨日12/14にUnity 2018.3がリリースされましたね。<iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fprtimes.jp%2Fmain%2Fhtml%2Frd%2Fp%2F000000082.000016287.html" title="Unity最新バージョン「Unity 2018.3」をリリース" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://prtimes.jp/main/html/rd/p/000000082.000016287.html">prtimes.jp</a></cite>個人的には長らく切望していた<strong>Nested Prefab</strong>機能が実装されたと聞いて早速使ってみたいと思いました。ですが、Unity使いの皆様ならよくあると思うのですが、「Unityのバージョンアップすると過去のが動かなくなるから上げたくないんだよなぁ...」ということがあるかと思います。デフォルトではUnityは1環境に1バージョンしか存在できないっぽい?ので、特に共同開発なんかしていると周りと足並みそろえて変更する必要があると思います。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>であれば、pyenvとかで複数バージョンを共存させられるけど、Unityでもそういったことできないのかな~と思っていたら2通りのやり方を見つけたので、誰かの役に立てばと思い、メモしておきます。</p> <h2>UnityHubを使う</h2> <p><img src="https://qiita-image-store.s3.amazonaws.com/0/199063/88ccab24-9020-5f1f-b027-2b84a6b0aa34.jpeg" alt="コメント 2018-12-16 002612.jpg" /><br/> 私はこの記事を書くにあたって知りましたが、こちらを使っている人は割といるのではないでしょうか?UnityHubの特徴はこちらの記事にわかりやすくまとめられています。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fk7a%2Fitems%2Fca4547725434580bc3a9" title="UnityHubを用いて複数バージョンのUnityEditorを管理する - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/k7a/items/ca4547725434580bc3a9">qiita.com</a></cite> 要点だけ抜粋すると</p> <ul> <li>複数のバージョンを管理できる</li> <li>Componentを追加インストールできる</li> <li>ver.2017以降であればUnityHub経由でインストールできる。それ以前もローカルにあれば、追加できる</li> <li>同一バージョンのUnityEditorを同時に起動できる</li> </ul> <p>あたりでしょうか。これを使えば、プロジェクトでは古いバージョンを使っているけど、自分の環境では新しいバージョンを試したいなんてときに便利だと思います。手っ取り早く使いたいならこっちを使うのがおすすめです。 <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/ddbb1611-7f22-c419-b42c-5e91a5c9aa2d.jpeg" alt="コメント 2018-12-16 011026.jpg" /></p> <h2>Unity on Docker</h2> <p>ここからが本命かつマイナーなお話になります。最近、バージョン管理や環境を汚さないようにDocker上でいろいろやろうという話を聞きます。かくいう私も<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>の環境とかはDocker上で作ったりしています。そこで、最近流行りのDockerを使ってUnityを動かせないかと思いました。結論から言うとおすすめしません。それでも、供養の意味も込めて検討記録ということで長くはなりますが、それでもいいよという心優しい方はお付き合いください。</p> <p>Docker自体のインストールも書いているといよいよ長くなりすぎるので、ここでは割愛します。私はWindows10なので、Docker for <a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>を使うためにこの辺りを参考にしました。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Ffkooo%2Fitems%2Fd2fddef9091b906675ca" title="WindowsでDocker環境を試してみる - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/fkooo/items/d2fddef9091b906675ca">qiita.com</a></cite> ついでに<a class="keyword" href="http://d.hatena.ne.jp/keyword/GUI">GUI</a>でコンテナ管理したいのでKitematicも入れています。</p> <p>Dockerのインストールには成功しました。いよいよ、Unityのコンテナを用意します。といっても私は<code>docker-compose up -d</code>程度しか使えないので、誰かいい感じの<code>Dockerfile</code>なり<code>docker-compose.yml</code>など提供してくれてないかなぁと検索したところ、ありました。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fnenadg%2Fdocker-unity3d" title="nenadg/docker-unity3d" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/nenadg/docker-unity3d">github.com</a></cite> 探せばいるもんですね。先人は偉大です。しかもReadMeを読んだところなにも考えずに<code>docker-compose up -d</code>をと唱えれば良いと書かれているもんだからありがたい限りです。ということで、早速<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8">コマンドプロンプト</a>を起動して、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>をcloneしてきて、<code>Dokerfile</code>がある<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リまで移動して、実行します。 ※ここからは回線状況にもよりますが、作業に<strong>2~3時間程度</strong>要します。また、ディスク容量に<strong>4GB程度の空き</strong>を作っておく必要があります。</p> <pre class="code" data-lang="" data-unlink>$docker-compose up -d</pre> <p>と叩くと、<code>Dockerfile</code>に従ってインストールが始まります。時間がかかりそうだったので、放置していたら、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BF%A5%A4%A5%E0%A5%A2%A5%A6%A5%C8">タイムアウト</a>の関係でいくつかのインストールに失敗していたのと、share directoryにのアクセスに失敗しました的なメッセージが出て止まっていました。同時にポップアップでshare directoryのアクセスを許可しますか?のようなものが出ていたので、OKを押して再インストール。うまくいかない場合は、一度インストールしたイメージ類をすべて消してから再度実行するとうまくいくと思います。</p> <pre class="code" data-lang="" data-unlink>$docker image list #ダウンロードしたイメージ一覧を表示 $docker rmi *** #***で指定したイメージを削除</pre> <p>当該のイメージが見つかりませんでしたのようなメッセージが出た場合はタグをつけて削除するとうまくいくと思います。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.memotansu.jp%2Fdocker%2F703%2F" title="docker rmiコマンドの使い方(実例付)CE対応 | めもたんす" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.memotansu.jp/docker/703/">www.memotansu.jp</a></cite></p> <p>これでコンテナのダウンロードまでは成功したようなのですが、コンテナの起動がうまくいかない。メッセージにリビルドしてねと出ていたので</p> <pre class="code" data-lang="" data-unlink>$docker-compose up --build</pre> <p>を実行。概ね上手くいったぽいけど、すると今度は、</p> <pre class="code" data-lang="" data-unlink>standard_init_linux.go:190: exec user process caused &#34;no such file or directory&#34;</pre> <p>Windows10を使っているのですが、どうやら<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>からcloneしてきたときに、改行コードが<code>CRLF</code>に変換されて落としてきています。ですが、コンテナ自体は<code>Ubuntu</code>だからか<code>LF</code>でないと動かないようです。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fnrk_baby%2Fitems%2Fd872e8f051a96a313601" title="docker-compose upするとコンテナが一瞬でexited with code 1する話 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/nrk_baby/items/d872e8f051a96a313601">qiita.com</a></cite> なので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C6%A5%AD%A5%B9%A5%C8%A5%A8%A5%C7%A5%A3%A5%BF">テキストエディタ</a>で改行コードを<code>LF</code>に変更して再度</p> <pre class="code" data-lang="" data-unlink>$docker-compose up --build</pre> <p>を実行するとようやくUnityのコンテナが起動!!ゴールは近いです。提供ページに書かれている通り、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VNC">VNC</a> Viewerで<a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>内に入ってくださいとのとこなので、お好きな<a class="keyword" href="http://d.hatena.ne.jp/keyword/VNC">VNC</a> Viewerを使ってコンテナ内に入ります。私は今回はUltra <a class="keyword" href="http://d.hatena.ne.jp/keyword/VNC">VNC</a>を使いました。<iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fforest.watch.impress.co.jp%2Flibrary%2Fsoftware%2Fultravnc%2F" title="窓の杜" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://forest.watch.impress.co.jp/library/software/ultravnc/">forest.watch.impress.co.jp</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.atmarkit.co.jp%2Fait%2Farticles%2F0709%2F21%2Fnews145.html" title="UltraVNCでWindows PCをリモート制御する(クライアント編)" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://www.atmarkit.co.jp/ait/articles/0709/21/news145.html">www.atmarkit.co.jp</a></cite>辺りを参考にしながら、インストールの設定は基本デフォルトで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>に登録とデスクトップにショートカットアイコンを作る的なものだけチェックを入れてインストール。さーて、<a class="keyword" href="http://d.hatena.ne.jp/keyword/IP%A5%A2%A5%C9%A5%EC%A5%B9">IPアドレス</a>を打っていざConnectと思ったのですが、またコケました。今度は何だと思ってKitematicのログを見ると、</p> <pre class="code" data-lang="" data-unlink>Creating unity-service ... done Attaching to unity-service unity-service | stored passwd in file: /home/adminuser/.vnc/passwd unity-service | 16/12/2018 07:00:26 -usepw: found /home/adminuser/.vnc/passwd unity-service | 16/12/2018 07:00:26 x11vnc version: 0.9.13 lastmod: 2011-08-10 pid: 8 unity-service | 16/12/2018 07:00:26 unity-service | 16/12/2018 07:00:26 wait_for_client: WAIT:cmd=FINDCREATEDISPLAY-Xvfb unity-service | 16/12/2018 07:00:26 unity-service | 16/12/2018 07:00:26 initialize_screen: fb_depth/fb_bpp/fb_Bpl 24/32/2560 unity-service | 16/12/2018 07:00:26 unity-service | 16/12/2018 07:00:26 Autoprobing TCP port unity-service | 16/12/2018 07:00:26 Autoprobing selected TCP port 5900 unity-service | 16/12/2018 07:00:26 Autoprobing TCP6 port unity-service | 16/12/2018 07:00:26 Autoprobing selected TCP6 port 5900 unity-service | 16/12/2018 07:00:26 listen6: bind: Address already in use unity-service | 16/12/2018 07:00:26 Not listening on IPv6 interface. unity-service | 16/12/2018 07:00:26 unity-service | unity-service | The VNC desktop is: 767f1c3f2d95:0 unity-service | PORT=5900 unity-service | 16/12/2018 07:05:11 Got connection from client 172.16.250.1 unity-service | 16/12/2018 07:05:11 other clients: unity-service | 16/12/2018 07:05:11 webSocketsHandshake: unknown connection error unity-service | 16/12/2018 07:05:11 Client 172.16.250.1 gone unity-service | 16/12/2018 07:05:11 Statistics events Transmit/ RawEquiv ( saved) unity-service | 16/12/2018 07:05:11 TOTALS : 0 | 0/ 0 ( 0.0%) unity-service | 16/12/2018 07:05:11 Statistics events Received/ RawEquiv ( saved) unity-service | 16/12/2018 07:05:11 TOTALS : 0 | 0/ 0 ( 0.0%) unity-service exited with code 137</pre> <p>的なメッセージが出ていました。どうやら<a class="keyword" href="http://d.hatena.ne.jp/keyword/VNC">VNC</a>アクセスに失敗していたのかよくわかりませんが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/VNC">VNC</a> Viewerを立ち上げる前にUnityのコンテナを立ち上げたのがいけなかったぽいので、一度コンテナを<code>down</code>させてから<a class="keyword" href="http://d.hatena.ne.jp/keyword/VNC">VNC</a> Viewerを立ち上げた後に、再度</p> <pre class="code" data-lang="" data-unlink>$docker-compose up --build</pre> <p>を実行すると、</p> <pre class="code" data-lang="" data-unlink>Creating unity-service ... done Attaching to unity-service unity-service | stored passwd in file: /home/adminuser/.vnc/passwd unity-service | 16/12/2018 07:24:51 -usepw: found /home/adminuser/.vnc/passwd unity-service | 16/12/2018 07:24:51 x11vnc version: 0.9.13 lastmod: 2011-08-10 pid: 8 unity-service | 16/12/2018 07:24:51 unity-service | 16/12/2018 07:24:51 wait_for_client: WAIT:cmd=FINDCREATEDISPLAY-Xvfb unity-service | 16/12/2018 07:24:51 unity-service | 16/12/2018 07:24:51 initialize_screen: fb_depth/fb_bpp/fb_Bpl 24/32/2560 unity-service | 16/12/2018 07:24:51 unity-service | 16/12/2018 07:24:51 Autoprobing TCP port unity-service | 16/12/2018 07:24:51 Autoprobing selected TCP port 5900 unity-service | 16/12/2018 07:24:51 Autoprobing TCP6 port unity-service | 16/12/2018 07:24:51 Autoprobing selected TCP6 port 5900 unity-service | 16/12/2018 07:24:51 listen6: bind: Address already in use unity-service | 16/12/2018 07:24:51 Not listening on IPv6 interface. unity-service | 16/12/2018 07:24:51 unity-service | unity-service | The VNC desktop is: 6afa1c440a08:0 unity-service | PORT=5900</pre> <p>となり、Connection Errorは解消されたっぽい。ということで、ローカルホストに<code>VNCServer: 127.0.0.1</code>でアクセスすると、パスワードを聞かれました。提供元のReadMeに書かれていた通り、パスワード<code>pass123</code>を入力すると、無事、コンテナ内に入れました。こんな感じのコンソール画面だけが表示された画面が見えるはずです。で、肝心の<code>Unity</code>はどこにあるのかというと、<code>/opt/Unity/Editor/Unity</code>にあるとのことですが、デフォルトの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リが<code>/opt/Unity/Editor</code>なので、その場で<code>Unity</code>を実行すれば起動できました。</p> <pre class="code" data-lang="" data-unlink>adminuser@6afa1c440a08:~$cd Unity</pre> <p>あとは画面に従って通常のUnityのインストールと同様に進めていきます。 <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/26a65a86-b365-6854-ae56-743ddc7dcc17.png" alt="image2.png" /> <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/0b3aa833-ce8b-b72a-5a69-7ea1e1717810.png" alt="image4.png" /> ログインもできて、さーて開発を始める準備が整ったことでしょう。新規に作るもよし、既存のプロジェクトを使うもよし。自分で作っていたプロジェクトを使いたい場合はCloudに上げておくか、ローカルであれば、cloneしてきた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>直下にあるcontextフォルダにおくように書かれていたのでお好みの箇所に置きましょう。コンテナ内からアクセスするときは、<code>File System-&gt;context-&gt;任意のフォルダ</code>に入っています。</p> <p>ところが、これで動くはず...あれ?一瞬エディター画面が表示されて<code>Aborted</code>とか言われるぞ...???なんかいろいろ調べるとバージョンによってはバグで動かないっぽいことを見かけたので、Unityのバージョンをアップすることにしました。 <a href="https://answers.unity.com/questions/1517429/installing-unity-201710f2-in-docker-container.html">Installing Unity 2017.1.0f2 In Docker Container</a> 使用したバージョンは<code>Dockerfile</code>に書かれていた当時のlatestである<a href="http://beta.unity3d.com/download/ee86734cf592/unity-editor_amd64-2017.2.0f3.deb">ver.2017.2.0f3</a>にしました。バージョンを変更したい場合は、お好みのバージョンのURLをコピーして<code>Dockerfile</code>の71行目あたりにある<code>.deb</code>ファイルを指定しているURLを変更すれば行けました。それ以外のバージョンを使いたい場合は、たぶん<a href="https://forum.unity.com/threads/unity-on-linux-release-notes-and-known-issues.350256/">こちらのスレッド</a>から対応したUnityのイメージを落としてくればいいんだと思います。スレッドのページを下に繰っていくと、古いのだと5.1から最新で2017.2.0b11まであるみたいです。どれが行けてどれが行けないまでは検証していないのでわかりません(^-^; バージョンアップして再度<code>dockder-compose up -d</code>からやり直すと無事NewProject作成で画面が表示された...? <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/5279001e-fe04-058f-f0de-328cc368324a.png" alt="image7.png" /> メモリが足りないのかこれ以上まともに描画されませんでした。こんな調子なので当然、プロジェクトが重すぎたのか、2dだからダメなのか自前のプロジェクトはうまく描画できませんでした。本当はこんな感じの画面を出したかったんですが... <img src="https://qiita-image-store.s3.amazonaws.com/0/199063/afdf0972-40b4-1b0b-0699-d83e3b79ae72.png" alt="image6.png" /></p> <p>画像は私の所属するサークル<a href="http://noelgame.html.xdomain.jp/">NOEL</a>で制作したゲームです。現在は第2作目のリリースに向けて鋭意制作中です。最新情報は随時<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>で流していきますので、よろしければフォローしてやってください。<a href="https://twitter.com/CircleNoelGame?lang=ja">@CircleNoelGame</a>(←単なる宣伝です。)</p> <p>これでめでたくDocker上でUnityが<strong>表示</strong>できるようになりました(まともに動くとは言っていない)。ものすっごく動作がもっさりしますが、とりあえず動いたということで、今回はここらへんで力尽きてしまいました。あとは割り当てメモリとか増やせばいいんですかね?でも、私の持つ低スペックPCでは3GB割り当てるのが精いっぱいなんですよね。年内に進展があれば、時間を見つけて続きを書きたいと思います。</p> <p>ちなみにUnrealEngineのdockerイメージも存在はするようです。頑張ればこっちも行けるんでしょうかね(;^_^A <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fconnorlanigan%2Funrealengine-docker" title="connorlanigan/unrealengine-docker" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/connorlanigan/unrealengine-docker">github.com</a></cite></p> <p>少しでも誰かの参考になれば幸いです。よければサークル<a href="http://noelgame.html.xdomain.jp/">NOEL</a>もよろしくお願いします。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>はこちら<a href="https://twitter.com/CircleNoelGame?lang=ja">@CircleNoelGame</a></p> <h2>その他のリファレンス</h2> <ul> <li><a href="https://hub.docker.com/r/gableroux/unity3d/">https://hub.docker.com/r/gableroux/unity3d/</a><cite class="hatena-citation"><a href="https://hub.docker.com/r/gableroux/unity3d/">hub.docker.com</a></cite></li> <li><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.robinryf.com%2Fblog%2F2017%2F09%2F30%2Frunning-unity-inside-docker.html" title="Run Unity Personal inside a Docker container" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.robinryf.com/blog/2017/09/30/running-unity-inside-docker.html">www.robinryf.com</a></cite></li> <li><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Frobinryf%2Fdocker-images" title="robinryf/docker-images" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/robinryf/docker-images">github.com</a></cite></li> <li><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdo-you-linux.com%2Fblog%2F2017%2F07%2F14%2Fmade-with-unity-%25E3%2582%25B2%25E3%2583%25BC%25E3%2583%25A0%25E3%2582%25A8%25E3%2583%25B3%25E3%2582%25B8%25E3%2583%25B3unity%25E3%2582%259230%25E5%2588%2586%25E3%2581%25A7linux%25E3%2581%25AB%25E3%2582%25A4%25E3%2583%25B3%25E3%2582%25B9%25E3%2583%2588%25E3%2583%25BC%25E3%2583%25AB%2F" title="Made with unity ゲームエンジンunityを30分でLinuxにインストール" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://do-you-linux.com/blog/2017/07/14/made-with-unity-%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3unity%E3%82%9230%E5%88%86%E3%81%A7linux%E3%81%AB%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB/">do-you-linux.com</a></cite></li> </ul> T-N-Clark 2018年私的読みたいアドベントカレンダー(随時更新) hatenablog://entry/10257846132664516902 2018-11-16T10:26:55+09:00 2018-12-20T08:11:31+09:00 qiita.com ゲーム開発 その3まである。やっぱり最近ホットなのでしょうか?その2でAgent-MLのことを書いてくださる方がいるようなので楽しみです。 qiita.com qiita.com qiita.com adventar.org ↑私も投稿しました t-n-clark.hatenadiary.jp お勉強 最近、まじめにお仕事でも使う必要が出てきたのでこの機に勉強でもし直そうかと思ってます。GAN関連の論文紹介もちらほら見かけるのでそのあたりを中心に見ておこうと思ってます。 qiita.com qiita.com qiita.com <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018" title="Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018">qiita.com</a></cite></p> <h2>ゲーム開発</h2> <p>その3まである。やっぱり最近ホットなのでしょうか?その2でAgent-MLのことを書いてくださる方がいるようなので楽しみです。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018%2Funity" title="Unity Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018/unity">qiita.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018%2Funity2" title="Unity #2 Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018/unity2">qiita.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018%2Funity3" title="Unity #3 Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018/unity3">qiita.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fadventar.org%2Fcalendars%2F3482" title="東ゲ部 Advent Calendar 2018 - Adventar" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://adventar.org/calendars/3482">adventar.org</a></cite> ↑私も投稿しました <iframe src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Ft-n-clark.hatenadiary.jp%2Fentry%2F2018%2F12%2F17%2F113000" title="UnityEditorのバージョン管理をしたい - もちっとメモ" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="http://t-n-clark.hatenadiary.jp/entry/2018/12/17/113000">t-n-clark.hatenadiary.jp</a></cite></p> <h2>お勉強</h2> <p>最近、まじめにお仕事でも使う必要が出てきたのでこの機に勉強でもし直そうかと思ってます。GAN関連の論文紹介もちらほら見かけるのでそのあたりを中心に見ておこうと思ってます。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018%2Faimath" title="機械学習の数理 Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018/aimath">qiita.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018%2Ficlr2019" title="ICLR2019をよむアドベントカレンダー Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018/iclr2019">qiita.com</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2018%2Fquantum" title="量子コンピュータ Advent Calendar 2018 - Qiita" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://qiita.com/advent-calendar/2018/quantum">qiita.com</a></cite></p> T-N-Clark 自作テストツールでもJenkinsで結果を可視化したい hatenablog://entry/10257846132615580563 2018-09-05T11:30:00+09:00 2018-09-05T11:30:05+09:00 経緯 C++で書いたコードのテストをしてテスト結果をJenkinsで可視化したい。だが、いかんせん低スペックな私にはその辺りの環境設定がわからん。周りからはとりあえず早くと急かされる。Jenkinsが読み込めるのはJUnitが出力する形式で書かれたXMLファイル。つまり、(わから)ないなら力業でその形式のXMLを書けばよかろうなのだ。 という頭の悪い発想に思い至ってしまったのが事の発端です。 JUnitの出力するXMLの構造を見てみる 今回は、こちらを参考にさせていただきました。 JUnitの実行結果のXMLフォーマット 私の稚拙な説明よりよっぽど明快に解説してくださっているので上のページを読… <h2>経緯</h2> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>で書いたコードのテストをしてテスト結果をJenkinsで可視化したい。だが、いかんせん低スペックな私にはその辺りの環境設定がわからん。周りからはとりあえず早くと急かされる。Jenkinsが読み込めるのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/JUnit">JUnit</a>が出力する形式で書かれた<a class="keyword" href="http://d.hatena.ne.jp/keyword/XML">XML</a>ファイル。つまり、(わから)ないなら力業でその形式の<a class="keyword" href="http://d.hatena.ne.jp/keyword/XML">XML</a>を書けばよかろうなのだ。 という頭の悪い発想に思い至ってしまったのが事の発端です。</p> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/JUnit">JUnit</a>の出力する<a class="keyword" href="http://d.hatena.ne.jp/keyword/XML">XML</a>の構造を見てみる</h2> <p>今回は、こちらを参考にさせていただきました。 <a href="https://dev.classmethod.jp/testing/unittesting/junit-xml-format/">JUnitの実行結果のXMLフォーマット</a></p> <p>私の稚拙な説明よりよっぽど明快に解説してくださっているので上のページを読んでいただければ十分かとは思いますが、概説すると</p> <ul> <li>testsuite <ul> <li>name:テストスイート名(テストクラス名,テストのグループ名)</li> <li>tests:testsuiteに含まれるテストの総件数</li> <li>failures:testsuiteに含まれる失敗(AssertionError)となったテストの総件数</li> <li>errors:testsuiteに含まれるエラー(AssertionError以外の例外)となったテストの総件数</li> <li>time:testsuiteに含まれるテストの総実行時間(秒)</li> </ul> </li> <li>testcase:testsuite要素の子要素 <ul> <li>classname:テストケースの属するクラス名</li> <li>name:テストケースの名前(半角数字で指定する?)</li> <li>time:テストケースの実行時間(秒)</li> </ul> </li> <li>failure:テストが失敗した場合に追加するtestcase要素の子要素 <ul> <li>type:失敗の種類(例外クラス名など)</li> <li>message:例外に含まれるメッセージ</li> </ul> </li> <li>error:テストが失敗でなくてエラーだった場合に利用。基本failure要素と同じ</li> <li>skipped:テストがスキップされた時に追加されるtestcase要素の子要素</li> <li>system-out:テスト実行時に標準出力に出力された情報をCDATAとして記述</li> <li>system-err:<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C9%B8%BD%E0%A5%A8%A5%E9%A1%BC%BD%D0%CE%CF">標準エラー出力</a>について記述する以外はsystem-out要素と同じ</li> </ul> <p>といった要素(<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AD%A1%BC%A5%DE">スキーマ</a>)を記述すればよさそうです。 恐らく日本語が混じっていてはダメです。</p> <h2>実装</h2> <p>ものすごく雑に組んでみます。実際は、テスト条件や期待値を外部ファイルから設定してテスト関数を動かすなど、もう少し工夫しています。 <code>MakeXMLResult()</code>が最終的に出力を行っているところです。</p> <pre class="code lang-cpp" data-lang="cpp" data-unlink><span class="synPreProc">#include </span><span class="synConstant">&lt;string&gt;</span> <span class="synPreProc">#include </span><span class="synConstant">&lt;vector&gt;</span> <span class="synPreProc">#include </span><span class="synConstant">&lt;fstream&gt;</span> <span class="synPreProc">#include </span><span class="synConstant">&lt;iostream&gt;</span> <span class="synStatement">using</span> <span class="synType">namespace</span> std; <span class="synPreProc">#define SUCCESS </span><span class="synConstant">0</span> <span class="synPreProc">#define FAILURE </span><span class="synConstant">1</span> <span class="synPreProc">#define EXCEPTION </span><span class="synConstant">2</span> <span class="synType">struct</span> SingleResult { <span class="synType">int</span> no; <span class="synType">double</span> time; <span class="synType">int</span> result; string name; string type; string message; }; <span class="synType">struct</span> TotalResult{ <span class="synType">int</span> test_tot_num; <span class="synType">int</span> ok_num; <span class="synType">int</span> error_num; <span class="synType">int</span> failure_num; <span class="synType">double</span> test_tot_time; }; <span class="synType">class</span> Test{ <span class="synStatement">private</span>: <span class="synStatement">public</span>: string OutputStdMessage(); string ErrorMessage(); <span class="synType">void</span> MakeXMLResult(string outputpath,vector&lt;SingleResult&gt; results,TotalResult tot_result); SingleResult DoTest(); TotalResult CalcTotalTest(vector&lt;SingleResult&gt; results); }; string Test::OutputStdMessage(){ <span class="synStatement">return</span> <span class="synConstant">&quot;std message&quot;</span>; } string Test::ErrorMessage(){ <span class="synStatement">if</span>(){<span class="synComment">//1件でもエラーか失敗があれば</span> <span class="synStatement">return</span> <span class="synConstant">&quot;Some Failure or Error Tests Caused&quot;</span>; } <span class="synStatement">else</span>{ <span class="synStatement">return</span> <span class="synConstant">&quot;All Test Success&quot;</span>; } } <span class="synType">void</span> Test::MakeXMLResult(string outputpath,vector&lt;SingleResult&gt; results,TotalResult tot_result){ <span class="synType">char</span> c_filepath[_MAX_PATH]; sprintf_s(c_filepath,<span class="synConstant">&quot;</span><span class="synSpecial">%s</span><span class="synConstant">&quot;</span>,outputpath.c_str()); ofstream output(c_filepath); output &lt;&lt; <span class="synConstant">&quot;&lt;?xml version=</span><span class="synSpecial">\&quot;</span><span class="synConstant">1.0</span><span class="synSpecial">\&quot;</span><span class="synConstant"> ?&gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;testsuite name=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.testname &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;tests=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.test_tot_num &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;errors=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.error_num &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;failures=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.failure_num &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;time=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.test_tot_time &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant">&gt;&quot;</span> &lt;&lt; endl; <span class="synStatement">for</span>(SingleResult result : results){ <span class="synStatement">if</span>(result.result == SUCCESS){ output &lt;&lt; <span class="synConstant">&quot;&lt;testcase classname=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.testname &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;name=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.name &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;time=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.time &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> /&gt;&quot;</span> &lt;&lt; endl; } <span class="synStatement">else</span> <span class="synStatement">if</span>(result.result == FAILURE){ output &lt;&lt; <span class="synConstant">&quot;&lt;testcase classname=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.testname &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;name=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.name &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;time=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.time &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;failure type=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.type &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;message=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.message &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;/failure&gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;/testcase&gt;&quot;</span> &lt;&lt; endl; } <span class="synStatement">else</span> <span class="synStatement">if</span>(result.result == EXCEPTION){ output &lt;&lt; <span class="synConstant">&quot;&lt;testcase classname=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; tot_result.testname &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;name=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.name &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;time=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.time &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;error type=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.type &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &quot;</span> &lt;&lt; <span class="synConstant">&quot;message=</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span> &lt;&lt; result.message &lt;&lt; <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant"> &gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;/error&gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;/testcase&gt;&quot;</span> &lt;&lt; endl; } } output &lt;&lt; <span class="synConstant">&quot;&lt;system-out&gt;&quot;</span> &lt;&lt; <span class="synConstant">&quot;&lt;![CDATA[&quot;</span> &lt;&lt; OutputStdMessage() &lt;&lt; <span class="synConstant">&quot;]]&gt;&quot;</span> &lt;&lt; <span class="synConstant">&quot;&lt;/system-out&gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;system-err&gt;&quot;</span> &lt;&lt; <span class="synConstant">&quot;&lt;![CDATA[&quot;</span> &lt;&lt; ErrorMessage() &lt;&lt; <span class="synConstant">&quot;]]&gt;&quot;</span> &lt;&lt; <span class="synConstant">&quot;&lt;/system-err&gt;&quot;</span> &lt;&lt; endl; output &lt;&lt; <span class="synConstant">&quot;&lt;/testsuite&gt;&quot;</span> &lt;&lt; endl; } SingleResult Test::DoTest(){ <span class="synComment">///テストコードを書く</span> <span class="synComment">///例えば結果を構造体に書き込んでいく</span> } TotalResult Test::CalcTotalTest(vector&lt;SingleResult&gt; results){ <span class="synComment">///全体の集計結果を出しておく</span> } <span class="synType">int</span> main(<span class="synType">int</span> argc, <span class="synType">char</span>* argv[]){ Test* test = <span class="synStatement">new</span> Test; string outputpath(argc&lt;=<span class="synConstant">1</span>? <span class="synConstant">&quot;.&quot;</span>: argv[<span class="synConstant">1</span>]); SingleResult result; vector&lt;SingleResult&gt; results; TotalResult tot_result; result = test-&gt;DoTest(); results.push_back(result); <span class="synComment">///きっとテストは二つ以上あるだろうから</span> tot_result = test-&gt;CalcTotalTest(results) test-&gt;MakeXMLResult(outputpath,results,tot_result); <span class="synStatement">delete</span> test; <span class="synStatement">return</span> <span class="synConstant">0</span>; } </pre> <p>テストコードも書いてやって、実際に出力された<a class="keyword" href="http://d.hatena.ne.jp/keyword/XML">XML</a>はこんな感じです。各々の数字に深い意味はありません。</p> <pre class="code lang-xml" data-lang="xml" data-unlink><span class="synComment">&lt;?</span><span class="synType">xml version</span>=<span class="synConstant">&quot;1.0&quot;</span><span class="synType"> </span><span class="synComment">?&gt;</span> <span class="synIdentifier">&lt;testsuite </span><span class="synType">name</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">tests</span>=<span class="synConstant">&quot;10&quot;</span><span class="synIdentifier"> </span><span class="synType">errors</span>=<span class="synConstant">&quot;2&quot;</span><span class="synIdentifier"> </span><span class="synType">failures</span>=<span class="synConstant">&quot;7&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;10&quot;</span><span class="synIdentifier">&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;101&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;error </span><span class="synType">type</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Error&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/error&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;201&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;2&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Failure&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;301&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;error </span><span class="synType">type</span>=<span class="synConstant">&quot;3&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Error&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/error&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;401&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;4&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;failure&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;501&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;5&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;failure&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;601&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;6&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Execute&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;701&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;7&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Execute&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;801&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;8&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Execute&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;802&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> /&gt;</span> <span class="synIdentifier">&lt;testcase </span><span class="synType">classname</span>=<span class="synConstant">&quot;Sample&quot;</span><span class="synIdentifier"> </span><span class="synType">name</span>=<span class="synConstant">&quot;910&quot;</span><span class="synIdentifier"> </span><span class="synType">time</span>=<span class="synConstant">&quot;1&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;failure </span><span class="synType">type</span>=<span class="synConstant">&quot;9&quot;</span><span class="synIdentifier"> </span><span class="synType">message</span>=<span class="synConstant">&quot;Failure&quot;</span><span class="synIdentifier"> &gt;</span> <span class="synIdentifier">&lt;/failure&gt;</span> <span class="synIdentifier">&lt;/testcase&gt;</span> <span class="synIdentifier">&lt;system-out&gt;</span><span class="synType">&lt;![</span><span class="synStatement">CDATA</span><span class="synType">[</span><span class="synConstant">std message</span><span class="synType">]]&gt;</span><span class="synIdentifier">&lt;/system-out&gt;</span> <span class="synIdentifier">&lt;system-err&gt;</span><span class="synType">&lt;![</span><span class="synStatement">CDATA</span><span class="synType">[</span><span class="synConstant">Some Failure or Error Tests Caused</span><span class="synType">]]&gt;</span><span class="synIdentifier">&lt;/system-err&gt;</span> <span class="synIdentifier">&lt;/testsuite&gt;</span> </pre> <p>これをJenkinsのビルド後の処理で「<a class="keyword" href="http://d.hatena.ne.jp/keyword/JUnit">JUnit</a>テスト結果の集計/テスト結果<a class="keyword" href="http://d.hatena.ne.jp/keyword/XML">XML</a>」で指定した<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リに出力されるようにしておけばJenkinsで見れるようになります。 注意点としては</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/xml">xml</a>までのパスは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EF%A1%BC%A5%AF%A5%B9%A5%DA%A1%BC%A5%B9">ワークスペース</a>からの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C1%EA%C2%D0%A5%D1%A5%B9">相対パス</a>で書く(私は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EF%A1%BC%A5%AF%A5%B9%A5%DA%A1%BC%A5%B9">ワークスペース</a>の直下に出力するかコピーされるようにしています)</li> <li>ジョブ終了直前のもののみが認識される(ファイルのタイムスタンプを見ているのか生成時刻が古いと「このファイル今のジョブのじゃないんじゃないの???」的なメッセージが出て認識されません)</li> </ul> T-N-Clark PythonからJenkinsのジョブを実行したい hatenablog://entry/10257846132614457435 2018-08-27T11:30:00+09:00 2018-08-27T11:30:03+09:00 経緯 最近ぼちぼち出張も増えてきて、出先で作業することも増えてきました。 出先ではノートPCを使って作業するのですが、手配したばかりで中は環境構築がなされていないすっからかんのPCです。 今後のためにがっつり環境を作ってもいいのですが、せっかく新品なのであまり環境を汚したくないですし、頑張ったところでまあスペックはたかが知れているようなものなので、なんとか普段使っているPCのリソースを使って重たい作業を回したいなあと考えていました。 そんな折、何をとち狂ったのかのか、「Jenkinsのジョブで実行ファイルを叩けば行けるんじゃない?ついでにFlaskでフロントを作ってWebアプリとして動かせるよ… <h1>経緯</h1> <p>最近ぼちぼち出張も増えてきて、出先で作業することも増えてきました。 出先ではノートPCを使って作業するのですが、手配したばかりで中は環境構築がなされていないすっからかんのPCです。 今後のためにがっつり環境を作ってもいいのですが、せっかく新品なのであまり環境を汚したくないですし、頑張ったところでまあスペックはたかが知れているようなものなので、なんとか普段使っているPCのリソースを使って重たい作業を回したいなあと考えていました。 そんな折、何をとち狂ったのかのか、「Jenkinsのジョブで実行ファイルを叩けば行けるんじゃない?ついでにFlaskでフロントを作ってWebアプリとして動かせるようにしてしまえ。」と思い立ってしまったのが事の始まりでした。 ということでなんか本来の使い方から外れているような気もしますが、タイトルの通り<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>(Flask)からJenkinsのジョブを実行する環境を作ったので備忘録的に残しておきます。</p> <h1>Jenkins環境</h1> <p>サー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A1%BC">バー</a>環境はすでに用意してあるものとします。 まず、ユーザー名と<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンをメモっておきます。 Jenkinsにログインしてユーザー名をクリックすると設定の中から確認できます。 次にリモートから実行を受け付けられるようにjobを用意します。 jobの種類は恐らくはなんでもいいのですが、今回は「フリースタイル・プロジェクトのビルド」で作ります。基本的には普通にジョブを作る時と一緒なので、リモートから実行するときに便利そうな設定だけ書いておきます。</p> <h2>ビルドのパラメーター化</h2> <p>ビルドの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を書くときにパラメーターを使いたい場合に設定します。POSTリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを投げた際にここで設定した名前をキーとしてパラメータを取得できます。</p> <h2>ビルド・トリガ</h2> <p>「リモートからビルド」にチェックを入れて認証<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを設定します。 恐らくなんでもいいので任意の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ン名を入力します。 ここでJenkinsの画面に、「リモートからビルドするには次のURLを使用します~」のようなメッセージが出ていると思うので、ここにリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを投げればリモートからジョブを実行できます。 ビルドのパラメーター化でパラメーターを設定した場合は、「buildWithParameters?token」の方を使用します。「TOKEN_NAME」の部分には認証<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンが入ります。 ビルド <a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>バッチコマンドであれば<code>%</code>で囲ってあげるとビルドのパラメータを取得できます。</p> <pre><code>SET PARAM = %param% APP.exe %PARAM% </code></pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>の場合は<code>$</code>でしたっけ?ここはJenkinsではなくバッチや<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%A7%A5%EB%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">シェルスクリプト</a>の書き方に沿って書いてください。 これでJenkins側の設定は終わりです。</p> <h2><a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a></h2> <p>次に<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>側の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を作ります。今回はFlask経由で実行するので若干冗長です。実行だけでよければ<code>job</code>の関数の部分さえあれば十分です。 <code>render_template</code>と<code>redirect</code>はWebアプリとして使いたいがためのお飾りです。 使う環境は3系を使います。</p> <pre><code>from flask import Flask ,request,redirect,render_template import urllib3 app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/job') def job(): param = request.args.get('param') url = 'JENKINS_URL/job/jobname/buildWithParameters?token=TOKEN_NAME&param='+param #&paramの部分はJenkinsのビルドのパラメーター化で設定したキーにしてください username = "username" #Jenskinsのユーザー名 password = "password" #JenkinsのAPIトークン http = urllib3.PoolManager() headers = urllib3.util.make_headers(basic_auth=('%s:%s' % (username,password))) response = http.request('POST', url, headers=headers) return redirect('http://localhost:8080/') if __name__ == '__main__': app.debug = True app.run(host='0.0.0.0',port=8080) </code></pre> <p>適当にhtmlも書きます。</p> <p data-height="265" data-theme-id="0" data-slug-hash="KxVvgQ" data-default-tab="html,result" data-user="sashimimochi" data-pen-title="KxVvgQ" class="codepen">See the Pen <a href="https://codepen.io/sashimimochi/pen/KxVvgQ/">KxVvgQ</a> by Sashimimochi (<a href="https://codepen.io/sashimimochi">@sashimimochi</a>) on <a href="https://codepen.io">CodePen</a>.</p> <script async src="https://static.codepen.io/assets/embed/ei.js"></script> <p>これで<code>localhost:8080</code>にアクセスして実行ボタンを押せばJenkinsが稼働しているサー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A1%BC">バー</a>のリソースを使って実行ファイルを動かせるようになりました。これで出先でもある程度作業ができるようになったかな。 セキュリティー等は考慮していないガバガバ構築なのでそのあたりは悪しからず。</p> T-N-Clark 認証proxy環境下でpythonからはてなブログに下書き投稿する hatenablog://entry/10257846132605039978 2018-08-06T11:30:00+09:00 2018-08-24T18:36:44+09:00 最近、プログラムはちょこちょこいじっているのですが、はてブのサイトを開いて記事を書くのをさぼっていたので投稿間隔が空いてしまいました。 ブラウザ開かなくても作業しながら手元のコードを手軽に投稿できれば少しはさぼりが減るかもと思い調べてました。 ということで、さっそく本題へ。 今回使用したコードはこちらを利用させていただきました。 github.com tadaken3.hatenablog.jp とある事情があってproxy配下から送りたかったのですが、このままでは送れないので微修正をば。 上記のコードにproxyの設定を足すだけなので変更含めて加えるのは5行です。 79行目の`request… <p>最近、プログラムはちょこちょこいじっているのですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>のサイトを開いて記事を書くのをさぼっていたので投稿間隔が空いてしまいました。 ブラウザ開かなくても作業しながら手元のコードを手軽に投稿できれば少しはさぼりが減るかもと思い調べてました。 ということで、さっそく本題へ。 今回使用したコードはこちらを利用させていただきました。 </p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="tadaken3/hatenablog_post" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Ftadaken3%2Fhatenablog_post" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/tadaken3/hatenablog_post">github.com</a></cite></p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="はてなブログにコマンド一発で投稿するPythonスクリプトを作ってみた - タダケンのEnjoy Tech" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftadaken3.hatenablog.jp%2Fentry%2Fpost-hatena-blog" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://tadaken3.hatenablog.jp/entry/post-hatena-blog">tadaken3.hatenablog.jp</a></cite></p> <p>とある事情があってproxy配下から送りたかったのですが、このままでは送れないので微修正をば。 上記のコードにproxyの設定を足すだけなので変更含めて加えるのは5行です。 79行目の`requests`の部分を</p> <pre><code> proxies = { 'http':'http://ユーザー名:パスワード@プロキシサーバーのアドレス:ポート番号/', 'https':'http://ユーザー名:パスワード@プロキシサーバーのアドレス:ポート番号/' } r = requests.post(url, data=data, headers=headers,proxies=proxies)</code></pre> <p>に変更するだけです。 これで無事送れるようになりました。今後はもう少し投稿頻度を上げられるといいな。</p> <p>ちょっと気を付けないと行けないのが、送る<a class="keyword" href="http://d.hatena.ne.jp/keyword/markdown">markdown</a>に&amp;とか&lt;とか&gt;とかのHTML<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C6%C3%BC%EC%CA%B8%BB%FA">特殊文字</a>が混じっていると400エラーを出して送信に失敗するので&amp;amp;などに直してから上げる必要がありそうですね。</p> T-N-Clark Unityで外部テキストを読んで表示するノベルが作りたい hatenablog://entry/10257846132607440142 2018-08-04T12:30:00+09:00 2018-08-04T12:30:03+09:00 手っ取り早くUnityでノベルゲームを作りたい時は宴とかを使えばいいんでしょうが、お試しで軽くノベルゲーム的なのを作って見たいだけなので今回は見送り。 madnesslabo.net じゃあデフォルトのAssetだけで作れんもんかと調べたらありました。やはり先人は偉大です。下記の記事にしたがって組んで見たら、おお!!すごい。テキストファイルに書いた内容がノベルゲームっぽく表示されてる!!しかも、ちゃんと@brでテキストが区切られていて//でコメントアウトされている。感動です。早速お借りさせていただきます。 tsubakit1.hateblo.jp 基本はこちらに書かれている通りに進めればできま… <p>手っ取り早くUnityでノベルゲームを作りたい時は宴とかを使えばいいんでしょうが、お試しで軽くノベルゲーム的なのを作って見たいだけなので今回は見送り。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Unity用ビジュアルノベルツール「宴」" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fmadnesslabo.net%2Futage%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://madnesslabo.net/utage/">madnesslabo.net</a></cite></p> <p>じゃあデフォルトのAssetだけで作れんもんかと調べたらありました。やはり先人は偉大です。下記の記事にしたがって組んで見たら、おお!!すごい。テキストファイルに書いた内容がノベルゲームっぽく表示されてる!!しかも、ちゃんと@brでテキストが区切られていて//で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%E1%A5%F3%A5%C8%A5%A2%A5%A6%A5%C8">コメントアウト</a>されている。感動です。早速お借りさせていただきます。</p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="uGUIでノベルゲームのようなものを作る、その2 コマンドによる挙動の管理 - テラシュールブログ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Ftsubakit1.hateblo.jp%2Fentry%2F2014%2F12%2F09%2F233502" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://tsubakit1.hateblo.jp/entry/2014/12/09/233502">tsubakit1.hateblo.jp</a></cite></p> <p>基本はこちらに書かれている通りに進めればできますが、個人的につまづいた事を備忘録的に記しておきます。</p> <ol> <li>Q.作った<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は何にアタッチすればいいの?<br />A.<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を動かしたいシーン上に適当に空のオブジェクトを作成して`ScenarioManager.cs`をアタッチします。すると自動的に`TextController.cs`も付随してアタッチされるはずです。あとはInspectorビューで`ScenarioManager.cs`のLoadFileNameにシナリオを書いたテキストのファイル名(拡張子は不要)を入力します。`TextController.cs`にはUiTextをアタッチします。これはその1と同じです。 <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="uGUIでノベルゲームのようなものを作る、その1 UIの表現と文字表現 - テラシュールブログ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Ftsubakit1.hateblo.jp%2Fentry%2F2014%2F12%2F06%2F233000" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://tsubakit1.hateblo.jp/entry/2014/12/06/233000">tsubakit1.hateblo.jp</a></cite></p> </li> <li>Q.`ScenarioManager.cs`のUpdate関数の`m_commandController.PreloadCommand (m_scenarios [m_currentLine])`あたりで`NullReferenceException`が出て動かないんだけど。(その2のコメントにもあったやつです)<br />A.commandControllerの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>が見つからない的な事を言っていると思われます。なので`commandController.cs`をシーン上のオブジェクトにアタッチして見たら動きました。アタッチするオブジェクトは何でもいいんだと思いますが私は先ほどの`ScenarioManager.cs`をアタッチしたのと同じオブジェクトにアタッチしました。</li> </ol> <p>せっかくなのでもう一工夫いじらせていただきます。我流<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CB%E2%B2%FE%C2%A4">魔改造</a>なので出来具合はご愛嬌という事で。</p> <h3>メッセージが表示されるとポポポポというSEを鳴らす</h3> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="ぼくがかんがえたさいきょうのAudioManager【Unity】 - (:3[kanのメモ帳]" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fkan-kikuchi.hatenablog.com%2Fentry%2FAudioManager" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://kan-kikuchi.hatenablog.com/entry/AudioManager">kan-kikuchi.hatenablog.com</a></cite></p> <p>オーディオ制御のコアの部分はこちらで紹介されているAudioManagerを利用させていただいてます。こちらのAudioManagerを使って`TextController.cs`のテキストが送られる部分をこんな感じに改造します。</p> <pre><code> public void SetNextLine(string text){ AudioManager.Instance.StopChargeSE ();//すでにポポポポがなっていたらの停止 AudioManager.Instance.PlayChargeSE ("serifSE");//新しくポポポポを鳴らす currentText = text; timeUntilDisplay = currentText.Length * intervalForCharactorDisplay; timeElapsed = Time.time; lastUpdateCharacter = -1; } </code></pre> <p>上で紹介されているAudioManagerではSEの途中停止はなかったのでStopBGMを参考にStopSEを自作しています。ほぼBGMをまるっとコピーして作っています。つまりStop可能なSEは一つだけですね。以下`AudioManager.cs`に足したStop可能SE部分を抜粋(名前がChargeSEになっているのは、はじめに作った時にチャージショット様に作ったためです。stopableSEとかにしておけばよかったですね)</p> <pre><code> //ChargeSEのフェードアウト時間 public const float CHARGE_SE_FADE_SPEED_RATE_MAX = 0.9f; public const float CHARGE_SE_FADE_SPEED_RATE_MIN = 0.3f; //BGM用とSE用それぞれのオーディオソース private AudioSource _chargeSESource; //全オーディオを保持 private SortedDictionary&lt;string, AudioClip&gt; _bgmDic, _seDic, _chargeSEDic; //作成したAudioSourceを取得し、変数にセットする。ボリュームもセットする for (int i = 0; i &lt; audioSourceArray.Length; i++) { audioSourceArray [i].playOnAwake = false; if (i == 0) { audioSourceArray [i].loop = true; _bgmSource = audioSourceArray [i]; _bgmSource.volume = PlayerPrefs.GetFloat (BGM_VOLUME_KEY, BGM_VOLUME_DEFAULT); } else if (i == 1) { _chargeSESource = audioSourceArray [i]; _chargeSESource.volume = PlayerPrefs.GetFloat (SE_VOLUME_KEY, SE_VOLUME_DEFAULT); } else { _seSourceList.Add (audioSourceArray [i]); audioSourceArray [i].volume = PlayerPrefs.GetFloat (SE_VOLUME_KEY, SE_VOLUME_DEFAULT); } } //リソースフォルダから全てのBGM,SEを読み込み、セットする _chargeSEDic = new SortedDictionary&lt;string, AudioClip&gt; (); object[] chargeSEList = Resources.LoadAll (SE_PATH); /* * ChargeSE * 指定したファイル名のChargeSEを流す * ただし、すでに流れている場合は前の曲をフェードアウトさせてから流す * 第二引数のfadeSpeedRateにフェードアウトスピードの割合を入れる */ public void PlayChargeSE(string chargeSEName,float fadeSpeedRate = CHARGE_SE_FADE_SPEED_RATE_MAX){ if (!_chargeSEDic.ContainsKey (chargeSEName)) { Debug.Log (chargeSEName + " can not be found"); return; } //現在、再生されていない場合はそのまま再生 if (!_chargeSESource.isPlaying) { _chargeSESource.clip = _chargeSEDic [chargeSEName] as AudioClip; _chargeSESource.Play (); } //違うChargeSEが流れているときは、流れているChargeSEをフェードアウトさせてから次を再生 //同じChargeSEが再生中はpass else if (_chargeSESource.clip.name != chargeSEName) { FadeOutChargeSE (fadeSpeedRate); } } /* * ChargeSEの停止 */ public void StopChargeSE(){ _chargeSESource.Stop (); } /* * 現在再生中のSEをフェードアウト * fadeSpeedrateでスピードを調整 */ public void FadeOutChargeSE(float fadeSpeedRate = CHARGE_SE_FADE_SPEED_RATE_MIN){ _isFadeOut = true; } /* * 音量調整 * BGMとSEのボリュームを別々に変更and保存 */ public void ChangeVolume(float BGMVolume,float SEVolume){ _chargeSESource.volume = SEVolume; } public void ChangeSEVolume(float SEVolume){ _chargeSESource.volume = SEVolume; } </code></pre> <p>これでメッセージと一緒にSEが流れる様になったのですが、テキストが表示され切った時点でSEを止めたいのでもう一手間加えます。テキストが表示され切ったかは`ScenarioManager.cs`の`if(m_textController.IsCompletaDisPlayText)`の部分で判定しているのでここに先ほどのStopSEを挟んでやります。</p> <pre><code> void Update(){ if (m_textController.IsCompleteDisplayText) { AudioManager.Instance.StopChargeSE (); if (m_currentLine &lt; m_scenarios.Length) { if (!m_isCallPreload) { (以下略) </code></pre> <p>これでテキストが表示され切った時点でSEが止まる様になりました。</p> <p>おまけでBGMも鳴らしちゃいましょう。BGMはシーンのスタート時に鳴らします。bgmはpublicで定義しておいて指定すればいいのですがたくさんシーンを作ると毎回打つのが面倒なので中に埋め込んじゃいます。その代わり辞書型で定義しておいてシーン名に応じて切り替えられる様にしました。</p> <pre><code> private Dictionary&lt;string,string&gt; bgmList = new Dictionary&lt;string, string&gt; () { { "Story1", "BGM1" }, { "Story2", "BGM2" }, { "Story3", "BGM3" } }; void PlayBGM(){ AudioManager.Instance.StopBGM (); AudioManager.Instance.PlayBGM (bgmList [SceneManager.GetActiveScene ().name]); } void Start(){ PlayBGM (); m_textController = GetComponent<textcontroller> (); m_commandController = GetComponent<commandcontroller> (); UpdateLines (LoadFileName); RequestNextLine (); } </commandcontroller></textcontroller></code></pre> <h3>シーンの切り替えエフェクトをつける</h3> <p>以前紹介したこちらを使ってやります。この記事の元ネタもテラシュールブログさんでしたね。いつもお世話になっています。</p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="【Unity】シーンの切り替えエフェクト - もちっとメモ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Ft-n-clark.hatenadiary.jp%2Fentry%2F2018%2F05%2F31%2F113049" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://t-n-clark.hatenadiary.jp/entry/2018/05/31/113049">t-n-clark.hatenadiary.jp</a></cite></p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="【Unity】Unityでトランジションを使用した綺麗な場面転換(uGUI対応版) - テラシュールブログ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Ftsubakit1.hateblo.jp%2Fentry%2F2015%2F11%2F04%2F015355" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://tsubakit1.hateblo.jp/entry/2015/11/04/015355">tsubakit1.hateblo.jp</a></cite></p> <p>以前と同じ様に`ScenarioManager.cs`に`Fadeout`用の関数を作ります。 遷移時のSEはInspectorViewから指定できる様にpublicで定義しておきます。</p> <pre><code> [SerializeField] Fade fade = null; public string changeSE; public void Fadeout(){ AudioManager.Instance.PlaySE (changeSE); fade.FadeIn (1, () =&gt; { Invoke ("ChangeNextScene", 0.5f); }); } public string nextScene;//次のシーンの名前 void ChangeNextScene(){ SceneManager.LoadScene (nextScene); } </code></pre> <p>そして、テキストファイルの中身が全て読み終わった時点で次のシーンに飛ばします。</p> <pre><code> void Update(){ if (m_textController.IsCompleteDisplayText) { AudioManager.Instance.StopChargeSE (); if (m_currentLine &lt; m_scenarios.Length) { if (!m_isCallPreload) { m_commandController.PreloadCommand (m_scenarios [m_currentLine]); m_isCallPreload = true; } if (Input.GetKeyDown (KeyCode.N)) { RequestNextLine (); } } else {//テキストの中身が全て読みこまれていて、かつNのキーが押された時 if (Input.GetKeyDown(KeyCode.N)) { Fadeout (); } } } else { if(Input.GetKeyDown(KeyCode.N)){ m_textController.ForceCompleteDisplayText (); } } } </code></pre> <p>これでテキストが全て読み終わった後にフェードアウトして次のシーンに遷移できる様になりました。 これでだいぶノベルゲームっぽくなりました。<a class="keyword" href="http://d.hatena.ne.jp/keyword/RPG">RPG</a>のシナリオシーンにも使えるんじゃないかな。</p> T-N-Clark C++でYAMLが読みたいんや...読めました hatenablog://entry/10257846132605272791 2018-07-30T11:30:00+09:00 2018-07-30T11:30:03+09:00 最近、C++のコードをちまちまいじっていたところ、設定ファイルとかの外部から渡すファイルって階層構造にしたほうが作りやすいし、読みやすいんだよな、これをそのままC++で読ませたいな...と思い立って調べました。 結論から言うと無事読めるようになったのですが、結構苦労したので備忘録を残しておきます。 今回はこちらのyaml-cppを使わせていただきました。提供方法はバイナリファイルではなくソースコードなので自分の環境でビルドしてバイナリ化してから使う必要があります。本当は最新版を使いたかったのですが、ビルドがうまくいかなかったのでver0.3.0を使いました。ver0.3とver0.6では使い方… <p>最近、<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>のコードをちまちまいじっていたところ、設定ファイルとかの外部から渡すファイルって階層構造にしたほうが作りやすいし、読みやすいんだよな、これをそのまま<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>で読ませたいな...と思い立って調べました。</p> <p>結論から言うと無事読めるようになったのですが、結構苦労したので備忘録を残しておきます。</p> <p>今回はこちらの<a class="keyword" href="http://d.hatena.ne.jp/keyword/yaml">yaml</a>-cppを使わせていただきました。提供方法はバイナリファイルではなく<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>なので自分の環境でビルドしてバイナリ化してから使う必要があります。本当は最新版を使いたかったのですが、ビルドがうまくいかなかったのでver0.3.0を使いました。ver0.3とver0.6では使い方が結構違うようなのでご注意を。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="jbeder/yaml-cpp" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fjbeder%2Fyaml-cpp" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/jbeder/yaml-cpp">github.com<br /></a></cite></p> <p>基本的なインストール方法は公式の通りです。日本語の解説記事はこちらが参考になります。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="C++ - yaml-cpp で YAML をパース! - mk-mode BLOG" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.mk-mode.com%2Foctopress%2F2014%2F10%2F14%2Fcpp-installation-yaml-cpp%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.mk-mode.com/octopress/2014/10/14/cpp-installation-yaml-cpp/">www.mk-mode.com</a></cite></p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title=" [cpp] yaml-cppをインストールした - はわわーっ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fyomi322.hateblo.jp%2Fentry%2F2012%2F04%2F17%2F235443" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://yomi322.hateblo.jp/entry/2012/04/17/235443">yomi322.hateblo.jp</a></cite></p> <p><a href="http://99blues.dyndns.org/blog/2010/12/parse_yaml_by_cpp/">C++ から YAMLファイルを読み込む « Stop Making Sense</a></p> <p> </p> <p>ついでに私が行った手順を大雑把に手順を書いておきます。</p> <ol> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>からファイルをダウンロードする。今回はver-0.3.0を使用する(ver-0.6.0はうまくビルドできなかった)</li> <li> <p><a href="https://cmake.org/">CMake</a>をダウンロードし、インストールする。今回はver-3.0.0-rc2を使用する。</p> </li> <li>ダウンロードしてきたcpp-<a class="keyword" href="http://d.hatena.ne.jp/keyword/yaml">yaml</a>の圧縮ファイルを解凍する</li> <li>解凍したフォルダ内にbuildという名前の空フォルダを作成する(この中にcamkeしたバイナリを保存します)</li> <li>cmakeでsourceにCMakeList.txtが入ったフォルダを指定してcmakeする。今回は<a class="keyword" href="http://d.hatena.ne.jp/keyword/GUI">GUI</a>でcmakeした。このとき、どの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%E9">コンパイラ</a>でcmakeするかによって32bitか64bitかが決まるので注意</li> <ol> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/GUI">GUI</a>を使った場合</li> <li>srcにcpp-<a class="keyword" href="http://d.hatena.ne.jp/keyword/yaml">yaml</a>のルートフォルダを出力先にbuildフォルダを指定してConfigureボタンをクリックする</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%E9">コンパイラ</a>として何を使うか聞かれるので選択する。今回はVisualStudio2012がインストールされている環境で64bit版で使用したいので<a class="keyword" href="http://d.hatena.ne.jp/keyword/Visual%20Studio">Visual Studio</a> 11 2012 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Win64">Win64</a>を選択</li> <li>無事完了し、Configuring doneが表示されればOKなので、次にGenerateボタンをクリックする</li> <li>無事Generating doneが表示されればOK</li> </ol> <li>buildフォルダ内にソリューションファイル.slnが生成されてるはずなのでVSでこれを開く</li> <li>プラットフォームがx64になっていることを確認し、ビルドモードをRelWithDebInfoに設定してビルドする。このときビルド対象にInstallを含めると適当な位置にライブラリを配備してくれるらしい。試していないので不明。</li> <li>無事ビルドが成功し、Installを指定しなかった場合は、RelWithDebInfoフォルダ内の.libファイルを取り出して<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>でスタティックライブラリを読めるように設定する。わからなければ以下を参考にする <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="VisualStudioで外部ライブラリを読み込めるようにする方法 | Corgi Lab. 〜備忘録のための技術ブログ〜" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fcorgi-lab.com%2Fwindows%2Fvs-external-library%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://corgi-lab.com/windows/vs-external-library/">corgi-lab.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="スタティックライブラリを作って読み込んでみる - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fizuki_y%2Fitems%2Fb6f35838c17bbab2d489" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/izuki_y/items/b6f35838c17bbab2d489">qiita.com</a></cite></p> </li> <li>include内の<a class="keyword" href="http://d.hatena.ne.jp/keyword/yaml">yaml</a>-cppフォルダをごそっと組み込みたいソフトで読める位置に設定する。より具体的にはVSのヘッダーファイルの設定をする</li> <li>あとは公式にそって書いていけばよい。</li> </ol> <p>これで下ごしらえは終わりです。早速実際に使ってみようと思うのですが、使い方にも癖があってymlファイルは特定の形式で書かれていないとうまく読んでくれないみたいです。まず、このような形式のymlファイルを用意します。内容は適当で意味はありませんです。 ファイル名も適当にinfo.ymlとでもしておきます。</p> <pre><code> - enemy: - name: enemy0 hp: 100 atk: 10 - name: enemy1 hp: 50 atk: 20 - name: enemy2 hp: 200 atk: 30 world: japan player: sashimimochi </code></pre> <p>次にymlの形式に合わせて<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>を実装します。 基本的には参考にさせていただいたページの通りですが、複数の要素を持つ場合にループで回す部分を改良しています。 インクルードはするとして、</p> <pre><code> #include "yaml-cpp\yaml.h" </code></pre> <p>まず、受け取る情報に合わせて構造体を実装します。ymlのデータが階層構造になっていれば階層分作成します。</p> <pre><code> struct Enemy{ string name; int hp; int atk; } struct Info{ vector&lt;enemy&gt; enemy; string world; string player; } </code></pre> <p>これを処理するオペレーターを用意します。オペレーターも作成した構造体分作成します。</p> <pre><code> void operator (const YAML::Node&amp; node, Enemy&amp; enemy){ node["name"] &gt;&gt; enemy.name; node["hp"] &gt;&gt; enemy.hp; node["atk"] &gt;&gt; enemy.atk; } void operator &gt;&gt; (const YAML::Node&amp; node, Info&amp; info){ const YAML::Node&amp; enemies = node["enemy"]; for(int i=0;i&lt;enemies.size();i++){ Enemy enemy; enemies[i] &gt;&gt; enemy; info.enemy.push_back(enemy); } node["world"] &gt;&gt; info.world; node["player"] &gt;&gt; info.player; } </code></pre> <p>そしてこれを読み込む関数を実装します。</p> <pre><code> void loadYMLFile(string ymlpath){ string name; int hp; int atk; string world; string player; try{ ifstream fin(ymlpath); YAML::Parser parser(fin); YAML::Node doc; parser.GetNextDocument(doc); Info info; for(int i=0;i&lt;doc.size();i++){ Info info; doc[i] &gt;&gt; info; for(int j=0;j&lt;info.enemy.size();j++){ name = info.enemy[j].name; hp = info.enemy[j].hp; atk = info.enemy[j].atk; cout &lt;&lt; "name:" &lt;&lt; name &lt;&lt; "\n" &lt;&lt; "HP:" &lt;&lt; hp &lt;&lt; "\n" &lt;&lt; "ATK:" &lt;&lt; atk &lt;&lt; endl; } world = info.world; player = info.player; cout &lt;&lt; "world:" &lt;&lt; world &lt;&lt; "\n" &lt;&lt; "player:" &lt;&lt; player &lt;&lt; endl; } }catch(YAML::ParserException&amp; e){ cerr &lt;&lt; e.what() &lt;&lt; endl; } } </code></pre> <p>無事ymlファイルに入力した情報がコンソールに出力されれば成功です。 まとめて書くとこうなる。 cpp-<a class="keyword" href="http://d.hatena.ne.jp/keyword/yaml">yaml</a>-reader.cpp</p> <pre><code> #include "yaml-cpp\yaml.h" #include &lt;string&gt; #include &lt;fstream&gt; struct Enemy{ string name; int hp; int atk; } struct Info{ vector&lt;enemy&gt; enemy; string world; string player; } void operator (const YAML::Node&amp; node, Enemy&amp; enemy){ node["name"] &gt;&gt; enemy.name; node["hp"] &gt;&gt; enemy.hp; node["atk"] &gt;&gt; enemy.atk; } void operator &gt;&gt; (const YAML::Node&amp; node, Info&amp; info){ const YAML::Node&amp; enemies = node["enemy"]; for(int i=0;i&lt;enemies.size();i++){ Enemy enemy; enemies[i] &gt;&gt; enemy; info.enemy.push_back(enemy); } node["world"] &gt;&gt; info.world; node["player"] &gt;&gt; info.player; } void loadYMLFile(string ymlpath){ string name; int hp; int atk; string world; string player; try{ ifstream fin(ymlpath); YAML::Parser parser(fin); YAML::Node doc; parser.GetNextDocument(doc); Info info; for(int i=0;i&lt;doc.size();i++){ Info info; doc[i] &gt;&gt; info; for(int j=0;j&lt;info.enemy.size();j++){ name = info.enemy[j].name; hp = info.enemy[j].hp; atk = info.enemy[j].atk; cout &lt;&lt; "name:" &lt;&lt; name &lt;&lt; "\n" &lt;&lt; "HP:" &lt;&lt; hp &lt;&lt; "\n" &lt;&lt; "ATK:" &lt;&lt; atk &lt;&lt; endl; } world = info.world; player = info.player; cout &lt;&lt; "world:" &lt;&lt; world &lt;&lt; "\n" &lt;&lt; "player:" &lt;&lt; player &lt;&lt; endl; } }catch(YAML::ParserException&amp; e){ cerr &lt;&lt; e.what() &lt;&lt; endl; } } int main(int argc, char* argv[]){ string ymlpath = argv[1]; loadYMLFile(ymlpath); return 0; } </code></pre> <p>これを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>して引数渡しで先ほどのymlファイルのパスを渡してあげると表示してくれる形にしてみました。</p> <pre><code>cpp-yaml-reader.exe info.yml</code></pre> <p>これでめでたく<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>でymlファイルが扱えるようになりました。設定情報を<a class="keyword" href="http://d.hatena.ne.jp/keyword/csv">csv</a>ファイルとかを使って頑張って階層構造を組まなくていいようになりました。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%B8%A5%C3%A5%AF%A5%CA%A5%F3%A5%D0%A1%BC">マジックナンバー</a>ではなくキーで指定できるのも地味にうれしいです。</p> T-N-Clark MattermostでTwitterの更新情報を自動で呟いてくれるbot的なのを作った hatenablog://entry/17391345971634029613 2018-06-15T11:30:00+09:00 2018-06-15T11:30:03+09:00 はじめに 最近、職場でOSSの環境を乱立しまくっているのですが、そのひとつとしてMattermostも立ち上げました。 釈迦に説法かとは思いますが、Mattermostは早い話が、オンプレミスで動くSlackです。うちもセキュリティが厳しいので、流行りのツールを使おうにもオンプレで動くものでないと使えなかったりします。 qiita.com で、立ち上げたのはいいのですが、早速言われたのが、「これ何に使うの?」。うちは独自のチャットツールがあるので、わざわざMattermostを使わなくてもいいじゃんという考えの人が多いようです。そこで、よくチェックする記事を自動で呟いてくれるbotを作れば需要… <h4>はじめに</h4> <p>最近、職場で<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>の環境を乱立しまくっているのですが、そのひとつとしてMattermostも立ち上げました。</p> <p>釈迦に説法かとは思いますが、Mattermostは早い話が、オンプレミスで動くSlackです。うちもセキュリティが厳しいので、流行りのツールを使おうにもオンプレで動くものでないと使えなかったりします。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="SlackクローンのMattermostを使ってみる - 導入、初期設定編- - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/terukizm/items/4a4016d8ec5a21856e4f" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/terukizm/items/4a4016d8ec5a21856e4f">qiita.com</a></cite></p> <p>で、立ち上げたのはいいのですが、早速言われたのが、「これ何に使うの?」。うちは独自のチャットツールがあるので、わざわざMattermostを使わなくてもいいじゃんという考えの人が多いようです。そこで、よくチェックする記事を自動で呟いてくれる<a class="keyword" href="http://d.hatena.ne.jp/keyword/bot">bot</a>を作れば需要があるんじゃなかろうかということで作りました。</p> <p>使っている環境はMattermostとJenkinsと<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>です。構成としてはこんな感じになっています。</p> <p><img class="hatena-fotolife" title="f:id:T-N-Clark:20180614000213p:plain" src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20180614/20180614000213.png" alt="f:id:T-N-Clark:20180614000213p:plain" width="498" /></p> <p>即興で作ったのでガバガバなのはご愛嬌ということで。Jenkinsはただのスケジューラーとして使っているので、こうして見返してみるとJenkinsである必要ないですね。まあ、Mattermostと繋げてビルドに失敗したときに通知が飛ばせるから(震え声)。あったので使った程度です。</p> <p>今回はこちらの<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>アカウントの更新情報を取得させていただくことにします。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B5%A1%B3%A3%B3%D8%BD%AC">機械学習</a>などの最新の論文情報をまとめてくださっているサイトです。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D6%A5%B9%A5%C8">アブスト</a>の日本語訳などもあるので、さっとチェックしたいときにとても参考になります。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="arXivTimes (@arxivtimes) | Twitter" src="https://hatenablog-parts.com/embed?url=https://twitter.com/arxivtimes" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://twitter.com/arxivtimes">twitter.com</a></cite></p> <h4>実装</h4> <p>では、早速実装していきます。まずは、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>の更新情報を取得します。色々方法はあると思うのですが、今回はこちらを利用しました。</p> <p><a href="http://twitrss.me/">TwitRSS.me - rss of twitter user feeds by screenscraping with perl</a></p> <p>次に、Mattermost側で情報を受け取るURLを用意しておきます。今回は情報を受け取るだけなので、内向きウェブフックを使います。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="SlackクローンのMattermostを使ってみる - 外部連携編 -(WebHooks、Hubot) - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/terukizm/items/4524249dd7f1298fdc06" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/terukizm/items/4524249dd7f1298fdc06">qiita.com</a></cite></p> <p>いよいよ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>を<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>で取得、データベースに保存、<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>形式に変換、Mattermostに送信部分を組んでいきます。最後に最終的に実装したコードを載せていますので、説明はいいという方は飛ばしてください。長いので処理内容毎に分けて説明します。といっても浅い理解なので間違っていたらすみません。コードもとりあえず、動くことだけしか考えてないので出来の悪い実装は適宜修正してください。ベースはこちらのサイトを参考にさせていただきました。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Python+pandasを使ってRSSフィードを取得→Mattermostに投稿&DBに保存 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/some-nyan/items/e35461129a480e0caede" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/some-nyan/items/e35461129a480e0caede">qiita.com</a></cite></p> <p>requestsで指定したURLから<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>の情報を取得します。requestライブラリについては、こちらをどうぞ。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Requests の使い方 (Python Library) - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/sqrtxx/items/49beaa3795925e7de666" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/sqrtxx/items/49beaa3795925e7de666">qiita.com</a></cite></p> <p>プロキシの設定が必要な場合はこちらが参考になります。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="PythonのWebスクレイピングでproxy経由でのhttpsアクセスがrequestsを使ったら簡単だった - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/koharite/items/731fcf5146c7b0c4e800" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/koharite/items/731fcf5146c7b0c4e800">qiita.com</a></cite></p> <p>無事、<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>の情報が取ってこれたら、feedparserでcontentから必要な情報を取ってきます。(これなんでfeedparserだけじゃだめなんだっけ?プロキシのせいだったかな。忘れてしまった。)この中のentryの中に欲しいデータがあるみたいなのでpandas形式に変換しておきます。そこまでできたらこちらを参考にTwitRSSでとってきた中から必要情報を抜き出します。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="【データサイエンスの基礎】pythonでRSSからデータ収集 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/shunsuke227ono/items/da52a290f78924c1f485" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/shunsuke227ono/items/da52a290f78924c1f485">qiita.com</a></cite></p> <p>今回必要なのは、idとtitleとlinkです。idはツイート毎のユニークな値です。これを使って既出か否かの判別をします。titleはツイートの内容にあたるのでこれをMattermostのメッセージにします。linkはそのツイートへのリンクです。今回はMattermostでメッセージをクリックすると<a class="keyword" href="http://d.hatena.ne.jp/keyword/Twitter">Twitter</a>にリンクするようにしておきます。</p> <p>取ってきた<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>の情報をデータベースに格納します。今回はそこら辺はサボって<a class="keyword" href="http://d.hatena.ne.jp/keyword/CSV">CSV</a>に保存しています。(ちなみにこの安易な考えで後に少々面倒なことになりました)</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Python+pandasを使ってRSSフィードを取得→Mattermostに投稿&DBに保存 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/some-nyan/items/e35461129a480e0caede" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/some-nyan/items/e35461129a480e0caede">qiita.com</a></cite></p> <p>全体通して書くとこうなります。</p> <pre><code> import requests,feedparser import pandas as pd import os import json def getNewFeed(rss_data,already_printed_feeds,filepath): feeds_info = [] feed = feedparser.parse(rss_data.content) entries = pd.DataFrame(feed.entries) if already_printed_feeds.empty: #全て新着Feed new_entries = entries else: #既出のFeedは除く new_entries = entries[~entries['id'].isin(already_printed_feeds['id'])] if not new_entries.empty: #新着Feedがあれば for key, row in new_entries.iterrows(): title = row['title'].split('http')[0] #Mattermostに投稿されるメッセージ.ここではmarkdown形式でリンクになるように書いている. feedinfo = '[**%s**](%s)' % (title, row['link']) feeds_info.append(feedinfo) #新着データがあれば既存のリストに追加する already_printed_feeds = already_printed_feeds.append(new_entries) #データベース(csv)に保存 if os.path.exists(filepath): new_entries.to_csv(filepath,encoding='utf-8',mode='a',header=False) else: new_entries.to_csv(filepath,encoding='utf-8') else: #新着Feedが無ければ print('not found new entries') return feeds_info #既出のfeed情報の取得 def getAlreadyPrintedFeeds(filepath): if os.path.exists(filepath): already_printed_feeds = pd.read_csv(filepath) else: already_printed_feeds = pd.Series() return already_printed_feeds def setPostMessage(feedinfo,username): payload = { 'text':feedinfo, 'username':username, } return payload #データベース(csv)へのパス filepath = 'entries.csv' #proxyの設定 proxies = { 'http':'http://id:passward@proxyadress:port', 'https':'http://id:passward@proxyadress:port' } #RSSFeed取得先のURL url = 'http://twitrss.me/twitter_user_to_rss/?user=arxivtimes' rss_data = requests.get(url,proxies=proxies) already_printed_feeds = getAlreadyPrintedFeeds(filepath) feedinfo = getNewFeed(rss_data,already_printed_feeds,filepath) #Mattermostの内向きウェブフック mattermosturl = 'http://localhost/hooks/***' #Mattermostのつぶやき時に表示される名前(好きな名前をつける) username = 'FeedBot' header = {'content-Type':'application/json'} #新着Feedを順にMattermostに投げる for i in range(len(feedinfo)): payload = setPostMessage(feedinfo[i]) resp = request.post(mattermosturl, header=header, data=json.dumps(payload)) </code></pre> <p>最後にこれを定期的に実行するためにJenkinsで実行スケジュールを組みます。と言ってもやることは単純で1時間に1回ジョブを実行させるだけです。確か今回採用している<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>の取得方法が1時間単位に更新された気がするのでこんなもんでいいでしょう。こちらを参考にさせていただきながら、スケジュールを設定していきます。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="[Jenkins] ビルドトリガ(定期的に実行)設定についてのまとめ - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/koara-local/items/79cb9c08e77ac9d94b1d" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/koara-local/items/79cb9c08e77ac9d94b1d">qiita.com</a></cite></p> <p>ということで最低限飛ばせるようになりました。</p> <p>ただ、ここで2つ問題が起きました。</p> <ol> <li>一つはこのままじゃ複数ページから取ってこれないのでもうちょっとだけいじります。といってもurlをリストにして順々に実行するだけです。なので大したことないです。</li> <li>もう一つは取ってきた<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSS">RSS</a>情報のcolumn数が揃ってないだと…どういう条件かまでは見てないのですが、ツイートによっては特定のcolumnのデータがあったりなかったりするみたいです。NaNになるならいいのですが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/CSV">CSV</a>への書き出し部分がいけないのかcolumnそのものが違って保存されてしまっているようです。そのせいで既出情報を読み出す際にcolumn数が合わないとエラーを食らいました。ダサいですが、`pandas`で読み込む際に`names`を指定して読み込むことにします。 <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="&lt;Python, pandas&gt; 列が一定でないデータを読み込む時、、 - ねこゆきのメモ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fnekoyukimmm.hatenablog.com%2Fentry%2F2016%2F06%2F17%2F115408" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://nekoyukimmm.hatenablog.com/entry/2016/06/17/115408">nekoyukimmm.hatenablog.com</a></cite></p> <p><a href="http://blog.mwsoft.jp/article/113600124.html">pandasでカラムサイズが一定でないcsv/tsvを読み込む : mwSoft blog</a></p> </li> </ol> <pre><code>import requests,feedparser import pandas as pd import os def getNewFeed(rss_data,already_printed_feeds,filepath): feeds_info = [] feed = feedparser.parse(rss_data.content) entries = pd.DataFrame(feed.entries) if already_printed_feeds.empty: #全て新着Feed new_entries = entries else: #既出のFeedは除く new_entries = entries[~entries['id'].isin(already_printed_feeds['id'])] if not new_entries.empty: #新着Feedがあれば for key, row in new_entries.iterrows(): title = row['title'].split('http')[0] #Mattermostに投稿されるメッセージ.ここではmarkdown形式でリンクになるように書いている. feedinfo = '[**%s**](%s)' % (title, row['link']) feeds_info.append(feedinfo) #新着データがあれば既存のリストに追加する already_printed_feeds = already_printed_feeds.append(new_entries) #データベース(csv)に保存 if os.path.exists(filepath): new_entries.to_csv(filepath,encoding='utf-8',mode='a',header=False) else: new_entries.to_csv(filepath,encoding='utf-8') else: #新着Feedが無ければ print('not found new entries') return feeds_info #既出のfeed情報の取得 def getAlreadyPrintedFeeds(filepath): if os.path.exists(filepath):   col_names = ['no', 'author', 'author_detail', 'authors', 'guidislink', 'id', 'link', 'links', 'published', 'published_parsed', 'summary', 'summary_detail', 'title', 'title_detail', 'twitter_place', 'twitter_source'] already_printed_feeds = pd.read_csv(filepath) else: already_printed_feeds = pd.Series() return already_printed_feeds import json def setPostMessage(feedinfo,username): payload = { 'text':feedinfo, 'username':username, } return payload def postForMattermost(feedinfo): #Mattermostの内向きウェブフック mattermosturl = 'http://localhost/hooks/***' #Mattermostのつぶやき時に表示される名前(好きな名前をつける) username = 'FeedBot' header = {'content-Type':'application/json'} #新着Feedを順にMattermostに投げる for i in range(len(feedinfo)): payload = setPostMessage(feedinfo[i]) resp = request.post(mattermosturl, header=header, data=json.dumps(payload)) def main(): #proxyの設定 proxies = { 'http':'http://id:passward@proxyadress:port', 'https':'http://id:passward@proxyadress:port' } #データベース(csv)へのパス filepath = 'entries.csv' already_printed_feeds = getAlreadyPrintedFeeds(filepath) #RSSFeed取得先のURL urls = ['http://twitrss.me/twitter_user_to_rss/?user=arxivtimes', 'http://twitrss.me/twitter_user_to_rss/?user=a_i_news', 'http://twitrss.me/twitter_user_to_rss/?user=ai_m_lab' ] for i in range(len(urls)): #RSS情報の取得 rss_data = requests.get(urls[i],proxies=proxies) #データベースへの登録 feedinfo = getNewFeed(rss_data,already_printed_feeds,filepath) #Mattermostへの送信 postForMattermost(feedinfo) if __name__ == "__main__": main() </code></pre> <p>多少整理もしたのでさっきよりは見やすくなったのではないでしょうか。Cの頃の癖なのか気づくと思考停止でインデックスでforループ回してますね。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%B3%A4%EC%A4%CF%A4%D2%A4%C9%A4%A4">これはひどい</a>。</p> <p>長いわりに稚拙な内容でしたが参考になれば幸いです。</p> <h4>後半で追加したサイト</h4> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="人工知能・機械学習ニュース(@A_I_News)さん | Twitter" src="https://hatenablog-parts.com/embed?url=https://twitter.com/A_I_News?lang=ja" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://twitter.com/A_I_News?lang=ja">twitter.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="人工知能,機械学習関係ニュース研究所(@AI_m_lab)さん | Twitter" src="https://hatenablog-parts.com/embed?url=https://twitter.com/AI_m_lab?lang=ja" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://twitter.com/AI_m_lab?lang=ja">twitter.com</a></cite></p> T-N-Clark 【Unity】シーンの切り替えエフェクト hatenablog://entry/17391345971648563767 2018-05-31T11:30:49+09:00 2018-07-14T15:21:11+09:00 【2018/7/14追記】作成したスクリプトにFadeCanvasをアタッチする部分が抜けていたので追記しました。 RPGでマップ画面から戦闘画面に切り替わるときとかにあるあれです。既にたくさんの方が書かれているので今さらですが、個人的にはまったので、備忘録として書いておきます。 手っ取り早くやりたいなら github.com bibinbaleo.hatenablog.com 複雑なエフェクトをかけたいなら tsubakit1.hateblo.jp 使い方の例:ボタンクリックでエフェクトをかけながら次のシーンに移る シーンを2つ用意する ルール画像を取得する4you.bz 取得したルール画像… <p>【2018/7/14<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C4%C9%B5%AD">追記</a>】作成した<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>にFadeCanvasをアタッチする部分が抜けていたので<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C4%C9%B5%AD">追記</a>しました。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/RPG">RPG</a>でマップ画面から戦闘画面に切り替わるときとかにあるあれです。既にたくさんの方が書かれているので今さらですが、個人的にはまったので、備忘録として書いておきます。</p> <p>手っ取り早くやりたいなら</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="naichilab/Unity-FadeManager" src="https://hatenablog-parts.com/embed?url=https://github.com/naichilab/Unity-FadeManager/blob/master/README.ja.md" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/naichilab/Unity-FadeManager/blob/master/README.ja.md">github.com</a></cite></p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="Unityでのシーン切り替え演出について学ぶ - トマシープが学ぶ" src="https://hatenablog-parts.com/embed?url=http://bibinbaleo.hatenablog.com/entry/2017/10/08/161733" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://bibinbaleo.hatenablog.com/entry/2017/10/08/161733">bibinbaleo.hatenablog.com</a></cite></p> <p>複雑なエフェクトをかけたいなら</p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="【Unity】Unityでトランジションを使用した綺麗な場面転換(uGUI対応版) - テラシュールブログ" src="https://hatenablog-parts.com/embed?url=http://tsubakit1.hateblo.jp/entry/2015/11/04/015355" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://tsubakit1.hateblo.jp/entry/2015/11/04/015355">tsubakit1.hateblo.jp</a></cite></p> <p>使い方の例:ボタンクリックでエフェクトをかけながら次のシーンに移る</p> <ol> <li>シーンを2つ用意する</li> <li> <p>ルール画像を取得する<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="ルール画像 | For You" src="https://hatenablog-parts.com/embed?url=http://4you.bz/rule" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://4you.bz/rule">4you.bz</a></cite></p> </li> <li>取得したルール画像をUnityのProjectにインポートする。フォルダー構成は任意。</li> <li>ルール画像のInspectorを変更する。TextureType=Default,TextureShape=2D,sRGB=on,AlphaSource=FromGrayScale,AlphaIsTransparency=on<br />にしてApplyを押す。</li> <li>シーン切り替えエフェクト用のパッケージを取得する。 <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="tsubaki/FadeCamera2" src="https://hatenablog-parts.com/embed?url=https://github.com/tsubaki/FadeCamera2" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/tsubaki/FadeCamera2">github.com</a></cite></p> </li> <li>ダウンロードしたパッケージを[Assets]-&gt;[Import Package]-&gt;[Custom Package]からインポートする。インポート対象はallで良い。</li> <li>シーン移動する前のシーンにインポートしたパッケージフォルダ[Fade]-&gt;[FadeCanvas]のプレハブをシーンに置く</li> <li>FadeCanvasにアタッチされている[FadeImage]でエフェクトの様子を調整できる。Materialを変更することで変化の様子を調整する。[UI-Fade-Cutout]にすると円を描くようなエフェクトになる。MaskTextureを変更するとエフェクトの模様を変更できる。ここに3.でインポートしたルール画像をアタッチする。</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Canvas">Canvas</a>下に[UI]-&gt;[Button]を置く。</li> <li>ButtonにFade用の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>をアタッチする。今回は以下のような<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を自作して与えた。 <pre><code>using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class FadePractice : MonoBehaviour { [SerializeField] Fade fade = null; public void Fadeout() { //fade.FadeIn (1, () =&gt; { // fade.FadeOut (1); //}); fade.FadeIn (1, () =&gt; { Invoke("LoadScene",0.5f); }); } public void LoadScene(){ SceneManager.LoadScene ("NextScene"); } }</code></pre> </li> <li>ButtonのOnClickに先ほどButtonにアタッチした[FadePractice]をアタッチする。この時[FadePractice]はInspector上のものをアタッチすること。アタッチできたら、[Runtime Only],関数名:[FadePractice.Fadeout]を選択する。</li> <li>FadePracticeにシーン上のFadeCanvasをアタッチする</li> <li>シーンをSaveしてゲームを実行する。ボタンをクリックして、フェードアウトエフェクトがかかりながら次のステージに遷移できたら完成。</li> </ol> <p>あとはお好みのエフェクトに変更したり、ステージの遷移になるように調整してください。</p> <p>FadeCanvasのSortingLayerをいじることで描画順序も調整できるようです。Materialによっても見え方がことなるので適宜変更してください。デフォルトだと[UI-Fade-Alpha]になっているので下にobjectがあると透過して下のobject見えるっぽいです。[UI-Fade-Cutout]に変更したら透過しなくなりました。</p> T-N-Clark 【GitBucket】社内用HPページを作って情報共有してみる hatenablog://entry/17391345971637104652 2018-04-24T10:30:00+09:00 2018-04-24T22:58:49+09:00 以前、Hugoを使って簡単ホームページの作り方をご紹介したかと思います。 t-n-clark.hatenadiary.jp しかし、作ったのはいいのですが、あんまり外に見せたくない、社内への情報共有程度に使えればいいんだけどな、オンプレミスで使えるGitHub Pases的なのがあればなあと思っていたら便利なものがありました。ちなみにGitHub Pasesとはこんな感じのものです。 www.tam-tam.co.jp GitHubだと社内の情報セキュリティ的に...という場合に使えるオンプレミスで動くGitBucketというものがあるのですが、これのプラグインでGitHub Pages的なも… <p> 以前、Hugoを使って簡単ホームページの作り方をご紹介したかと思います。</p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="Markdownでホームページが書きたくて - もちっとメモ" src="https://hatenablog-parts.com/embed?url=http://t-n-clark.hatenadiary.jp/entry/2017/12/06/113000" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://t-n-clark.hatenadiary.jp/entry/2017/12/06/113000">t-n-clark.hatenadiary.jp</a></cite></p> <p>しかし、作ったのはいいのですが、あんまり外に見せたくない、社内への情報共有程度に使えればいいんだけどな、オンプレミスで使える<a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a> Pases的なのがあればなあと思っていたら便利なものがありました。ちなみに<a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a> Pasesとはこんな感じのものです。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="GitHub Pages を使った静的サイトの公開方法が、とても簡単になっていた | Tips Note by TAM" src="https://hatenablog-parts.com/embed?url=https://www.tam-tam.co.jp/tipsnote/html_css/post11245.html" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.tam-tam.co.jp/tipsnote/html_css/post11245.html">www.tam-tam.co.jp</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a>だと社内の情報セキュリティ的に...という場合に使えるオンプレミスで動くGitBucketというものがあるのですが、これの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>で<a class="keyword" href="http://d.hatena.ne.jp/keyword/GitHub">GitHub</a> Pages的なものがあったのでこれを使うことにしました。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="GitBucket - A Git platform" src="https://hatenablog-parts.com/embed?url=https://gitbucket.github.io/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://gitbucket.github.io/">gitbucket.github.io</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="gitbucket/gitbucket-pages-plugin" src="https://hatenablog-parts.com/embed?url=https://github.com/gitbucket/gitbucket-pages-plugin" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/gitbucket/gitbucket-pages-plugin">github.com</a></cite></p> <p>こちらで紹介されている通り、特定の名前のブランチに上げるとホームページとして見ることができます。<br /><a href="https://www.indetail.co.jp/blog/gitbucket-plugin/">GitBucketを補助するプラグイン4選 | 株式会社INDETAIL - インディテール</a></p> <p>Hugoを使う場合はpublicフォルダをGitBucketに上げます。うん、これで無事作ったホームページが公開できました。満足満足。</p> <p>年度が変わったので新しいメンバーに自分のプロジェクトの紹介とかに使ってはいかがでしょうか。</p> T-N-Clark 【Unity】画像をグルクル回転させるアニメーションを作りたい hatenablog://entry/17391345971635517261 2018-04-23T11:30:00+09:00 2018-04-23T11:30:04+09:00 最近行っているゲーム製作の中で、幾何学模様を組み合わせてサイバーっぽいパーツを作りました。で、友人の「これって回ったら格好よさそうじゃない?」という無責任な発言でこれを回すことになりました。 今回はこんな感じのアニメーションを作ります。 いろいろやりようはあると思いますが、色んなUIパーツに対して使えるよう今回はスクリプトで回すことにします。今回はこちらを参考に組みました(というか、ほぼそのままです)。 tsubakit1.hateblo.jp docs.unity3d.com 実際のコードはこちら。自作部分はえいやで組んだので適当です。 using System.Collections; u… <p>最近行っているゲーム製作の中で、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B4%F6%B2%BF%B3%D8">幾何学</a>模様を組み合わせてサイバーっぽいパーツを作りました。で、友人の「これって回ったら格好よさそうじゃない?」という無責任な発言でこれを回すことになりました。</p> <p>今回はこんな感じのアニメーションを作ります。</p> <p><img class="hatena-fotolife" title="f:id:T-N-Clark:20180421151712g:plain" src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20180421/20180421151712.gif" alt="f:id:T-N-Clark:20180421151712g:plain" width="226" /></p> <p>いろいろやりようはあると思いますが、色んなUIパーツに対して使えるよう今回は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>で回すことにします。今回はこちらを参考に組みました(というか、ほぼそのままです)。</p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="【Unity】Animatorで動かすオブジェクトをスクリプトでも動かす際の注意 - テラシュールブログ" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Ftsubakit1.hateblo.jp%2Fentry%2F2016%2F08%2F08%2F235900" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://tsubakit1.hateblo.jp/entry/2016/08/08/235900">tsubakit1.hateblo.jp</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Quaternion.AngleAxis - Unity スクリプトリファレンス" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdocs.unity3d.com%2Fja%2Fcurrent%2FScriptReference%2FQuaternion.AngleAxis.html" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://docs.unity3d.com/ja/current/ScriptReference/Quaternion.AngleAxis.html">docs.unity3d.com</a></cite></p> <p>実際のコードはこちら。自作部分<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%A8">はえ</a>いやで組んだので適当です。</p> <pre><code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class RotAmimation : MonoBehaviour { public float angle = 1; public bool rot = true; void LateUpdate(){ if (rot) { transform.rotation *= Quaternion.AngleAxis (angle, Vector3.back); } else { transform.rotation *= Quaternion.AngleAxis (angle, Vector3.forward); } } } </code></pre> <p>公式リファレンスにある通りにangleで回転角をで回転方向を決めています。つまり、angleで回転速度(角速度)を調整します。回転方向については、Vector3とあることから分かるように3次元に回転できて、それぞれ</p> <ul> <li>up:(0,1,0)</li> <li>down:(0,-1,0)</li> <li>back:(0,0,-1)</li> <li>forward:(0,0,1)</li> <li>left:(-1,0,0)</li> <li>right:(1,0,0)</li> </ul> <p>のようになっています。今回は2次元平面上での回転なのでz方向の回転、すなわち</p> <ul> <li>back</li> <li>forward</li> </ul> <p>を使います。とりあえず回転方向を2種類を切り替えられればいいので、時計回りならtrue、反時計回りならfalseのboolで切り替えています。あとは、これを回転させたい画像にアタッチして、インスペクターでパラメータを調整すれば完成です。</p> <p><img class="hatena-fotolife" title="f:id:T-N-Clark:20180421153331p:plain" src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20180421/20180421153331.png" alt="f:id:T-N-Clark:20180421153331p:plain" /></p> <p>ちなみに、私の場合は描画順序がうまくいかなかったのでSortingLayerで調整しました。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="[Unity] LayerとSortingLayerの違い - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Flycoris102%2Fitems%2Fb620654192af4f695fb4" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/lycoris102/items/b620654192af4f695fb4">qiita.com</a></cite></p> T-N-Clark JupyterLabで開発しながら.pyファイルを作りたい hatenablog://entry/17391345971633877839 2018-04-16T11:30:00+09:00 2018-04-16T11:30:01+09:00 最近、JupyterLabのβ版が公開されて、ますますJupyterでの開発が捗っている方も多いのではないでしょうか? github.com 私もそんな一人なのですが、複数人でソースを共有したいときやflaskで作ったapiを叩くとき、Jenkinsからキックするときはやっぱり.py形式の方が使いやすい。ということで、.ipynbを保存すると同時に.pyファイルも保存してくれるやり方を探したら、ありましたのでメモっておきます。今回はこちらを参考にさせていただきました。 adtech.cyberagent.io まず、ターミナルなどのコンソールを起動して、configを作成します。 jupyte… <p>最近、JupyterLabのβ版が公開されて、ますますJupyterでの開発が捗っている方も多いのではないでしょうか?</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="jupyterlab/jupyterlab" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fjupyterlab%2Fjupyterlab" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/jupyterlab/jupyterlab">github.com</a></cite></p> <p>私もそんな一人なのですが、複数人でソースを共有したいときやflaskで作った<a class="keyword" href="http://d.hatena.ne.jp/keyword/api">api</a>を叩くとき、Jenkinsからキックするときはやっぱり.py形式の方が使いやすい。ということで、.ipynbを保存すると同時に.pyファイルも保存してくれるやり方を探したら、ありましたのでメモっておきます。今回はこちらを参考にさせていただきました。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Jupyter Notebook の Tips をまとめてみた" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fadtech.cyberagent.io%2Ftechblog%2Farchives%2F2317" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://adtech.cyberagent.io/techblog/archives/2317">adtech.cyberagent.io</a></cite></p> <p>まず、ターミナルなどのコンソールを起動して、configを作成します。</p> <pre><code>jupyter notebook —generate-config</code></pre> <p>生成時に生成されたパスが表示されるので、そのファイルを開いて中身を書き換えます。</p> <pre><code>#c.FileContentsManager.post_save_hook = None</code></pre> <p>これを</p> <pre><code>import io import os from notebook.utils import to_api_path _script_exporter = None def script_post_save(model, os_path, contents_manager, **kwargs): """convert notebooks to Python script after save with nbconvert replaces `ipython notebook --script` """ from nbconvert.exporters.script import ScriptExporter if model['type'] != 'notebook': return global _script_exporter if _script_exporter is None: _script_exporter = ScriptExporter(parent=contents_manager) log = contents_manager.log base, ext = os.path.splitext(os_path) py_fname = base + '.py' script, resources = _script_exporter.from_filename(os_path) script_fname = base + resources.get('output_extension', '.txt') log.info("Saving script /%s", to_api_path(script_fname, contents_manager.root_dir)) with io.open(script_fname, 'w', encoding='utf-8') as f: f.write(script) c.FileContentsManager.post_save_hook = script_post_save </code></pre> <p>のように書き換えます。</p> <p>これで後は実際にJupyterLabを起動して、ファイル保存をするだけで.ipynbファイルと.pyファイルが同時に生成されているはずです。</p> <p>ついでに、「個別の環境にJupyterをインストールするのは面倒だよ」という場合は、オンプレ<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>のようにサーバー用PCにみんなでアクセスして使うという方法があります。これもブラウザで稼働する点のメリットですね。これでユーザーはブラウザさえあれば即使えますし、バージョンやライブラリの管理も1台のPCで済みます。リモートで使うための方法はこちらが参考になります。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="リモートサーバでjupyter notebookを起動させローカル環境で使う - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fsyo_cream%2Fitems%2F05553b41277523a131fd" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/syo_cream/items/05553b41277523a131fd">qiita.com</a></cite></p> T-N-Clark Texファイルが大きくなったので分けようと思う hatenablog://entry/8599973812321718719 2018-02-06T11:30:45+09:00 2018-02-06T11:30:45+09:00 論文や講義ノートなど、長編の書き物をしていると段々TeXファイルが大きくなってきます。私の場合は図を多用していたので特にそうでした。大きくなってくると当然、ビルドにも時間がかかります。しかも私は、タイプミスをしょっちゅうするので、結構な頻度でビルドしてはプレビューで確かめてを繰り返さないといけない始末。なんとかならないのかと唸っていたら友人から「章ごとに別々のTeXファイルに分ければいいじゃん」と言われたので、調べてみたらありました。qiita.com 上記のサイト様にも書いてありますが、基本的には章ごとにTeXファイルを1作って、まとめるときは、まとめる用のTeXファイルを作ってその中に各章… <p>論文や<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B9%D6%B5%C1%A5%CE%A1%BC%A5%C8">講義ノート</a>など、長編の書き物をしていると段々<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルが大きくなってきます。私の場合は図を多用していたので特にそうでした。大きくなってくると当然、ビルドにも時間がかかります。しかも私は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BF%A5%A4%A5%D7%A5%DF%A5%B9">タイプミス</a>をしょっちゅうするので、結構な頻度でビルドしてはプレビューで確かめてを繰り返さないといけない始末。なんとかならないのかと唸っていたら友人から「章ごとに別々の<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルに分ければいいじゃん」と言われたので、調べてみたらありました。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="効率的な LaTeX ファイル分割術 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/wtsnjp/items/6ba3b8e12514d8a3bd41" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/wtsnjp/items/6ba3b8e12514d8a3bd41">qiita.com</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%BE%E5%B5%AD">上記</a>のサイト様にも書いてありますが、基本的には章ごとに<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルを1作って、まとめるときは、まとめる用の<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルを作ってその中に各章の<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルの内容を埋め込むイメージ。(そういえば最近、ホームページ作りでも似たようなことやったなあ)<br />一つ気を付けないといけないのは、まとめる際に「<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルの内容をそのまま埋め込む」ことになるので各章の<a class="keyword" href="http://d.hatena.ne.jp/keyword/TeX">TeX</a>ファイルにはプリアンブル(usepackageとか書いてある¥begin{document}より上の部分です)が書けないということです。これで何が困るって各ファイル毎にビルドできないので、結局書いた内容を確かめようと思うと全ファイルまとめてビルドしないといけません。これではあまり解決になってません。<br />と思っていたらあるじゃないですか、各ファイル毎にビルドできる方法が。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="効率的な LaTeX ファイル分割術、のための docmute パッケージ - マクロツイーター" src="https://hatenablog-parts.com/embed?url=http://d.hatena.ne.jp/zrbabbler/20150906/1441509908" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://d.hatena.ne.jp/zrbabbler/20150906/1441509908">d.hatena.ne.jp</a></cite></p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="【卒論・修論】 分割Texファイルを単体でもコンパイルできるようにする(docmuteパッケージ) - 粗大メモ置き場" src="https://hatenablog-parts.com/embed?url=http://ossyaritoori.hatenablog.com/entry/2016/12/22/【卒論・修論】_分割Texファイルを単体でもコンパ" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://ossyaritoori.hatenablog.com/entry/2016/12/22/【卒論・修論】_分割Texファイルを単体でもコンパ">ossyaritoori.hatenablog.com</a></cite></p> <p>どうやらこのパッケージを使うとプリアンブル部分を無視してまとめてくれるみたいです。なので、まとめファイルと同じプリアンブルを各ファイルに書いておけば章ごとにビルドできる。これで大分楽になりました。ありがとうございます。</p> <p>ちなみにinputとincludeの違いはこちらの通りだそうです。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title=" input, includeコマンド: 複数の.texファイルを一つのファイルでまとめてコンパイル · Issue #1 · uribo/LaTeX_tips" src="https://hatenablog-parts.com/embed?url=https://github.com/uribo/LaTeX_tips/issues/1" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://github.com/uribo/LaTeX_tips/issues/1">github.com</a></cite></p> T-N-Clark アクセス解析を復習してみる hatenablog://entry/8599973812340032637 2018-01-23T11:30:00+09:00 2018-01-24T07:34:43+09:00 とある事情でまた新たにサイトを作ることになったので、早速アクセス解析を導入しました。前にもやったことあるのでし余裕でしょ...と思ったのですが、前にやったのは1年以上前だったのですっかり忘れてしまっていました。ということで導入手順を復習したので備忘録的にそのメモ書きとして残しておきます。今回も先人の方々の知恵をお借りしました。 今回導入したのがGoogleアナリティクスとGoogle Search Consoleです。www.google.co.jp www.google.com はてブにはデフォルトでアクセス解析がついているのですが、無料アカウントだと1ヶ月しか見られません。当然はてブでもで… <p>とある事情でまた新たにサイトを作ることになったので、早速<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%AF%A5%BB%A5%B9%B2%F2%C0%CF">アクセス解析</a>を導入しました。前にもやったことあるのでし余裕でしょ...と思ったのですが、前にやったのは1年以上前だったのですっかり忘れてしまっていました。ということで導入手順を復習したので備忘録的にそのメモ書きとして残しておきます。今回も先人の方々の知恵をお借りしました。</p> <p>今回導入したのが<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>アナリティクスと<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Search Consoleです。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Google アナリティクス - モバイル、プレミアム、無料のウェブサイト解析 – Google" src="https://hatenablog-parts.com/embed?url=http://www.google.co.jp/intl/ja/analytics/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://www.google.co.jp/intl/ja/analytics/">www.google.co.jp</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Google Search Console" src="https://hatenablog-parts.com/embed?url=https://www.google.com/webmasters/tools/home?hl=ja" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.google.com/webmasters/tools/home?hl=ja">www.google.com</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>にはデフォルトで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%AF%A5%BB%A5%B9%B2%F2%C0%CF">アクセス解析</a>がついているのですが、無料アカウントだと1ヶ月しか見られません。当然<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>でもできますし、私も導入しましたが、今回は自作のホームページの方に導入したのでそちら視点で書いていきます。</p> <p>自前のホームページを作るときはXdomainさんを利用させていただいております。簡単なアカウント登録だけで無料で3GBを借りられてHTMLなら広告なし、広告ありなら<a class="keyword" href="http://d.hatena.ne.jp/keyword/PHP">PHP</a>,<a class="keyword" href="http://d.hatena.ne.jp/keyword/WordPress">WordPress</a>も使えるというお得な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EC%A5%F3%A5%BF%A5%EB%A5%B5%A1%BC%A5%D0%A1%BC">レンタルサーバー</a>です。そしてなんと、1個(2個だったかもしれない)<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C6%C8%BC%AB%A5%C9%A5%E1%A5%A4%A5%F3">独自ドメイン</a>を取得できるというのが魅力的すぎます。いいのかこんなことできて。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="ドメイン取得&無料レンタルサーバー Xdomain(エックスドメイン)" src="https://hatenablog-parts.com/embed?url=https://www.xdomain.ne.jp/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://www.xdomain.ne.jp/">www.xdomain.ne.jp</a></cite></p> <p>興味を持った方は以下のサイトに導入手順が丁寧に説明されています。ちなみにファイルのアップロードは[<a class="keyword" href="http://d.hatena.ne.jp/keyword/FTP">FTP</a>アカウント設定]-[WebFTP]-[ログイン]からファイルマネージャーが開けるのでWeb上からファイルのアップロードができます。<a class="keyword" href="http://d.hatena.ne.jp/keyword/FTP%A5%AF%A5%E9%A5%A4%A5%A2%A5%F3%A5%C8">FTPクライアント</a>に比べると少々UIが使いづらいですが、特別<a class="keyword" href="http://d.hatena.ne.jp/keyword/FTP%A5%AF%A5%E9%A5%A4%A5%A2%A5%F3%A5%C8">FTPクライアント</a>を用意する必要はないので少数のファイルを扱うだけなら十分便利です。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="xdomain無料サーバー" src="https://hatenablog-parts.com/embed?url=http://rensrv.com/free/xdomain/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://rensrv.com/free/xdomain/">rensrv.com</a></cite></p> <p>色々脱線しましたが、以下のサイトを参考に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>アナリティクスの設定をしていきます。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Googleアナリティクス導入時の設定・設置方法【初心者向け】|アクセス解析ツール「人工知能AIアナリスト」ブログ" src="https://hatenablog-parts.com/embed?url=https://wacul-ai.com/blog/access-analysis/google-analytics-setting/ga-beginner/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://wacul-ai.com/blog/access-analysis/google-analytics-setting/ga-beginner/">wacul-ai.com</a></cite></p> <p>私の場合は、hugoで作っているので導入はとても簡単でした。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="hugo で Google analyticsの設定 - はなたんのブログ" src="https://hatenablog-parts.com/embed?url=http://www.tdtsh.com/archives/4002/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://www.tdtsh.com/archives/4002/">www.tdtsh.com</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A5%D6">はてブ</a>につけたい方はこちらが参考になります。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="はてなブログにGoogleアナリティクスを設定する手順【2017最新版】" src="https://hatenablog-parts.com/embed?url=http://memobiz.net/how-to-setting-analytics" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://memobiz.net/how-to-setting-analytics">memobiz.net</a></cite></p> <p>続いて<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Search Consoleです。基本は以下のサイトに従って行うだけ。<iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="【2017年12月 完全版】はてなブログにGoogle Search Consoleを導入する方法 - Next Innovation" src="https://hatenablog-parts.com/embed?url=http://www.yuki-t.site/entry/hatena-Google-Search-Console" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://www.yuki-t.site/entry/hatena-Google-Search-Console">www.yuki-t.site</a></cite></p> <p>Xdomainを利用されている方は、URL登録時に表示されたhtmlファイルをダウンロードし、rootフォルダにアップロードするだけ。特別ファイルに書き込む内容はありません。</p> <p>仕上げに<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>アナリティクスと<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Search Consoleを連携させてしまいましょう。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="【5分で完了】サーチコンソールとGoogleアナリティクスを連携する方法|アクセス解析ツール「人工知能AIアナリスト」ブログ" src="https://hatenablog-parts.com/embed?url=https://wacul-ai.com/blog/access-analysis/google-analytics-setting/googleanalytics-searchconsole/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://wacul-ai.com/blog/access-analysis/google-analytics-setting/googleanalytics-searchconsole/">wacul-ai.com</a></cite></p> <p>応用編ですが、ついでに<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>アナリティクスを使えばファイルのダウンロード数のカウントなんかもできるそうです。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="できた!ファイルダウンロード数の計測" src="https://hatenablog-parts.com/embed?url=https://big-mac.jp/recommend/marketing-theory/did-it-measuring-the-number-of-file-downloads/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://big-mac.jp/recommend/marketing-theory/did-it-measuring-the-number-of-file-downloads/">big-mac.jp</a></cite></p> <p>現状、大してアクセス数があるわけではありませんが、一通りやりたいことはできたのでここまでとします。色々設置するとweb解析屋さんになった気分になれます。</p> <p>よければこちらの過去記事もどうぞ。Hugoでのホームページ作成について書いたものです。 </p> <p><iframe class="embed-card embed-blogcard" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" title="Markdownでホームページが書きたくて - もちっとメモ" src="https://hatenablog-parts.com/embed?url=http://t-n-clark.hatenadiary.jp/entry/2017/12/06/113000" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://t-n-clark.hatenadiary.jp/entry/2017/12/06/113000">t-n-clark.hatenadiary.jp</a></cite></p> <p> </p> T-N-Clark PowerPointもGit管理できないかな... Pythonで書けばいいじゃない hatenablog://entry/8599973812338465381 2018-01-18T11:30:00+09:00 2018-01-22T23:51:50+09:00 あけましておめでとうございます。2018年初投稿です。 タイトル通り、PowerPointをテキスト形式で管理する方法を模索していたら、こんなの見つけました。Pythonでパワポが作れるライブラリがあるらしい。最近の機械学習、データ解析屋さんにはありがたいです。いつも思うが先人の方々はありがたいまとめをしてくださっています。 qiita.com インストール環境がオンラインでできるなら。こちらの通り。qiita.com Proxyとかなんらかしらの理由でオフラインでやりたい場合は、こちら。pypi.python.org gzファイルをダウンロードしてきて pip install python-… <p>あけましておめでとうございます。2018年初投稿です。</p> <p>タイトル通り、<a class="keyword" href="http://d.hatena.ne.jp/keyword/PowerPoint">PowerPoint</a>をテキスト形式で管理する方法を模索していたら、こんなの見つけました。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D1%A5%EF%A5%DD">パワポ</a>が作れるライブラリがあるらしい。最近の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B5%A1%B3%A3%B3%D8%BD%AC">機械学習</a>、データ解析屋さんにはありがたいです。いつも思うが先人の方々はありがたいまとめをしてくださっています。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Pythonでパワポの説明資料(報告書)を生成する - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/code_440/items/22e8539da465686496d3?utm_source=Qiitaニュース&amp;utm_campaign=93ec9743c9-Qiita_newsletter_293_01_10_2018&amp;utm_medium=email&amp;utm_term=0_e44feaa081-93ec9743c9-33708685" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/code_440/items/22e8539da465686496d3?utm_source=Qiitaニュース&amp;utm_campaign=93ec9743c9-Qiita_newsletter_293_01_10_2018&amp;utm_medium=email&amp;utm_term=0_e44feaa081-93ec9743c9-33708685">qiita.com</a></cite></p> <p>インストール環境がオンラインでできるなら。こちらの通り。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Pythonで簡単なパワポファイルの作成 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/daiki7nohe/items/11019e90cd43c82095fa" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/daiki7nohe/items/11019e90cd43c82095fa">qiita.com</a></cite></p> <p>Proxyとかなんらかしらの理由でオフラインでやりたい場合は、こちら。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="python-pptx 0.6.7 : Python Package Index" src="https://hatenablog-parts.com/embed?url=https://pypi.python.org/pypi/python-pptx" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://pypi.python.org/pypi/python-pptx">pypi.python.org</a></cite></p> <p>gzファイルをダウンロードしてきて</p> <pre><code>pip install python-pptx-0.6.7.tar.gz</code></pre> <p>でインストールできます。</p> <p>その他使い方は下記参照で。普段<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>で解析しながらそのままシームレスに発表資料が作れる感じでGood!<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="python-pptxまとめ - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/pocket8137/items/3d8fda2c47664bf9130b" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/pocket8137/items/3d8fda2c47664bf9130b">qiita.com</a></cite></p> <p>公式ドキュメント</p> <p><a href="https://python-pptx.readthedocs.io/en/latest/user/quickstart.html">Getting Started — python-pptx 0.6.7 documentation</a></p> <p><a href="https://media.readthedocs.org/pdf/python-pptx/latest/python-pptx.pdf">https://media.readthedocs.org/pdf/python-pptx/latest/python-pptx.pdf</a></p> <p>Wordと<a class="keyword" href="http://d.hatena.ne.jp/keyword/Excel">Excel</a>もいけるみたい。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="python for MS Office | neblog" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.brain.rcast.u-tokyo.ac.jp%2Fwp%2Fnebula%2Fprograming-tools%2Fpython%2Fms-office" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://www.brain.rcast.u-tokyo.ac.jp/wp/nebula/programing-tools/python/ms-office">www.brain.rcast.u-tokyo.ac.jp</a></cite></p> T-N-Clark もうExcelでテストなんて発狂しそう hatenablog://entry/8599973812327768849 2017-12-19T11:30:00+09:00 2017-12-19T18:19:30+09:00 最近、テスト作業をすることが増えてきたのですが、周りの文化として地道にExcel表にエビデンスを貼り付けるという古典的な手法を使っております。頑張ってExcelの共有モードを使って分担しながらやっているのですが、重いわ、バイナリファイルだからGitで管理できないわ、読みにくいわで発狂しそうになっていました。 そこで、発狂する前にこの自体をなんとかしないかということでOSSを探し漁ったところTestLinkなるものに出会いました。詳しくはこちらのサイト様で丁寧に説明してくださっていますが、簡単にいうとWebベースで動くテスト管理ツールです。ライセンスはGPLライセンスだそうです。 gihyo.j… <p>最近、テスト作業をすることが増えてきたのですが、周りの文化として地道に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Excel">Excel</a>表に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%D3%A5%C7%A5%F3%A5%B9">エビデンス</a>を貼り付けるという古典的な手法を使っております。頑張って<a class="keyword" href="http://d.hatena.ne.jp/keyword/Excel">Excel</a>の共有モードを使って分担しながらやっているのですが、重いわ、バイナリファイルだからGitで管理できないわ、読みにくいわで発狂しそうになっていました。</p> <p>そこで、発狂する前にこの自体をなんとかしないかということで<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>を探し漁ったところ<a href="http://testlink.org/">TestLink</a>なるものに出会いました。詳しくはこちらのサイト様で丁寧に説明してくださっていますが、簡単にいうとWebベースで動く<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C6%A5%B9%A5%C8%B4%C9%CD%FD%A5%C4%A1%BC%A5%EB">テスト管理ツール</a>です。ライセンスは<a class="keyword" href="http://d.hatena.ne.jp/keyword/GPL%A5%E9%A5%A4%A5%BB%A5%F3%A5%B9">GPLライセンス</a>だそうです。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="きちんと学びたいテストエンジニアのためのTestLink入門" src="https://hatenablog-parts.com/embed?url=http://gihyo.jp/dev/serial/01/testlink" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://gihyo.jp/dev/serial/01/testlink">gihyo.jp</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/TestLink">TestLink</a>単体をインストールしてもいいのですが、諸々がパッケージ化されている方が楽なので今回はBitNamiでインストールしました。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>ユーザーであれば、こちらのサイト様が丁寧に解説してくださっています。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="TestLinkを bitnami で Windows にインストールしてみる - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/hida/items/40e7d2b0481ed3db1993" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/hida/items/40e7d2b0481ed3db1993">qiita.com</a></cite></p> <p>ちなみにBitNamiについてはこちらのサイト様をご参照ください。<br /><a href="http://macruby.info/wordpress/how-to-call-bitnami.html">BitNamiの読み方は? | MacRuby</a></p> <p>すでにBitNamiで<a class="keyword" href="http://d.hatena.ne.jp/keyword/Redmine">Redmine</a>などをインストール済みであれば、こちらのサイト様が参考になります。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title=" Win7 Bitnami RedmineにSourceForge TestLinkを追加する - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/Sakade3346/items/f687f69dd902dd723e47" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/Sakade3346/items/f687f69dd902dd723e47">qiita.com</a></cite></p> <p><a href="http://mao-instantlife.hatenablog.com/entry/2015/06/24/Redcaseをリリース前手動テスト管理に使う">Redcase&#x3092;&#x30EA;&#x30EA;&#x30FC;&#x30B9;&#x524D;&#x624B;&#x52D5;&#x30C6;&#x30B9;&#x30C8;&#x7BA1;&#x7406;&#x306B;&#x4F7F;&#x3046; - &#x51A5;&#x51A5;&#x4E43;&#x5FD7;</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/Redmine">Redmine</a>単体で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>で提供されている<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C6%A5%B9%A5%C8%B4%C9%CD%FD%A5%C4%A1%BC%A5%EB">テスト管理ツール</a>もあるようです。</p> <p>今まで作った<a class="keyword" href="http://d.hatena.ne.jp/keyword/Excel">Excel</a>のテスト仕様書を<a class="keyword" href="http://d.hatena.ne.jp/keyword/TestLink">TestLink</a>にインポートしたいという場合は、こちらのサイト様方が解説してくださっています。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="【TestLinkでナレッジ蓄積】Excelからテスト項目書をインポートする方法" src="https://hatenablog-parts.com/embed?url=http://quesqa.com/testlink/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://quesqa.com/testlink/">quesqa.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="TestLink1.9.9を使ってみた | 実験ぶろぐ(仮)試供品" src="https://hatenablog-parts.com/embed?url=http://needtec.exblog.jp/22478889/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://needtec.exblog.jp/22478889/">needtec.exblog.jp</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="TestLink1.9.10へのバージョンアップ時の調査 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/mima_ita/items/f2837a91c9492a2b9e72" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/mima_ita/items/f2837a91c9492a2b9e72">qiita.com</a></cite></p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Excel">Excel</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>のバージョンによっては<a class="keyword" href="http://d.hatena.ne.jp/keyword/VBA">VBA</a>が動かない場合があるのでご注意ください。私の場合はこちらのサイト様に助けていただきました。<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="MSXMLを使ったExcel VBAがWindows 8.1で動かない時の対処方法 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/rryu/items/f5ea20c721cefc8cdbdf" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/rryu/items/f5ea20c721cefc8cdbdf">qiita.com</a></cite></p> <p>導入した感想は気が向いたら書きます。</p> <p>お勉強用資料はこちら様などが参考になりました。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="TestLinkの資料リンク - プログラマの思索" src="https://hatenablog-parts.com/embed?url=http://forza.cocolog-nifty.com/blog/2014/10/testlink-4f89.html" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://forza.cocolog-nifty.com/blog/2014/10/testlink-4f89.html">forza.cocolog-nifty.com</a></cite></p> <p> </p> T-N-Clark Bootstrap使ってみた hatenablog://entry/8599973812323947048 2017-12-15T11:30:00+09:00 2017-12-18T22:51:32+09:00 ※【注意】Bootstrepのバージョンによってはない機能もあるようです.各サイトを利用するときはバージョンのご確認を.まあ,究極的にはCSSソースを見ればいいのでしょうが. 導入 getbootstrap.com techacademy.jp グリッドシステム・レスポンシブデザイン qiita.com websae.net qiita.com テーブル www.atmarkit.co.jp ボタン webnetamemo.com <p>※【注意】Bootstrepのバージョンによってはない機能もあるようです.各サイトを利用するときはバージョンのご確認を.まあ,究極的には<a class="keyword" href="http://d.hatena.ne.jp/keyword/CSS">CSS</a>ソースを見ればいいのでしょうが.</p> <ul> <li>導入</li> </ul> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Bootstrap" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgetbootstrap.com%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://getbootstrap.com/">getbootstrap.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Webデザインの知識がなくてもOK!Bootstrapの使い方【入門者向け】" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftechacademy.jp%2Fmagazine%2F6270" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://techacademy.jp/magazine/6270">techacademy.jp</a></cite></p> <ul> <li>グリッドシステム・レスポンシブデザイン</li> </ul> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="こうすれば理解できるBootstrap 4のレスポンシブWebデザインとそのCSS - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Ftonkotsuboy_com%2Fitems%2F7c01460b59c3ca5ee047" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/tonkotsuboy_com/items/7c01460b59c3ca5ee047">qiita.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Bootstrapのグリッドシステムの使い方を初心者に向けておさらいする" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fwebsae.net%2Ftwitter-bootstrap-grid-system-21060224%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://websae.net/twitter-bootstrap-grid-system-21060224/">websae.net</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Bootstrap使い方メモ1(基本+CSS) - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fopengl-8080%2Fitems%2F2764b6db143b1a4411f6" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/opengl-8080/items/2764b6db143b1a4411f6">qiita.com</a></cite></p> <ul> <li>テーブル</li> </ul> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Webデザイン初心者でもできる、Bootstrapの使い方超入門 (3/4)" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fwww.atmarkit.co.jp%2Fait%2Farticles%2F1403%2F19%2Fnews034_3.html" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://www.atmarkit.co.jp/ait/articles/1403/19/news034_3.html">www.atmarkit.co.jp</a></cite></p> <ul> <li>ボタン</li> </ul> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Bootstrapに用意されているクラス【ボタン編】" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwebnetamemo.com%2Fcoding%2Fbootstrap%2F201511202268" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://webnetamemo.com/coding/bootstrap/201511202268">webnetamemo.com</a></cite></p> T-N-Clark Markdownでホームページが書きたくて hatenablog://entry/8599973812323947002 2017-12-06T11:30:00+09:00 2018-01-11T00:18:58+09:00 最近、導入してみたタグの記事ばかり上げて節操ないですね。でも、導入してみたはいいが、使い勝手やニーズが合わなくてやめることはままあると思います。下調べが足りないと言われればそれまでですが…でも、それくらい気軽に新しい技術に触れられる世の中で私は幸せです。 前口上はこれくらいにして早速今回導入してみたのはHugoです。 詳細は詳しいサイトがいくらでもあるのでそちらを参照ください。ここでは、公開や管理すら無視した最低限のことだけ書いておきます。 公式からダウンロードするWindowsの方Hugo の使い方 - knooto MacやLinuxの方qiita.com パスを通す先ほどダウンロードして… <p>最近、導入してみたタグの記事ばかり上げて節操ないですね。でも、導入してみたはいいが、使い勝手やニーズが合わなくてやめることはままあると思います。下調べが足りないと言われればそれまでですが…でも、それくらい気軽に新しい技術に触れられる世の中で私は幸せです。</p> <p>前口上はこれくらいにして早速今回導入してみたのはHugoです。</p> <p>詳細は詳しいサイトがいくらでもあるのでそちらを参照ください。ここでは、公開や管理すら無視した最低限のことだけ書いておきます。</p> <ol> <li>公式からダウンロードする<br /><a class="keyword" href="http://d.hatena.ne.jp/keyword/Windows">Windows</a>の方<br /><a href="https://knooto.info/hugo/">Hugo の使い方 - knooto</a><br /> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Mac">Mac</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/Linux">Linux</a>の方<iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="HUGOを使ってサイトを立ち上げる方法 - Qiita" src="https://hatenablog-parts.com/embed?url=https://qiita.com/syui/items/869538099551f24acbbf" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/syui/items/869538099551f24acbbf">qiita.com</a></cite></p> </li> <li>パスを通す<br />先ほどダウンロードしてきたファイルを展開するとhugo.exeファイルがあるはずあので,それを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8">コマンドプロンプト</a>やターミナルから呼び出せるようにパスを通します.</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8">コマンドプロンプト</a>を開く</li> <li>HTMLのソースを置きたいフォルダまで移動する<br />cdで移動するだけです.</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8">コマンドプロンプト</a>などで <pre><code>hugo new site [フォルダ名]</code></pre> を実行する.<br />フォルダ名はなんでもいいです.適当に"HOMEPAGE"とかでいいです.なければその名前のフォルダが作られます.ここでHugoのテーマを入れろとか言われますが,入れなくても動きます.テーマを入れる際はライセンスにお気を付けください.多くがMITライセンスだと思います. <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="MITライセンスってなに?どうやって使うの?商用でも無料で使えるの? - WisdomMingle.com(ウィズダムミングル・ドットコム)" src="https://hatenablog-parts.com/embed?url=http://wisdommingle.com/mit-license/" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://wisdommingle.com/mit-license/">wisdommingle.com</a></cite></p> </li> <li>テンプレートとなる側となるHTMLを書く<br /> <p>6~8は以下のサイト様をご参照ください.<br /><a href="http://wdkk.co.jp/note/2015/0731-hugo/">Hugoでwebサイト構築(1) レイアウトことはじめ | Watanabe-DENKI Inc. 渡辺電気株式会社</a></p> </li> <li>記事のレイアウトを作る</li> <li>コンテンツを書く<br />マークダウン(md)で先ほどのテンプレートに埋め込む内容を書きます.</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8">コマンドプロンプト</a>などで <pre><code>hugo</code></pre> を実行する.<br />これで6,7でつくったファイルをindex.htmlへと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>します.<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%D1%A5%A4%A5%EB">コンパイル</a>されたものはpublicフォルダに生成されます.mdで書いたものはその名前のフォルダの中に作られます.例えば"test.md"としたならば「public-&gt;test」にindex.htmlがあるはずです.</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8">コマンドプロンプト</a>などで <pre><code>hugo server</code></pre> を実行する.<br />ローカルサーバーを立ち上げて表示します.適当に<a class="keyword" href="http://d.hatena.ne.jp/keyword/IE">IE</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/Chrome">Chrome</a>などのブラウザを起動して表示されたURLを打ち込こめば見えます.基本的には<br /><a href="http://localhost:1313/test/">http://localhost:1313/<br /></a>になるのではないでしょうか.あくまでローカルサーバーなので,ウェブ上に公開されたわけではありません.ここで表示されるのはpublic直下にあるindex.htmlです.<br /><a href="http://localhost:1313/test/">http://localhost:1313/test/</a><br />とすればtest.mdから生成されたindex.htmlを表示できます.ちなみに直接htmlファイル名をしていしないでフォルダ名をしていしたときは,そのフォルダ内のindex.htmlという名前のフォルダがデフォルトでは表示されます.<br /><a href="http://localhost:1313/test/">http://localhost:1313/test/hoge.html</a><br />としておけば,index.html以外のhtmlファイルにもアクセスできます.</li> </ol> <p>とりあえずこれで動きます。</p> <p>あとはヘッダー、フッターをつけたり、でページを増やしたり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/markdown">markdown</a>の書き方を覚えたりすればよいのではないのでしょうか。次回辺りに体裁を整えるためにBootstrapを使う話をつけようと思います。</p> <p>その他参考ページ</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Markdown記法 チートシート - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2FQiita%2Fitems%2Fc686397e4a0f4f11683d" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/Qiita/items/c686397e4a0f4f11683d">qiita.com</a></cite></p> <p><a href="https://www-he.scphys.kyoto-u.ac.jp/member/shotakaha/dokuwiki/doku.php?id=toolbox:hugo:start">https://www-he.scphys.kyoto-u.ac.jp/member/shotakaha/dokuwiki/doku.php?id=toolbox:hugo:start</a></p> T-N-Clark 【C++】グループ開発のためにコメントを残すことにした hatenablog://entry/8599973812293713558 2017-11-30T11:30:00+09:00 2017-11-30T11:30:01+09:00 コメント・ドキュメントのお話 ソースコードからドキュメントをDoxygenで生成する Doxygen 向け C/C++ ファイルのコメントテンプレート - l1o0の日記 qiita.com 良い?悪い?コードコメントの書き方 from Shigenori Sagawa www.slideshare.net ドローイングツール qiita.com plantuml.com dev.classmethod.jp dev.classmethod.jp <ul> <li>コメント・ドキュメントのお話</li> </ul> <p><a href="http://alctail.sakura.ne.jp/tip/cplus_kannrenn/cplusplus/doc.shtml">ソースコードからドキュメントをDoxygenで生成する</a></p> <p><a href="http://d.hatena.ne.jp/l1o0/touch/20101209/1291903016">Doxygen 向け C/C++ ファイルのコメントテンプレート - l1o0の日記</a></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Doxygen 書き方メモ (C, C++) - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Finabe49%2Fitems%2F23e615649e8539d857a8" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/inabe49/items/23e615649e8539d857a8">qiita.com</a></cite></p> <p><iframe style="border: 1px solid #CCC; border-width: 1px; margin-bottom: 5px; max-width: 100%;" src="https://www.slideshare.net/slideshow/embed_code/key/GRxMmMJXupE9Ql" width="427" height="356" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" allowfullscreen=""> </iframe></p> <div style="margin-bottom: 5px;"><strong> <a href="https://www.slideshare.net/ssagawa/ss-14579535" title="良い?悪い?コードコメントの書き方" target="_blank">良い?悪い?コードコメントの書き方</a> </strong> from <strong><a href="https://www.slideshare.net/ssagawa" target="_blank">Shigenori Sagawa</a></strong></div> <p><cite class="hatena-citation"><a href="https://www.slideshare.net/ssagawa/ss-14579535">www.slideshare.net</a></cite></p> <ul> <li>ドローイングツール</li> </ul> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="テキストでUMLを書く - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fhakaicode%2Fitems%2F98823e0ceab3f3f33cca" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/hakaicode/items/98823e0ceab3f3f33cca">qiita.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Open-source tool that uses simple textual descriptions to draw UML diagrams." src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fplantuml.com%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://plantuml.com/">plantuml.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Atom と PlantUML で快適シーケンス図駆動開発ライフ | Developers.IO" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdev.classmethod.jp%2Fdevenv%2Fatom-plantuml-sequence%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://dev.classmethod.jp/devenv/atom-plantuml-sequence/">dev.classmethod.jp</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="エンジニアのための無料で使えるドローイングツールまとめ | Developers.IO" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fdev.classmethod.jp%2Fetc%2Fdrawing_tools%2F" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://dev.classmethod.jp/etc/drawing_tools/">dev.classmethod.jp</a></cite></p> <p><cite class="hatena-citation"></cite> </p> T-N-Clark PythonでGUIアプリ作りたくなったので、Kivyを入れてみた hatenablog://entry/8599973812321700970 2017-11-28T11:30:00+09:00 2017-11-28T11:30:01+09:00 最近、私的にも仕事でもpythonを使う機会が増えてきました。ちょうど、qiitaの方でありがた過ぎる記事に出会ったこともあって、この機にpythonでGUIアプリケーションを作ってみようと思いました。 qiita.com この中で紹介されていたGUIアプリケーション作成環境はKivyというものでした。 Kivy - Wikipedia というわけで早速インストールを試みました。今回はこれらのサイト様のお力を借りてインストールしました。 Kivy 超入門(1):インストールからHello, World! まで qiita.com 私は、pythonはanaconda経由で3.6をすでにインスト… <p>最近、私的にも仕事でも<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>を使う機会が増えてきました。ちょうど、qiitaの方でありがた過ぎる記事に出会ったこともあって、この機に<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>で<a class="keyword" href="http://d.hatena.ne.jp/keyword/GUI">GUI</a>アプリケーションを作ってみようと思いました。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Pythonのテキスト作りました - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2FKatsunoriNakamura%2Fitems%2Fb465b0cf05b1b7fd4975%3Futm_source%3DQiita%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9%26utm_campaign%3D280fb7b737-Qiita_newsletter_279_09_27_2017%26utm_medium%3Demail%26utm_term%3D0_e44feaa081-280fb7b737-33714525" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/KatsunoriNakamura/items/b465b0cf05b1b7fd4975?utm_source=Qiitaニュース&amp;utm_campaign=280fb7b737-Qiita_newsletter_279_09_27_2017&amp;utm_medium=email&amp;utm_term=0_e44feaa081-280fb7b737-33714525">qiita.com</a></cite></p> <p>この中で紹介されていた<a class="keyword" href="http://d.hatena.ne.jp/keyword/GUI">GUI</a>アプリケーション作成環境はKivyというものでした。</p> <p><a href="https://ja.wikipedia.org/wiki/Kivy">Kivy - Wikipedia</a></p> <p>というわけで早速インストールを試みました。今回はこれらのサイト様のお力を借りてインストールしました。</p> <p><a href="http://iatlex.com/python/kivy_1/">Kivy 超入門(1):インストールからHello, World! まで</a></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Python3系 + Visual StudioでKivyの開発環境(Windows)を作成する - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fdario_okazaki%2Fitems%2Ffe2682fbd88e971c2ef2" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/dario_okazaki/items/fe2682fbd88e971c2ef2">qiita.com</a></cite></p> <p>私は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/python">python</a>はanaconda経由で3.6をすでにインストール済みだったので、kivyとその関連ライブラリーのインストールからはじめました。公式に書いてある通り、</p> <pre><code>python -m pip install --upgrade pip wheel setuptools</code></pre> <p>でインストールを試みたのですが、いきなりコケました。なぜかオンラインでsetuptoolsがインストールできませんでした。仕方ないので、ブラウザからダウンロードしてきて</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="setuptools 38.2.1 : Python Package Index" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fpypi.python.org%2Fpypi%2Fsetuptools" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://pypi.python.org/pypi/setuptools">pypi.python.org</a></cite></p> <p>オフラインインストールをしました。やり方は単純で、</p> <ol> <li>pipが使えるコンソール(<a class="keyword" href="http://d.hatena.ne.jp/keyword/windows">windows</a>でanacondaを入れているならAnacondaのプロンプト、<a class="keyword" href="http://d.hatena.ne.jp/keyword/mac">mac</a>ならterminal)を開いて、ダウンロードしてきた「setuptools-38.2.1-py2.py3-none-any.whl」などの名前のファイルと同じ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リに移動します。</li> <li>移動先で <pre><code>pip install setuptools-38.2.1-py2.py3-none-any.whl</code></pre> を実行します。</li> </ol> <p>基本的にはこの2ステップだけで入るはずです。色々メッセージが出て、最後に</p> <pre><code>Successfully installed setuptools-38.2.1</code></pre> <p>などの成功メッセージが出れば成功です。</p> <p>次にdocutilsとpygmentsとpypiwin32とsdl2とglewをインストールします。</p> <pre><code>python -m pip install docutils pygments pypiwin32 kivy.deps.sdl2 kivy.deps.glew</code></pre> <p>ここでも何かのインストールで失敗しました。人によってはなくてもいいライブラリもあるので今回は無視。必要があれば、何がないのかわかった時点で再インストールすることにします。ライブラリの詳細は公式を参考にどうぞ。</p> <p><a href="https://pyky.github.io/kivy-doc-ja/installation/installation-windows.html">Installation on Windows(翻訳済み) — Kivy 1.10.0 ドキュメント</a></p> <p><a href="https://libsdl.org/download-2.0.php">Simple DirectMedia Layer - SDL version 2.0.7 (stable)</a></p> <p>次に、映像や音声系のライブラリgstreamerのインストール。</p> <pre><code>python -m pip install kivy.deps.gstreamer</code></pre> <p>今回は3.6なのでangleは飛ばします。最後にKivyについてもインストール。</p> <pre><code>python -m pip install kivy</code></pre> <p>実行して</p> <pre><code>Successfully installed Kivy-Garden-0.1.4 kivy-1.10.0</code></pre> <p>などのメッセージが出れば成功です。具体的にどれか必須かはわかりませんが、上述のどれかがないとKivyのインストールに失敗します。私の場合は、setuptoolsがないせいでした。メッセージがいっぱい出て面食らうかもしれませんが、だいたい最後に出るメッセージを読めば、何に失敗したかくらいはわかります。私の場合は、を実行したら、</p> <pre><code>Could not import setuptools which is required to install from a source distribution. Please install setuptools</code></pre> <p>などと出ました。これを見れば、「あぁ、setuptoolsがないから怒られているのね」ということぐらいはわかるかと思います。ないものがわかったら、pipでインストールを試みる、オンラインインストールに失敗したら、ブラウザ経由でダウンロードしてオフラインでインストールする。これで大半は解決できると思います。ちなみに<a class="keyword" href="http://d.hatena.ne.jp/keyword/mac">mac</a>の私は、Kivyはインストールできたのですが、サンプルコードを実行したら、<a class="keyword" href="http://d.hatena.ne.jp/keyword/pygame">pygame</a>がないと怒られました。さらに<a class="keyword" href="http://d.hatena.ne.jp/keyword/pygame">pygame</a>のインストールには<a class="keyword" href="http://d.hatena.ne.jp/keyword/sdl">sdl</a>がいるのでこれから先にインストール。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Mac環境にてKivyをインストールしてみた - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fume3_%2Fitems%2F833087d7ff607531291e" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/ume3_/items/833087d7ff607531291e">qiita.com</a></cite></p> <p>無事、<a class="keyword" href="http://d.hatena.ne.jp/keyword/sdl">sdl</a>→<a class="keyword" href="http://d.hatena.ne.jp/keyword/pygame">pygame</a>の順で無事インストール成功。<a class="keyword" href="http://d.hatena.ne.jp/keyword/pygame">pygame</a>をオフラインインストールするならこちらを参考にすれば落とせます。</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Pygameをインストールする方法 (Windows編)" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Fgamepro.blog.jp%2Fpython%2Fpygame%2Finstall" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://gamepro.blog.jp/python/pygame/install">gamepro.blog.jp</a></cite></p> <p><a href="http://iatlex.com/python/kivy_1/">Kivy 超入門(1):インストールからHello, World! まで</a></p> <p>に習って</p> <pre><code> from kivy.app import App from kivy.uix.label import Label class IntroKivy(App): def build(self): return Label(text="Hello, World!") if __name__ == "__main__": IntroKivy().run() </code></pre> <p>を実行すると、コンソール上はなんかまだエラーを吐いている気もしましたが、とりあえず"Hellow World"の画面が表示されました。やったね。</p> <p><img class="hatena-fotolife" title="f:id:T-N-Clark:20171128004402p:plain" src="https://cdn-ak.f.st-hatena.com/images/fotolife/T/T-N-Clark/20171128/20171128004402.png" alt="f:id:T-N-Clark:20171128004402p:plain" width="337" /></p> <p>これからもう少し遊んで、冒頭の教科書に習って簡単なアプリを作ってみようと思います。長文、駄文ではありますが、ご参考になれば幸いです。ではまた。</p> <p>〜参考〜冒頭の教科書以外でKivy基本文法を学ぶときに参考になりそうなサイト様</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Python Kivyの使い方① ~Kv Languageの基本~ - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fdario_okazaki%2Fitems%2F7892b24fcfa787faface" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/dario_okazaki/items/7892b24fcfa787faface">qiita.com</a></cite></p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="Pycon JP 2017 の「kivyによるアプリケーション開発のすすめ」のソースコードの解説 - Qiita" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fqiita.com%2Fdario_okazaki%2Fitems%2Fb616e04da3541235fbd1" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="https://qiita.com/dario_okazaki/items/b616e04da3541235fbd1">qiita.com</a></cite></p> <p>Anacondaのライブラリとの連携</p> <p><iframe class="embed-card embed-webcard" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" title="【python3】Windows版のkivyでnumpyやpandasを使う【kivy, numpy】" src="https://hatenablog-parts.com/embed?url=http%3A%2F%2Flaughingman-log.blogspot.jp%2F2015%2F09%2Fwindowskivynumpypandas.html%3Fm%3D1" frameborder="0" scrolling="no"></iframe><cite class="hatena-citation"><a href="http://laughingman-log.blogspot.jp/2015/09/windowskivynumpypandas.html?m=1">laughingman-log.blogspot.jp</a></cite></p> T-N-Clark