<?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:gd="http://schemas.google.com/g/2005" xmlns:georss="http://www.georss.org/georss" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:thr="http://purl.org/syndication/thread/1.0"><id>tag:blogger.com,1999:blog-6154025142032985882</id><updated>2026-05-24T21:27:00.375+09:00</updated><category term="Android"/><category term="Ubuntu"/><category term="Python"/><category term="Eclipse"/><category term="Android Studio"/><category term="Wi-Fi"/><category term="Windows"/><category term="JAVA"/><category term="Android Emulator"/><category term="BeagleBoard-xM"/><category term="wxPython"/><category term="Google"/><category term="JavaScript"/><category term="Gradle"/><category term="Git"/><category term="Nexus7"/><category term="Vino"/><category term="Xperia"/><category term="Android NDK"/><category term="GNOME"/><category term="JUnit"/><category term="XRC"/><category term="Atom"/><category term="Custom ROM"/><category term="Dropbox"/><category term="Google Maps"/><category term="Mercurial"/><category term="Visual Studio"/><category term="Bluetooth"/><category term="Gmail"/><category term="KeePass"/><category term="MPD"/><category term="OpenLayers 3"/><category term="SQLite"/><category term="dconf"/><category term="fontconfig"/><category term="gsettings"/><category term="Adobe AIR"/><category term="Blogger"/><category term="C#"/><category term="Cygwin"/><category term="Docker"/><category term="Eclipse CDT"/><category term="Electron"/><category term="Exif"/><category term="FFmpeg"/><category term="Google Docs"/><category term="Gtk+"/><category term="HTA"/><category term="Hulu"/><category term="ImageMagick"/><category term="Markdown"/><category term="Marked"/><category term="Nautilus"/><category term="Proguard"/><category term="RAID"/><category term="Raspberry Pi"/><category term="Samba"/><category term="VMware"/><category term="Visual Studio Code"/><category term="emacs"/><category term="node"/><category term="pip"/><category term="qpdf"/><category term="動画変換"/><category term="AdSense"/><category term="Batch File"/><category term="CMake"/><category term="Chrome"/><category term="Cypress"/><category term="C言語"/><category term="Embed"/><category term="Fragment"/><category term="GIF"/><category term="I2C"/><category term="Inkscape"/><category term="Kindle"/><category term="Kotlin"/><category term="Linux"/><category term="Logicool"/><category term="Logitech"/><category term="Mozc"/><category term="NetBIOS"/><category term="OTG"/><category term="OpenSSL"/><category term="PC"/><category term="PSoC"/><category term="Pogoplug"/><category term="PulseAudio"/><category term="PyCharm"/><category term="RDP"/><category term="React"/><category term="Remmina"/><category term="SQL"/><category term="SRS-D5"/><category term="Serial port"/><category term="Spectron"/><category term="SyntaxHighlighter"/><category term="Tera Term"/><category term="Terminal"/><category term="TypeScript"/><category term="USA"/><category term="Unifying Receiver"/><category term="Unit Test"/><category term="VNC"/><category term="VirtualBox"/><category term="Vite"/><category term="VitualBox"/><category term="Vue"/><category term="WSL"/><category term="X Window"/><category term="ZIP"/><category term="avahi"/><category term="calibre"/><category term="cups"/><category term="diff"/><category term="ftp"/><category term="grpc"/><category term="jQuery"/><category term="keytool"/><category term="lzo"/><category term="mDNS"/><category term="nkf"/><category term="pki"/><category term="sikuli"/><category term="stunnel"/><category term="systemd"/><category term="twitter"/><category term="venv"/><category term="zsh"/><title type="text">穀風</title><subtitle type="html">プログラム全般、無節操に手を出しまくってみる</subtitle><link href="https://kokufu.blogspot.com/feeds/posts/default" rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default?redirect=false" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/" rel="alternate" type="text/html"/><link href="http://pubsubhubbub.appspot.com/" rel="hub"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default?start-index=26&amp;max-results=25&amp;redirect=false" rel="next" type="application/atom+xml"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><generator uri="http://www.blogger.com" version="7.00">Blogger</generator><openSearch:totalResults>367</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><xhtml:meta content="noindex" name="robots" xmlns:xhtml="http://www.w3.org/1999/xhtml"/><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-1947943426996857782</id><published>2024-08-15T09:54:00.000+09:00</published><updated>2024-08-15T09:54:51.733+09:00</updated><title type="text">[Vuetify3] custom icon を追加する</title><content type="html">&lt;pre class="marked"&gt;
[公式の説明](https://vuetifyjs.com/en/features/icon-fonts)が少し分かりづらいのですが、Vuetify3 には icon の設定方法が**2つ**&lt;sup&gt;&lt;span class='barenote'&gt;「MDI - CSS」と「MDI - JS SVG」の2つです。「Icon Search」は単なる便利ツールなのでご注意を。&lt;/span&gt;&lt;/sup&gt;あります。
- 「MDI - CSS」は全 MDI fornt を一括でインストールする方法
- 「MDI - JS SVG」は必要な font だけをインストールする方法

&lt;div class="separator" style="clear: both;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmz_1nyatWUpqGmkruMCYhTgPdj2LckOfZra0SRZLMtNJyTJUo3vg-j22pB03Jc7b7t1cRTRmaHMT3bhVA4jGp2vYmVXPk8wLC-zwUKKuzaWuBAM4AYZeUo27DtW7nSzpw1cwvYJn4UG0VSlZCNEI9xDo5hyphenhyphenGtPsvZITlURXPzdt32EeRdKgfkt-eZHWU/s210/2024-08-15_09-50.png" style="display: block; padding: 1em 0; text-align: center; "&gt;&lt;img alt="" border="0" height="320" data-original-height="210" data-original-width="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmz_1nyatWUpqGmkruMCYhTgPdj2LckOfZra0SRZLMtNJyTJUo3vg-j22pB03Jc7b7t1cRTRmaHMT3bhVA4jGp2vYmVXPk8wLC-zwUKKuzaWuBAM4AYZeUo27DtW7nSzpw1cwvYJn4UG0VSlZCNEI9xDo5hyphenhyphenGtPsvZITlURXPzdt32EeRdKgfkt-eZHWU/s320/2024-08-15_09-50.png"/&gt;&lt;/a&gt;&lt;/div&gt;


当然、後者の方が効率も良いしお勧めなのですが、公式が書いていないメリットがもう一つあります。

「MDI - JS SVG」の方は custom icon の表示が超楽なのです。

&lt;a name='more'&gt;&lt;/a&gt;

## そもそも「追加」する必要がない
「MDI - JS SVG」は "MDI" と書いてありますが、実はほとんど Material Design Icon とは関係がありません。  
この Icon Set&lt;sup&gt;&lt;span class='barenote'&gt;**Set**と言っていいのか？&lt;/span&gt;&lt;/sup&gt; は与えられた文字列を path として svg icon として描画する機能を提供しています。  
簡単に言ってしまえば、1 path の svg であれば何でもオーケーなのです&lt;sup&gt;&lt;span class='barenote'&gt;正確には Vuetify の中で使用されているいくつかの icon をセットとして提供しています&lt;/span&gt;&lt;/sup&gt;

論より証拠。
まずはやってみましょう。[公式](https://vuetifyjs.com/en/features/icon-fonts/#mdi-js-svg)に書いてあるように `mdi` icon set を使用するようにします。

```js
import { createVuetify } from 'vuetify'
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'

export default createVuetify({
  icons: {
    defaultSet: 'mdi',
    aliases,
    sets: {
      mdi,
    },
  },
})
```

その後、以下のように SVG の path 情報をベタ書き。
```html
&lt;template&gt;
  &lt;v-icon icon="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" &gt;&lt;/v-icon&gt;
&lt;/template&gt;
```
これで、以下のようにアイコンが表示されます。
&lt;div class="separator" style="clear: both;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBYfiZMNdI3H150_sRY7Mg6OjZyd4SLGXbaK_WLEnc8dw4XRr98Upk-IzVICb-eZ4OKCGHNVNimquRhyispvZRONpkXZZvbHy_kP4h01CxCdJjU4A8Ka08szq-B0nH8RsoANKPzNlWUdchqDj3MaIcEQkNOdzmULo64Ppv96zs-bcFHzlkJCXEzxEk-GY/s85/2024-08-15_09-52.png" style="display: block; padding: 1em 0; text-align: center; "&gt;&lt;img alt="" border="0" height="200" data-original-height="85" data-original-width="49" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBYfiZMNdI3H150_sRY7Mg6OjZyd4SLGXbaK_WLEnc8dw4XRr98Upk-IzVICb-eZ4OKCGHNVNimquRhyispvZRONpkXZZvbHy_kP4h01CxCdJjU4A8Ka08szq-B0nH8RsoANKPzNlWUdchqDj3MaIcEQkNOdzmULo64Ppv96zs-bcFHzlkJCXEzxEk-GY/s200/2024-08-15_09-52.png"/&gt;&lt;/a&gt;&lt;/div&gt;

実際、 [@mdi/js](https://github.com/Templarian/MaterialDesign-JS) の中身を見てみると、以下のように string 情報が羅列されているだけです。

```js
export var mdiAbTesting = "M4 2A2 2 0 0 0 2 4V12H4V8H6V12H8V4A2 2 0 0 0 6 2H4M4 4H6V6H4M22 15.5V14A2 2 0 0 0 20 12H16V22H20A2 2 0 0 0 22 20V18.5A1.54 1.54 0 0 0 20.5 17A1.54 1.54 0 0 0 22 15.5M20 20H18V18H20V20M20 16H18V14H20M5.79 21.61L4.21 20.39L18.21 2.39L19.79 3.61Z";
export var mdiAbacus = "M5 5H7V11H5V5M10 5H8V11H10V5M5 19H7V13H5V19M10 13H8V19H10V17H15V15H10V13M2 21H4V3H2V21M20 3V7H13V5H11V11H13V9H20V15H18V13H16V19H18V17H20V21H22V3H20Z";
export var mdiAbjadArabic = "M12 4C10.08 4 8.5 5.58 8.5 7.5C8.5 8.43 8.88 9.28 9.5 9.91C7.97 10.91 7 12.62 7 14.5C7 17.53 9.47 20 12.5 20C14.26 20 16 19.54 17.5 18.66L16.5 16.93C15.28 17.63 13.9 18 12.5 18C10.56 18 9 16.45 9 14.5C9 12.91 10.06 11.53 11.59 11.12L16.8 9.72L16.28 7.79L11.83 9C11.08 8.9 10.5 8.28 10.5 7.5C10.5 6.66 11.16 6 12 6C12.26 6 12.5 6.07 12.75 6.2L13.75 4.47C13.22 4.16 12.61 4 12 4Z";
...省略
```

## カスタムアイコンを使用する方法
というわけで、結論としては以下のように path 情報を提供するファイルを生成して、
```js
`title: "customIcons.ts";
export const customMyIcon = 'M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z';
```

`@mdi/js` と同様に使用してあげれば OK です。
```js
import {customMyIcon} from '@/icons'
```
```html
&lt;template&gt;
&lt;v-icon :icon="customMyIcon"&gt;&lt;/v-icon&gt;
&lt;/template&gt;
```

ただし、このとき生成される `svg` タグは 24 x 24 (ViewBox も) なので注意が必要です。
path 情報も 24 x 24 で生成しておかねばなりません。


## 複数 path や ViewBox のサイズが違う場合どうしたら良いのか？
まず、単色であれば 単一 path に変換してあげたほうが良いでしょう。  
そうすることで、色を後から変えることができます。

また、サイズも 24 x 24 に変換したものを使うのが良いかと思います。

それでも、複雑な svg を表示したい。という場合&lt;sup&gt;&lt;span class='barenote'&gt;もう、それアイコンじゃないよね、という話も…&lt;/span&gt;&lt;/sup&gt;は、以下のようにsvg タグを実装したカスタムコンポーネントを用意しましょう。
このとき、`class="v-icon__svg"` を加えることで、アイコンの特性をもたせることができます。ただし、この class 名は将来的に変わる可能性もあるので要注意です。
```html
` title: "CustomMyIcon.vue"
&lt;tamplate&gt;
  &lt;svg class="v-icon__svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"&gt;
    &lt;!-- svg 実装--&gt;
  &lt;/svg&gt;
&lt;/tamplate&gt;
```

以下のようにコンポーネントを直接 `:icon` に渡した場合、アイコンサイズで表示してくれます。
```js
import CustomMyIcon from '@/CustomMyIcon.vue'
```
```html
&lt;template&gt;
&lt;v-icon :icon="CustomMyIcon"&gt;&lt;/v-icon&gt;
&lt;/template&gt;
```

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/1947943426996857782/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/1947943426996857782" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1947943426996857782" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1947943426996857782" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2024/08/vuetify3-custom-icon.html" rel="alternate" title="[Vuetify3] custom icon を追加する" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmz_1nyatWUpqGmkruMCYhTgPdj2LckOfZra0SRZLMtNJyTJUo3vg-j22pB03Jc7b7t1cRTRmaHMT3bhVA4jGp2vYmVXPk8wLC-zwUKKuzaWuBAM4AYZeUo27DtW7nSzpw1cwvYJn4UG0VSlZCNEI9xDo5hyphenhyphenGtPsvZITlURXPzdt32EeRdKgfkt-eZHWU/s72-c/2024-08-15_09-50.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-2405411123087564282</id><published>2023-09-13T15:41:00.000+09:00</published><updated>2023-09-13T15:41:19.235+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Vite"/><category scheme="http://www.blogger.com/atom/ns#" term="Vue"/><title type="text">[Vite] マルチページを作成した際は rewrite を正しく設定しなければならない</title><content type="html">&lt;pre class="marked"&gt;
[Vite](https://vitejs.dev/) では、以下のように `rollupOptions.input` を指定することでマルチページアプリケーションを作ることが可能です。

```js
`title: "vite.config.ts"
export default defineConfig({
  // 省略
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'src/main/index.html'),
        admin: resolve(__dirname, 'src/admin/index.html'),
        user: resolve(__dirname, 'src/user/index.html'),
      },
    },
  },
});
```

&lt;div class="reference"&gt;&lt;div class="title"&gt;参考&lt;/div&gt;&lt;div class="content"&gt;

[Build Options | Vite](https://rollupjs.org/configuration-options/#input)

&lt;/div&gt;&lt;/div&gt;

場合によっては便利なのですが、この設定をした上で [Vue Router](https://router.vuejs.org/) のようなルーティング機能を使うと問題がおこるので注意しなければなりません。

例えば `main` 以下に `/main/users/1200` のようなサブページを作りたい場合、ルーターの設定をしただけでは動かないはずです。

この場合、サーバーは `/main/users/1200` などというページが存在しないので、特定のページに rewrite します。
これが、 Vite の dev server の場合、デフォルトで `/index.html` なのです。つまり、そんなページは存在しないとエラーを吐かれてしまいます。

これを、 `/main.html` に rewrite するよう、設定を変更しなければなりません。

&lt;a name='more'&gt;&lt;/a&gt;

### dev server
`npm run dev` 等で開発サーバーを立ち上げた場合、 Vite が内部に持っているサーバーが立ち上がります。
このサーバーの rewrite を設定するには、以下のように `vite.config.ts` に plugins を設定します。

```js
`title: "vite.config.ts"
export default defineConfig({
  // 省略
  plugins: [
    {
      name: 'rewrite-route',
      configureServer({ middlewares }) {
        middlewares.use((req, _, next) =&gt; {
          if (req.url != null) {
            if (req.url.startsWith('/main/')) {
              req.url = '/main.html';
            } else if (req.url.startsWith('/admin/')) {
              req.url = '/admin.html';
            } else if (req.url.startsWith('/user/')) {
              req.url = '/user.html';
            }
          }
          next();
        });
      },
    },
  ],
});
```

&lt;div class="reference"&gt;&lt;div class="title"&gt;参考&lt;/div&gt;&lt;div class="content"&gt;

https://vitejs.dev/guide/api-plugin.html#configureserver

&lt;/div&gt;&lt;/div&gt;

### Firebase Hosting
この挙動はサーバー側のものですから、サーバーの種類を変えた場合、設定方法も変わります。

本番環境にデプロイする場合は、こちらも考慮しなければなりません。

例えば [Firebase Hosting](https://firebase.google.com/docs/hosting/full-config?authuser=0#rewrites) の場合、以下のような設定ファイルを用意することで rewrite を制御することが可能です。


```js
`title: "firebase.json"
{
  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/main/**",
        "destination": "/main.html"
      },
      {
        "source": "/admin/**",
        "destination": "/admin.html"
      },
      {
        "source": "/user/**",
        "destination": "/user.html"
      },
      {
        "source": "**",
        "destination": "/error.html"
      }
    ]
  }
}
```

大抵のホスティングサービスは、こういった制御が可能になっていますので、各サービスのマニュアルを参照してください。

&lt;/pre&gt;
</content><link href="https://kokufu.blogspot.com/feeds/2405411123087564282/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/2405411123087564282" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/2405411123087564282" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/2405411123087564282" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2023/09/vite-rewrite.html" rel="alternate" title="[Vite] マルチページを作成した際は rewrite を正しく設定しなければならない" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-4604619579796747883</id><published>2023-01-05T17:14:00.006+09:00</published><updated>2023-01-05T17:14:53.577+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><title type="text">Android の権限ダイアログで2度「許可しない」が選択されたことを検知する方法</title><content type="html">&lt;pre class="marked"&gt;
&lt;div class="separator" style="clear: both;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRQoaPxUefi9d8EferjSPySgOr3YWXEq5MyL_xbY3c2LDrBwIDlIaApekzBIMABRZHHZ4rg6rfqOiCH4jv5ANQxvefztS_7HLsIR9p3-3b4xrrWKcv498JD-qnfVF-64WzL_xEtaWCZePrwNmPLh8cf7LrN0HgpRhTSzi2rSqSv8bSt6cuO6opf7Xn/s530/2023-01-05_16-25.png" style="display: block; padding: 1em 0; text-align: center; "&gt;&lt;img alt="" border="0" height="320" data-original-height="530" data-original-width="500" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRQoaPxUefi9d8EferjSPySgOr3YWXEq5MyL_xbY3c2LDrBwIDlIaApekzBIMABRZHHZ4rg6rfqOiCH4jv5ANQxvefztS_7HLsIR9p3-3b4xrrWKcv498JD-qnfVF-64WzL_xEtaWCZePrwNmPLh8cf7LrN0HgpRhTSzi2rSqSv8bSt6cuO6opf7Xn/s320/2023-01-05_16-25.png"/&gt;&lt;/a&gt;&lt;/div&gt;

Android 6.0 (API Level 23) 以降、マイクの使用許可などプライバシーに関わるような権限はアプリの中から個別に許可を求めなければいけなくなりました。具体的には以下のようなコードを実行すると上記のようなダイアログが表示されます&lt;sup&gt;&lt;span class='barenote'&gt;`AndroidManifest.xml` に `uses-permission` の記述も必要&lt;/span&gt;&lt;/sup&gt;。（`permissionLauncher` の実装は後半を参照）
```kotlin
val granted = ContextCompat.checkSelfPermission(
    requireContext(), Manifest.permission.RECORD_AUDIO
)
if (granted != PackageManager.PERMISSION_GRANTED) {
    permissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
}
```

以前は毎回このコードを実行することでダイアログを表示することが出来ていたのですが、Android 11 (API Level 30) 以降、ユーザーが2度「許可しない」を選択すると、次からはダイアログが表示されなくなってしまいました。

&lt;a name='more'&gt;&lt;/a&gt;

自分がユーザーの立場からすると、今は使いたくないからとりあえず「許可しない」を選択しておくことは結構あると思うのです。しかし、いざ使いたいと思った時に反応しなくなるのは困ります。
これを回避する方法は無いものかと調べてみたのですが、結論から言うと回避する方法はなさそうです。

では、どういう実装にするのが良いのでしょうか？少し調べてみました。

&gt; 参考
&gt; 
&gt; [アプリの権限をリクエストする　|  Android Developers](https://developer.android.com/training/permissions/requesting#handle-denial) 
&gt; 
&gt; [how to determine if the user checked "Never ask again"? | Stack Overflow](https://stackoverflow.com/a/31925748)

以上の情報をまとめると、 `ActivityResultCallback` の中で `shouldShowRequestPermissionRationale` を確認して処理を分けるのが良さそうです。

```kotlin
private val permissionLauncher = 
    registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted -&gt;
        if (!granted) {
            if (shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)) {
                // 普通の「許可しない」が選択された
                // 独自ダイアログ等を表示してパーミッションがなぜ必要なのかを説明する
            } else {
                // 「許可しない（次回から表示しない）」が選択された
                // Preference に保存するなどする
            }
        }
    }
```

開発者の視点からすると、 `shouldShowRequestPermissionRationale` の値に関わらず独自ダイアログ等を表示して何とか許可させたいと思うのですが、それはこの変更の意図を無視した方法なのでお勧めできません。

例えば Preferences に保存する等、2度拒否されたことを記録するだけにし、その情報を元にして以下のような施策を入れるのが良さそうです。
- アイコンを非表示にする
- アイコンを設定画面に移す（それをクリックしたときは独自ダイアログ）
- 設定画面等に説明文とともに「アプリ情報」へのリンクを表示する

なお、「アプリ情報」画面への遷移は以下のようにリクエストすることができます。

```kotlin
val intent = Intent().apply {
    action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
    data = Uri.parse("package:$packageName")
}
startActivity(intent)
```
&lt;div class="separator" style="clear: both;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEir7wKdNZSRHVy7F97Z46cKtEm90hN-A4UtwAaLPHVY_4kdHPzjCKZhfsO7RZYx8_CB0IvI7daDpSgQQ4fLIfrlBLnbTt6YIbQAg7D3B6Ijv8ZtFVtLQA73UNIg0Llt5KyxebvEK8vvLHPFSXaY4rVYVkJ-_0Be4jvVheW_guTXuGH-wTBC7BpkF97t/s1920/Screenshot_20230105_170909.png" style="display: block; padding: 1em 0; text-align: center; "&gt;&lt;img alt="" border="0" height="320" data-original-height="1920" data-original-width="1080" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEir7wKdNZSRHVy7F97Z46cKtEm90hN-A4UtwAaLPHVY_4kdHPzjCKZhfsO7RZYx8_CB0IvI7daDpSgQQ4fLIfrlBLnbTt6YIbQAg7D3B6Ijv8ZtFVtLQA73UNIg0Llt5KyxebvEK8vvLHPFSXaY4rVYVkJ-_0Be4jvVheW_guTXuGH-wTBC7BpkF97t/s320/Screenshot_20230105_170909.png"/&gt;&lt;/a&gt;&lt;/div&gt;



&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/4604619579796747883/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/4604619579796747883" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4604619579796747883" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4604619579796747883" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2023/01/android-2.html" rel="alternate" title="Android の権限ダイアログで2度「許可しない」が選択されたことを検知する方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRQoaPxUefi9d8EferjSPySgOr3YWXEq5MyL_xbY3c2LDrBwIDlIaApekzBIMABRZHHZ4rg6rfqOiCH4jv5ANQxvefztS_7HLsIR9p3-3b4xrrWKcv498JD-qnfVF-64WzL_xEtaWCZePrwNmPLh8cf7LrN0HgpRhTSzi2rSqSv8bSt6cuO6opf7Xn/s72-c/2023-01-05_16-25.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-7182013645802890575</id><published>2021-07-17T17:44:00.002+09:00</published><updated>2021-07-17T17:51:55.843+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><title type="text">Android の Stroke を特定の辺だけにする</title><content type="html">&lt;pre class="marked"&gt;
[【Android】特定の辺だけにstrokeをつけたい](https://qiita.com/izumin5210/items/3123939043d2b78b4914)
にあるように、特定の辺の stroke を実現するにはコードでの実装が必要という認識だったのですが、最近になって
[InsetDrawable](https://developer.android.com/reference/kotlin/android/graphics/drawable/InsetDrawable) が XML で使用できることを知って&lt;sup&gt;&lt;span class='barenote'&gt;というより、InsetDrawable の存在を知った&lt;/span&gt;&lt;/sup&gt;、これを使えば XML だけで stroke を消せるのではないかと思い、やってみました。

&lt;a name='more'&gt;&lt;/a&gt;

```xml
&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&lt;inset android:insetBottom="-6dp"
        xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;shape&gt;
        &amp;lt;stroke
                android:width="5dp"
                android:color="#FF0000" /&amp;gt;
        &amp;lt;solid android:color="#DDDDDD" /&amp;gt;
        &amp;lt;corners
                android:topLeftRadius="15dp"
                android:topRightRadius="15dp" /&amp;gt;
    &lt;/shape&gt;
&lt;/inset&gt;
```
Bottom だけ消してみたら以下のような感じになりました。
注意点としては、inset に入れる値が `stroke` の `width` と全く同じだと、場合によっては表示されてしまう場合があること&lt;sup&gt;&lt;span class='barenote'&gt;丸め込み誤差のせいだと思われる&lt;/span&gt;&lt;/sup&gt;。少し大きめの値を指定しましょう。

&lt;div class="separator" style="clear: both;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmQb1B3QfP0RWsf7RvQq8dzCHHpmONEI1uHiPFQC3YljUD6Ii6EkJ419xExwDAvDJ39BX2jK-3VFxyekp1IqMnFKwh-AG5oolQklP3y7_apc9r1kBOu4eZEW8hE0FBb8T2wSD2I8q7VIg/s568/001.png" style="display: block; padding: 1em 0; text-align: center; "&gt;&lt;img alt="" border="0" height="400" data-original-height="568" data-original-width="359" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmQb1B3QfP0RWsf7RvQq8dzCHHpmONEI1uHiPFQC3YljUD6Ii6EkJ419xExwDAvDJ39BX2jK-3VFxyekp1IqMnFKwh-AG5oolQklP3y7_apc9r1kBOu4eZEW8hE0FBb8T2wSD2I8q7VIg/s400/001.png"/&gt;&lt;/a&gt;&lt;/div&gt;

その気になって探してみると、既に情報は出ていました。

[Open-sided Android stroke? - Stack Overflow](https://stackoverflow.com/questions/2422120/open-sided-android-stroke/32145791#32145791)

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/7182013645802890575/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/7182013645802890575" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7182013645802890575" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7182013645802890575" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2021/07/android-stroke.html" rel="alternate" title="Android の Stroke を特定の辺だけにする" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmQb1B3QfP0RWsf7RvQq8dzCHHpmONEI1uHiPFQC3YljUD6Ii6EkJ419xExwDAvDJ39BX2jK-3VFxyekp1IqMnFKwh-AG5oolQklP3y7_apc9r1kBOu4eZEW8hE0FBb8T2wSD2I8q7VIg/s72-c/001.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-8774866307917143552</id><published>2020-03-11T09:17:00.000+09:00</published><updated>2020-03-11T10:07:06.454+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><category scheme="http://www.blogger.com/atom/ns#" term="Kotlin"/><title type="text">by ViewModels() を使って ViewModel を取得する方法</title><content type="html">&lt;pre class="marked"&gt;
以前は以下のように [ViewModelProviders](https://developer.android.com/reference/androidx/lifecycle/ViewModelProviders) を使用して [ViewModel](https://developer.android.com/reference/androidx/lifecycle/ViewModel) を取得していましたが、この方法は Deprecated になりました。

```kotlin
val myViewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
```

ドキュメントを見てみると、代わりに kotlin の委譲 `by viewModels()`、もしくは `ViewModelProvider`&lt;sup&gt;&lt;span class='barenote'&gt;使い方は最後に参照として書きました&lt;/span&gt;&lt;/sup&gt; を使ってね。と書いてあります。

ところが、`by viewModels()` はどう使うのかが、何処にも書いてありません&lt;sup&gt;&lt;span class='barenote'&gt;少なくとも2020年3月10日時点で公式のドキュメントはなさそう&lt;/span&gt;&lt;/sup&gt;。

そこで、ネットの情報とコードを参考に使ってみました。

&lt;a name='more'&gt;&lt;/a&gt;

### 依存関係
`viewModels` は `fragment-ktx` というパッケージ内に入っているので&lt;sup&gt;&lt;span class='barenote'&gt;これも公式には書いてなさそう&lt;/span&gt;&lt;/sup&gt;、これを `implementation` に加えます。

```groovy
`title: "build.gradle in app";
dependencies {
    ...
    implementation 'androidx.fragment:fragment-ktx:1.2.2'
}
```

### コンパイラのバージョンを 1.8 互換に
環境によっては以下のように、JVM のターゲットバージョンが合ってない旨のエラーがでてしまいます。

"Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option"

これを解決するには、以下のように `build.gradle` に追記します。

```groovy
`title: "build.gradle in app";
android {
    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
}
```

この問題、`viewModels()` に限ったことではなく、新しめの kotlin ライブラリを使おうとすると起こります。

&gt; 参考
&gt;
&gt; [android - Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6 - Stack Overflow](https://stackoverflow.com/a/56996020)


### 基本の使い方
以下のように使用します。
```kotlin
class MainActivity : AppCompatActivity() {
    // lazy なので最初に使用する時に初期化される
    private val myViewModel by viewModels&amp;ltMyViewModel&amp;gt;()

    fun something() {
        myViewModel.users.observe(this, Observer {
            // Do something
        })
    }
}
```

### Factory 指定
Factory を指定した初期化の方法は以下。
```kotlin
private val myViewModel  by viewModels&amp;lt;MyViewModel&amp;gt;{ MyViewModelFactory() }
```

`viewModels` の実装を覗いてみると、以下のようになっています。
`Factory` を返す関数を与えられるようになっているわけですね。
```kotlin
@MainThread
inline fun &amp;lt;reified VM : ViewModel&amp;gt; ComponentActivity.viewModels(
    noinline factoryProducer: (() -&gt; Factory)? = null
): Lazy&amp;lt;VM&amp;gt; {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
```

`ViewModelLazy()` も public なクラスなので、これを直接使えば `ViewModelStoreOwner` (Activity や Fragment 等）を指定できます。
しかし、これは遅延初期化オブジェクト。どのタイミングで呼ばれるかが定かではありません&lt;sup&gt;&lt;span class='barenote'&gt;正確には、呼び出しを全部網羅して確認すれば不可能ではないけど、それだと lazy を使う意味はもうない&lt;/span&gt;&lt;/sup&gt;。
Activity や Fragment 等、ライフサイクルによっては正しく動作しないものを指定すべきではありません。

そういった用途には以下の `ViewModelProvider` を使うのが良いでしょう。

### 【参考】ViewModelProvider クラスを使う
ドキュメントには `by viewModels()` もしくは [ViewModelProvider()](https://developer.android.com/reference/androidx/lifecycle/ViewModelProvider) を使って。とかいてあります。
こちらは、`ViewModelProviders.of()` とほぼ同様なので、簡単に使えるかと思います。

```kotlin
// ViewModelProviders.of(this) とほぼ同じように使える
val myViewModel= ViewModelProvider(this).get(MyViewModel::class.java)
```

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/8774866307917143552/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/8774866307917143552" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/8774866307917143552" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/8774866307917143552" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2020/03/by-viewmodels-viewmodel.html" rel="alternate" title="by ViewModels() を使って ViewModel を取得する方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-8128419575426868770</id><published>2020-03-07T09:51:00.000+09:00</published><updated>2020-03-07T09:51:25.600+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><title type="text">Android Q で外部ストレージのファイルが開けなくなった</title><content type="html">&lt;pre class="marked"&gt;
Android Pi (API Level 28) まで正しく動いていたアプリが、Android Q 上で動作させると動かなくなってしまいました。

具体的には、External Storage にファイルを書き込もうとすると、以下のような例外が発生してしまうのです。

`java.io.FileNotFoundException: /storage/emulated/0/202003061644.zip: open failed: EACCES (Permission denied)`

Android Pi までは動作していたので、当然 `WRITE_EXTERNAL_STORAGE` と `READ_EXTERNAL_STORAGE` パーミッションは適切に&lt;sup&gt;&lt;span class='barenote'&gt;静的にも動的にも&lt;/span&gt;&lt;/sup&gt;取得しています。

&lt;a name='more'&gt;&lt;/a&gt;

### Android Q から External Storage のアクセス権が厳しくなった
調べてみると、Android Q からは External Storage に直接アクセス出来るエリアが限定されたようです。
具体的には [getExternalFilesDir()](https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String)) で取得されるディレクトリ以外はパス指定で読み書き出来ません&lt;sup&gt;&lt;span class='barenote'&gt;その代わり、パーミッションは必要なくなった&lt;/span&gt;&lt;/sup&gt;。

&gt; 参考
&gt;
&gt; [Exception 'open failed: EACCES (Permission denied)' on Android - Stack Overflow](https://stackoverflow.com/questions/8854359/exception-open-failed-eacces-permission-denied-on-android)
&gt;
&gt; [対象範囲別外部ストレージ アクセスを管理する  |  Android デベロッパー  |  Android Developers](https://developer.android.com/training/data-storage/files/external-scoped)

### 解決策（暫定）
以下のフラグを `AndroidManifest.xml" に書いておくと、API 28 までと同等のアクセス権に変わるとのこと。

```xml
`title: "AndroidManifest.xml";
&amp;lt;application
    android:requestLegacyExternalStorage="true"
```

しかし、これは暫定対応です。
来年（2020年）以降リリースのバージョンではこのフラグは動かないらしいです。

### 解決策
そもそも External Storage を使わないか、`getExternalFilesDir()`を使うようにするしかないと思われます。

なお、Media 系と Download ディレクトリは別途アクセスする方法が提供されています。

詳しくは上記、公式サイトをご参考ください。
&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/8128419575426868770/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/8128419575426868770" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/8128419575426868770" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/8128419575426868770" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2020/03/android-q.html" rel="alternate" title="Android Q で外部ストレージのファイルが開けなくなった" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-1526433689138689186</id><published>2020-03-06T19:44:00.001+09:00</published><updated>2020-03-06T19:45:54.414+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio Code"/><title type="text">VS Code のターミナルで Ctrl + p を使えるようにする</title><content type="html">&lt;pre class="marked"&gt;
[Visual Studio Code](https://code.visualstudio.com/) がどんどん便利になってきてます。
最近、[Remote Development](https://github.com/Microsoft/vscode-remote-release) を入れたのですが、遠隔マシン上のコードがさもローカルにあるかのように扱えて重宝しています。

しかし、そうなると気になるのがターミナルの使い勝手。
Remote 中は付属ターミナルの方が圧倒的に便利&lt;sup&gt;&lt;span class='barenote'&gt;既にssh接続済みのターミナルが複数作れる&lt;/span&gt;&lt;/sup&gt;なので、別途ターミナルを開くのは、あり得なく感じます。
ところが一つ問題が。Ctrl + p を押すと、"Go to File" 機能が働いて、ターミナルからフォーカスが外れてしまうのです。

&lt;a name='more'&gt;&lt;/a&gt;

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyIuqO2dYAm9Wf9rUE2xzwpfFqWQZjHFVb1jMr_tWWT1kQKWERJl9yhX5oUrXbYVcsUBbx5sAktZ97a_H9bp9I0k6Jc2YpazMNqOADgRrtpS5lS9M5OIDZTX_xlb286ADPvAAClwR3ugs/s1600/001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyIuqO2dYAm9Wf9rUE2xzwpfFqWQZjHFVb1jMr_tWWT1kQKWERJl9yhX5oUrXbYVcsUBbx5sAktZ97a_H9bp9I0k6Jc2YpazMNqOADgRrtpS5lS9M5OIDZTX_xlb286ADPvAAClwR3ugs/s640/001.png" width="640" height="135" data-original-width="848" data-original-height="179" /&gt;&lt;/a&gt;&lt;/div&gt;

私の使っている [ZSH](https://www.zsh.org/) は Ctrl + p が非常に強力で、これを使わないでシェル作業することは考えられません。
何か回避策はあるだろうと探してみたら、公式サイトにちゃんと書いてありました。

&gt; 参考
&gt;
&gt; [Integrated Terminal in Visual Studio Code](https://code.visualstudio.com/docs/editor/integrated-terminal#_forcing-key-bindings-to-pass-through-the-terminal)

`Ctrl + b`, `Ctrl + k`, `Ctrl + f` を使えるようにする方法も同様に載っています。
これらを修正するとかなり便利になりました&lt;sup&gt;&lt;span class='barenote'&gt;個人的にはデフォルト設定にしちゃえばいいのにと思う&lt;/span&gt;&lt;/sup&gt;。


### VS Code の Settings から変更する方法
Settings を開き（Ctrl + ,）、"Search settings" に `terminal.integrated.commandsToSkipShell` と入力します。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZfUwWzxIga_zfkOxYMV7l3p1D5hunMBjF1PjOCZmrIwGJHStse7nUkmuqY9gv1YNrzwIxakxdCZiEWY0i-Eh0QlJ9uZAeP-jAYOWyHnuKINRAPZbku-sTeTu5KOWKzNdjQS9WUr5BBAA/s1600/002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZfUwWzxIga_zfkOxYMV7l3p1D5hunMBjF1PjOCZmrIwGJHStse7nUkmuqY9gv1YNrzwIxakxdCZiEWY0i-Eh0QlJ9uZAeP-jAYOWyHnuKINRAPZbku-sTeTu5KOWKzNdjQS9WUr5BBAA/s640/002.png" width="640" height="375" data-original-width="967" data-original-height="567" /&gt;&lt;/a&gt;&lt;/div&gt;

Add Item を押して、`-workbench.action.quickOpen` を追加すればOK。


### 設定ファイルを直接変更する方法
[Settings files locations](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations) を参考に設定ファイルの場所を調べ、開きます。

以下の項目を追加すればOK。
```js
{
  "terminal.integrated.commandsToSkipShell": [
    "-workbench.action.quickOpen",
  ]
}
```

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/1526433689138689186/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/1526433689138689186" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1526433689138689186" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1526433689138689186" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2020/03/vs-code-ctrl-p.html" rel="alternate" title="VS Code のターミナルで Ctrl + p を使えるようにする" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyIuqO2dYAm9Wf9rUE2xzwpfFqWQZjHFVb1jMr_tWWT1kQKWERJl9yhX5oUrXbYVcsUBbx5sAktZ97a_H9bp9I0k6Jc2YpazMNqOADgRrtpS5lS9M5OIDZTX_xlb286ADPvAAClwR3ugs/s72-c/001.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-1550859611125437700</id><published>2019-11-21T13:58:00.000+09:00</published><updated>2019-11-21T13:58:34.159+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Electron"/><category scheme="http://www.blogger.com/atom/ns#" term="Raspberry Pi"/><title type="text">electron v7 以降は Raspberry Pi にデフォルトでインストールできない</title><content type="html">&lt;pre class="marked"&gt;
Raspberry Pi で electron をインストールしようとすると以下のように落ちてしまいました。

```
&gt; electron@7.1.2 postinstall /home/pi/test/node_modules/electron
&gt; node install.js

(node:5861) UnhandledPromiseRejectionWarning: HTTPError: Response code 404 (Not Found)
    at EventEmitter.&lt;anonymous&gt; (/home/pi/test/node_modules/got/source/as-stream.js:35:24)
    at EventEmitter.emit (events.js:210:5)
    at module.exports (/home/pi/test/node_modules/got/source/get-response.js:22:10)
    at ClientRequest.handleResponse (/home/pi/test/node_modules/got/source/request-as-event-emitter.js:155:5)
    at Object.onceWrapper (events.js:300:26)
    at ClientRequest.emit (events.js:215:7)
    at ClientRequest.origin.emit (/home/pi/test/node_modules/@szmarczak/http-timer/source/index.js:37:11)
    at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:583:27)
    at HTTPParser.parserOnHeadersComplete (_http_common.js:115:17)
    at TLSSocket.socketOnData (_http_client.js:456:22)
(node:5861) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:5861) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
npm WARN saveError ENOENT: no such file or directory, open '/home/pi/test/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/home/pi/test/package.json'
npm WARN test No description
npm WARN test No repository field.
npm WARN test No README data
npm WARN test No license field.

+ electron@7.1.2
added 87 packages from 91 contributors and audited 104 packages in 22.813s
found 0 vulnerabilities
```

&lt;a name='more'&gt;&lt;/a&gt;
### 対策
先に対処方法を書いておくと、以下のように `--arch=armv7l` を指定してインストールすれば大丈夫。

```bash
`gutter: false;
$ npm install --arch=armv7l electron
```

もしくは、`~/.npmrc` に以下を追加しておけばOK。
```text
`title: "~/.npmrc";
arch=armv7l
```

&gt; 参考
&gt;
&gt; [Error installing on RPi4 · Issue #20723 · electron/electron](https://github.com/electron/electron/issues/20723)


### 原因
`install.js` の中でリリースファイルをダウンロードしているのですが、ここで `process.arch` を使っています。この値は `arm` になるので、
`https://github.com/electron/electron/releases/download/v7.1.2/electron-v7.1.2-linux-arm.zip` というファイルをダウンロードしようとします。
しかし、アップロードされているファイルは `electron-v7.1.2-linux-armv7l.zip` です。（`arm` ではなく `armv7l`） 

electron 7.1.2
```javascript
`title: "electron/install.js"; first-line: 31; highlight: 37;
downloadArtifact({
  version,
  artifactName: 'electron',
  force: process.env.force_no_cache === 'true',
  cacheRoot: process.env.electron_config_cache,
  platform: process.env.npm_config_platform || process.platform,
  arch: process.env.npm_config_arch || process.arch
}).then((zipPath) =&gt; extractFile(zipPath)).catch((err) =&gt; onerror(err))
```

electron v6 ではどうだったかと言うと、以下のように `process.arch` を使っていません。

electron 6.1.4
```javascript
`title: "electron/install.js"; first-line: 30; highlight: 35;
// downloads if not cached
download({
  cache: process.env.electron_config_cache,
  version: version,
  platform: process.env.npm_config_platform,
  arch: process.env.npm_config_arch,
  strictSSL: process.env.npm_config_strict_ssl === 'true',
  force: process.env.force_no_cache === 'true',
  quiet: process.env.npm_config_loglevel === 'silent' || process.env.CI
}, extractFile)
```

`process.env.npm_config_arch` が `undefined` の場合、`download()` の中で `process.arch` を `getNodeArch()` という関数で変換します。
以下のように、`arm` は 適切なアーキテクチャに変換されるため、v6 までは正しく動いていたようです。

```javascript
function getNodeArch(arch) {
    if (arch === 'arm') {
        switch (process.config.variables.arm_version) {
            case '6':
                return uname();
            case '7':
                return 'armv7l';
            default:
                break;
        }
    }
    return arch;
}
```

&gt; 参考
&gt;
&gt; [electron/get: Download Electron release artifacts](https://github.com/electron/get)

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/1550859611125437700/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/1550859611125437700" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1550859611125437700" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1550859611125437700" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/11/electron-v7-raspberry-pi.html" rel="alternate" title="electron v7 以降は Raspberry Pi にデフォルトでインストールできない" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-5821673141099672441</id><published>2019-11-19T08:21:00.000+09:00</published><updated>2019-11-19T08:21:06.760+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="node"/><title type="text">node-ffi 公式は node 12 でビルドが通らない</title><content type="html">&lt;pre class="marked"&gt;
node から直接Cのライブラリを呼び出せる [ffi](https://www.npmjs.com/package/ffi) というライブラリを
インストールしようとしたところ、以下のようなエラーが出てインストール出来ませんでした。

```bash
`gutter: false;
$ npm install --save ffi

&gt; ref@1.3.5 install /home/yusuke/ffi_sample/node_modules/ref
&gt; node-gyp rebuild

make: Entering directory '/home/yusuke/tmp/node_modules/ref/build'
  CXX(target) Release/obj.target/binding/src/binding.o
../src/binding.cc: In function ‘Nan::NAN_METHOD_RETURN_TYPE {anonymous}::WriteObject(Nan::NAN_METHOD_ARGS_TYPE)’:
../src/binding.cc:222:43: error: no matching function for call to ‘v8::Value::BooleanValue()’
   bool persistent = info[3]-&gt;BooleanValue();

... 省略

gyp ERR! build error 
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/usr/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23)
gyp ERR! stack     at ChildProcess.emit (events.js:210:5)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12)
```

依存している [ref](https://www.npmjs.com/package/ref) というライブラリのビルドに失敗しているようです。

この `v8::Value::BooleanValue()` 等の参照関数はだいぶ前から deprecated だったようです。
そして、node v.12 でついに削除されました。
そのため、node v.12 以降ではビルドが通らなくなってしまったのです。


### 解決策（一時的な）
幸いなことに、この問題は既に PullRequest として報告されていました。

&lt;a name='more'&gt;&lt;/a&gt;

&gt; 参考
&gt;
&gt; [Support Node v12.0.0 by lxe · Pull Request #114 · TooTallNate/ref · GitHub](https://github.com/TooTallNate/ref/pull/114)

ところが、この修正にも問題があって、この修正に対する PullRequest も上がっています&lt;sup&gt;&lt;span class='barenote'&gt;そういう場合、本線にはどうマージされるんだろう…？&lt;/span&gt;&lt;/sup&gt;。
そして、`ref` だけでなく `ffi` 自体と関連するライブラリにも同じ問題があり、修正が必要です。

&gt; 参考
&gt;
&gt; [Support Node v12.0.0 by lxe · Pull Request #544 · node-ffi/node-ffi · GitHub](https://github.com/node-ffi/node-ffi/pull/544)

とまぁ、たらい回しに探していった結果、[fishg/node-ffi](https://github.com/fishg/node-ffi) 上の修正が要件をみたしているようです。

レポ指定&lt;sup&gt;&lt;span class='barenote'&gt;github の場合、ホスト名等を省略できる。知らなかった…&lt;/span&gt;&lt;/sup&gt;でインストールすれば使えます。

```bash
`gutter: false;
$ npm install "ffi@fishg/node-ffi.git#node-12"
```

しかし、[ref](https://github.com/TooTallNate/ref) や [ffi](https://github.com/TooTallNate/ref) はどちらも1年近くメンテナンスされていません。
この node v.12 に対応する PullRequest も半年以上放置。
正式に対応する日はくるのでしょうか？

そして、deprecated な関数をかなり呼んでます。
つまり、また node のバージョンが上がったらビルドが通らなくなる可能性大。

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/5821673141099672441/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/5821673141099672441" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/5821673141099672441" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/5821673141099672441" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/11/node-ffi-node-12.html" rel="alternate" title="node-ffi 公式は node 12 でビルドが通らない" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-5984713887552794565</id><published>2019-10-26T08:06:00.000+09:00</published><updated>2019-10-26T08:06:23.662+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Electron"/><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="node"/><category scheme="http://www.blogger.com/atom/ns#" term="Spectron"/><title type="text">Electron のデフォルトでは Spectron が正しく動作しない</title><content type="html">&lt;pre class="marked"&gt;
@Electron 7.0.0
@Spectron 9.0.0

[electron-quick-start](https://github.com/electron/electron-quick-start) に [mocha](https://mochajs.org/) と [spectron](https://electronjs.org/spectron) をインストールして、簡単なテストを走らせてみました。
すると、以下のようなエラーが。

```
  1) Application launch
       shows an initial window:
     TypeError: waitUntilWindowLoaded Cannot read property 'isLoading' of undefined
      at waitUntil(&lt;Function&gt;, ) - application.js:263:17
```

実際に走らせたのは以下のコードです。
&lt;a name='more'&gt;&lt;/a&gt;

```js
`highlight: 25;
const Application = require('spectron').Application
const assert = require('assert')
const electronPath = require('electron')
const path = require('path')

describe('Application launch', function () {
  this.timeout(10000)

  beforeEach(function () {
    this.app = new Application({
      path: electronPath,
      args: [path.join(__dirname, '..')]
    })
    return this.app.start()
  })

  afterEach(function () {
    if (this.app &amp;&amp; this.app.isRunning()) {
      return this.app.stop()
    }
  })

  it('shows an initial window', function () {
    // 以下の waitUntilWindowLoaded() 内で落ちてる
    return this.app.client.waitUntilWindowLoaded().getWindowCount().then(function (count) {
      assert.equal(count, 1)
    })
  })
})
```

`waitUntilWindowLoaded()` 内で問題が起こっているようですが、このコードは `Spectron` の公式ページに書かれているものです。
`waitUntilWindowLoaded()` の使用方法に問題があるとは思えません&lt;sup&gt;&lt;span class='barenote'&gt;公式に書いてあっても、ドキュメントが更新されてなく使えないということは多々あるけど…&lt;/span&gt;&lt;/sup&gt;。


### Electron の設定が原因
この問題、実は [Spectron の README.md](https://github.com/electron-userland/spectron#node-integration) にしれっと書いてありました。

&gt; The Electron helpers provided by Spectron require accessing the core Electron APIs in the renderer processes of your application. So, either your Electron application has nodeIntegration set to true or you'll need to expose a require window global to Spectron so it can access the core Electron APIs.

`nodeIntegration` が `false` だと Spectron は正しく動かないとのこと。

そして、この `nodeIntegration`、Electron 5 以降ではデフォルト `false` なのです。（理由は後述）

このことは、公式の [Issue#174](https://github.com/electron-userland/spectron/issues/174) にもあがっていて、他にも `app.electron` や `app.webContents` が `undefined` になってしまう問題があるようです。
実際、`app.webContents.executeJavaScript('1 + 2')` を呼んでみたところ、エラーになりました。

```
  1) Application launch
       shows an initial window:
     TypeError: Cannot read property 'executeJavaScript' of undefined
      at Context.&lt;anonymous&gt; (test/spec.js:24:35)
      at &lt;anonymous&gt;
      at process._tickDomainCallback (internal/process/next_tick.js:228:7)
```


### 対策
`nodeIntegration` を `true` にすれば動作します。
しかし、常に `true` にしてしまうとセキュリティリスクがあるので、以下のようにテストの時だけ `true` になるようにするのが良いかと思います。

```js
  mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: (process.env.NODE_ENV === 'test')
    }
  })
```

私の環境では `NODE_ENV` 環境変数は自動で `test` に設定されました。
ただ、これは環境依存だと思うので `package.json` に以下のように書いておくと安心です。
```js
`title: "package.json";
  "scripts": {
    "test": "NODE_ENV=test mocha"
  },
```

私の環境では公式に載っている `preload.js` 内で `window.electronRequire` を設定する方法は動きませんでした。何でだろう…


### なぜ、デフォルト false になったのか
`nodeIntegration` が `true` だと Renderer プロセスの中から Node.js の API にアクセス出来るようになります。
つまり、Renderer からローカルのファイルやその他のローカルリソースに直接アクセスできてしまうのです。

もし、この状態で悪意のある外部コンテンツを実行すると、とても危険です。
そのため、`nodeIntegration` はデフォルト `false` になったのだと思われます。

セキュリティを高めたのは良いけれど、テスト時と実行時の動作環境があまりに異なってしまうのはどうなんだろう…
&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/5984713887552794565/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/5984713887552794565" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/5984713887552794565" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/5984713887552794565" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/10/electron-spectron.html" rel="alternate" title="Electron のデフォルトでは Spectron が正しく動作しない" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-637437932537661361</id><published>2019-09-11T09:11:00.000+09:00</published><updated>2019-09-11T09:11:02.483+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SQL"/><title type="text">[SQL] ウィンドウ関数で累積和を計算する方法</title><content type="html">&lt;pre class="marked"&gt;

| id | date | profit |
|--:|-----------:|-----:|
| 0 | 2019-03-01 | 1000 | 
| 1 | 2019-03-04 |  500 | 
| 2 | 2019-04-05 | -300 | 
| 3 | 2019-05-01 |  100 | 

上記 table1 から profit の累積和（cumlative_profit）を求める QUERY は以下&lt;sup&gt;&lt;span class='barenote'&gt;私は MariaDB 10.3.10 で動作確認しましたが、「達人に学ぶSQL徹底指南書 第2版」によると、主要なDMBSで動作するとのこと。&lt;/span&gt;&lt;/sup&gt;。

```sql
SELECT *,
       SUM(profit) OVER W AS cumulative_profit 
FROM   table1
WINDOW W AS (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
ORDER BY date;
```

これは、以下のように書いたのと同等。
```sql
SELECT *,
       SUM(profit) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_profit 
FROM   table1
ORDER BY date;
```

最近読み終わったのだけど、SQL に関しては以下の本が秀逸。
もちろん、ウィンドウ関数の使い方もとてもわかりやすく書いてある。
お勧め。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&amp;t=team00-22&amp;m=amazon&amp;o=9&amp;p=8&amp;l=as1&amp;IS2=1&amp;detail=1&amp;asins=4798157821&amp;linkId=34109bcb5906b251e857c46b66a71a01&amp;bc1=000000&amp;lt1=_blank&amp;fc1=333333&amp;lc1=0066c0&amp;bg1=ffffff&amp;f=ifr"&gt;
&lt;/iframe&gt;
&lt;/div&gt;

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/637437932537661361/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/637437932537661361" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/637437932537661361" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/637437932537661361" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/09/sql.html" rel="alternate" title="[SQL] ウィンドウ関数で累積和を計算する方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-4962285657657883098</id><published>2019-06-24T08:29:00.000+09:00</published><updated>2019-06-24T08:29:00.187+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grpc"/><category scheme="http://www.blogger.com/atom/ns#" term="JAVA"/><title type="text">io.grpc.ManagedChannelProvider$ProviderNotFoundException が発生した時の対処方法</title><content type="html">&lt;pre class="marked"&gt;
[Google Photos API](https://developers.google.com/photos/) の Java クライアントライブラリを使おうとしたところ、以下のようなエラーが出てしまいました。

```
io.grpc.ManagedChannelProvider$ProviderNotFoundException: No functional channel service provider found. Try adding a dependency on the grpc-okhttp, grpc-netty, or grpc-netty-shaded artifact
```

&gt; Try adding a dependency on the grpc-okhttp, grpc-netty, or grpc-netty-shaded artifact

とのことなので `ManagedChannelProvider` の実装が必要なことはわかりました。
が、「どうやって指定するのよ…」と悩むこと小一時間。

&lt;a name='more'&gt;&lt;/a&gt;

### 解決方法
結論から言うと、特に Java のコードですることはなく、依存ライブラリを指定してやればよいだけでした。
例えば、gradle なら以下の1行を追加するだけです&lt;sup&gt;&lt;span class='barenote'&gt;grpc-netty 等でも大丈夫だと思いますが、未確認です&lt;/span&gt;&lt;/sup&gt;。

```groovy
`highlight: 2;
dependencies {
    implementation 'io.grpc:grpc-okhttp:1.21.0'
}

```

結局、エラーメッセージの指示通りにすれば良かったというオチでした。


### リフレクションを使ってた
[ManagedChannelProvider](https://github.com/grpc/grpc-java/blob/v1.21.x/api/src/main/java/io/grpc/ManagedChannelProvider.java) のコードを見ると、`ManagedChannelProvider` の実装を実行時に探していることがわかります。


```java
`first-line: 37;
  private static final ManagedChannelProvider provider = ServiceProviders.load(
      ManagedChannelProvider.class,
      HARDCODED_CLASSES,
      ManagedChannelProvider.class.getClassLoader(),
      new PriorityAccessor&lt;ManagedChannelProvider&gt;() {
        @Override
        public boolean isAvailable(ManagedChannelProvider provider) {
          return provider.isAvailable();
        }

        @Override
        public int getPriority(ManagedChannelProvider provider) {
          return provider.priority();
        }
      });
```


### 最適化している場合は注意
上記のようにリフレクションを使っているので、
ProGuard 等で最適化している場合は grpc-okhttp を最適化対象から外しておかないといけません。


&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/4962285657657883098/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/4962285657657883098" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4962285657657883098" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4962285657657883098" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/06/iogrpcmanagedchannelproviderprovidernot.html" rel="alternate" title="io.grpc.ManagedChannelProvider$ProviderNotFoundException が発生した時の対処方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-475647327477856884</id><published>2019-06-08T07:50:00.000+09:00</published><updated>2019-06-08T07:50:02.697+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="lzo"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="Windows"/><title type="text">Windows に python-lzo をインストール</title><content type="html">&lt;pre class="marked"&gt;
@Windows 10 Pro 64bit

[LZO](http://www.oberhumer.com/opensource/lzo/) というアーカイブ系ライブラリを Python でラップしてくれるパッケージに
[python-lzo](https://pypi.org/project/python-lzo/) があります。

Linux では pip で一発インストール可能なのですが、Windows ではエラーが出てしまいインストールできません。

```shell
`gutter: false;
&gt; pip install python-lzo
Collecting python-lzo
  Using cached https://files.pythonhosted.org/packages/af/60/41f17f56c920a956f1d4b9f04f9755c045b2b06b9dd933b33cdd37ab9fd7/python-lzo-1.12.tar.gz
    ERROR: Complete output from command python setup.py egg_info:
    ERROR: Traceback (most recent call last):
      File "&lt;string&gt;", line 1, in &lt;module&gt;
      File "C:\Users\yusuk\AppData\Local\Temp\pip-install-jzhjob69\python-lzo\setup.py", line 46, in &lt;module&gt;
        raise Exception("please set LZO_DIR to where the lzo source lives")
    Exception: please set LZO_DIR to where the lzo source lives
```

公式の Issue によると、これは仕様で、自身でコンパイルしないといけないようです。

&gt; 参考
&gt;
&gt; [unable to install python-lzo using "pip install python-lzo" on Windows · Issue #5 · jd-boyd/python-lzo · GitHub](https://github.com/jd-boyd/python-lzo/issues/5)

&lt;a name='more'&gt;&lt;/a&gt;

というわけで、やってみました。

### ソースコードをダウンロード
[Download](http://www.oberhumer.com/opensource/lzo/#download) よりソースコードをダウンロードします。
今回は lzo-2.10.tar.gz をダウンロードしてきました。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrM4c-cFpvytMddG50LwtEX2gDqDfUdJ7cfBcse1_YnJJTUmuYHzm_inclNOhxv1odeciA2zxJYeFv8SltGg8ugkKz59_y2M5HPzvIJgz4jCvMOkVFE_F-al1GT2DWnddb43VHbraDBgY/s1600/003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrM4c-cFpvytMddG50LwtEX2gDqDfUdJ7cfBcse1_YnJJTUmuYHzm_inclNOhxv1odeciA2zxJYeFv8SltGg8ugkKz59_y2M5HPzvIJgz4jCvMOkVFE_F-al1GT2DWnddb43VHbraDBgY/s1600/003.png" data-original-width="219" data-original-height="139" /&gt;&lt;/a&gt;&lt;/div&gt;


### 展開
先のファイルを展開します。

なお、以後のコマンドライン操作は "x64_x86 Cross Tools Command Prompt for VS 2019" 上で行っています&lt;sup&gt;&lt;span class='barenote'&gt;x86 系の人は "x86 Native..." を使えばよいはず&lt;/span&gt;, &lt;span class='barenote'&gt;VS 2019 じゃなくても大丈夫だと思います&lt;/span&gt;, &lt;span class='barenote'&gt;Visual Studio を使わないビルド方法も提供されています&lt;/span&gt;&lt;/sup&gt;。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWr-1ic6ulVSg6hKtOpKuX6zkCt4mUjvHnv9VW92KxMmInlzNneO7JMgXp7GqtIX8wpKsZ3sq__ZEs8I4dTuui2Ymq7rpCzoHRvgoegDvYGsChfByPEbj7iYNn48edsiILfI3EHiL0tf8/s1600/001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWr-1ic6ulVSg6hKtOpKuX6zkCt4mUjvHnv9VW92KxMmInlzNneO7JMgXp7GqtIX8wpKsZ3sq__ZEs8I4dTuui2Ymq7rpCzoHRvgoegDvYGsChfByPEbj7iYNn48edsiILfI3EHiL0tf8/s1600/001.png" data-original-width="383" data-original-height="203" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6frJfcbsB69TffIlDQTRKQ82jqGpbEzELgwdDDAIG2aqs8_gzRuMxn0HvlX7VYUg1G2GvuIEJ3PBLHiT38mEny4zyxoCY46r8Mdo9geQCCZ1RDwgNlyfzpuvSOaBFA9qc2ZGlH3COD3Y/s1600/002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6frJfcbsB69TffIlDQTRKQ82jqGpbEzELgwdDDAIG2aqs8_gzRuMxn0HvlX7VYUg1G2GvuIEJ3PBLHiT38mEny4zyxoCY46r8Mdo9geQCCZ1RDwgNlyfzpuvSOaBFA9qc2ZGlH3COD3Y/s640/002.png" width="640" height="335" data-original-width="979" data-original-height="512" /&gt;&lt;/a&gt;&lt;/div&gt;

ダウンロードしたフォルダに移動して、展開します。
```shell
`gutter: false;
&gt; cd %HOMEPATH%\Downloads
&gt; tar xzf lzo-2.10.tar.gz
```

現在では Windows で `tar` が使えるので、これを使わない手はないと思いますが、もちろん [7-Zip](https://sevenzip.osdn.jp/) 等その他のツールを使っても大丈夫なはずです。

&gt; 参考
&gt;
&gt; [Windows その14 - Windows上でcurlとtarコマンドが利用可能に - kledgeb](https://kledgeb.blogspot.com/2017/12/windows-14-windowscurltar.html)


### ビルド
Linux 以外のビルド方法は "B\00README.TXT" に書かれています。

基本的には対象のバッチファイルを実行すればよいだけです。

```shell
`gutter: false;
&gt; cd lzo-2.10
&gt; .\B\win32\vc.bat
...
//
// Building LZO was successful. All done.
```


### pip install
環境変数 `LZO_DIR` に先ほどビルドを実行したフォルダを指定します。

あとは、仮想環境なりユーザー環境なりに `pip install` で完了です。

```shell
&gt; LZO_DIR=%CD%
&gt; pip install python-lzo
Collecting python-lzo
  Using cached https://files.pythonhosted.org/packages/af/60/41f17f56c920a956f1d4b9f04f9755c045b2b06b9dd933b33cdd37ab9fd7/python-lzo-1.12.tar.gz
Installing collected packages: python-lzo
  Running setup.py install for python-lzo ... done
Successfully installed python-lzo-1.12
```


### 64bit 系の人は注意
"python-lzo-1.12" は 32bit 系のバイナリを想定しているようです。
64bit の Native コンパイラでコンパイルすると、うまくいかないので注意が必要です。

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/475647327477856884/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/475647327477856884" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/475647327477856884" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/475647327477856884" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/06/windows-python-lzo.html" rel="alternate" title="Windows に python-lzo をインストール" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrM4c-cFpvytMddG50LwtEX2gDqDfUdJ7cfBcse1_YnJJTUmuYHzm_inclNOhxv1odeciA2zxJYeFv8SltGg8ugkKz59_y2M5HPzvIJgz4jCvMOkVFE_F-al1GT2DWnddb43VHbraDBgY/s72-c/003.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-7219044084958719946</id><published>2019-05-16T23:22:00.002+09:00</published><updated>2019-06-06T23:40:26.584+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio Code"/><title type="text">VSCode で Ctrl が Ctrl+CapsLock になってしまう場合の対処方法</title><content type="html">&lt;pre class="marked"&gt;
Ubuntu 18.04 LTS で [Visual Studio Code](https://code.visualstudio.com) を使っていたところ、Ctrl + S を押してもセーブが出来ませんでした。

ステータスバーを見てみると "(Ctrl+CapsLock) was pressed" の文字が。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyjJHheOT2oANrkDQmcw1EcvpkwQfPHkt7L3V4IWHzcEMAa51tudFUn1wKm5YFpTBYfeArjpfj7cSuIWfJyH_ti98tt5oonpGNFFn8iVo22My5PI2PYDWkULuqFzelWQRNXsC3OpfbJdM/s1600/001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyjJHheOT2oANrkDQmcw1EcvpkwQfPHkt7L3V4IWHzcEMAa51tudFUn1wKm5YFpTBYfeArjpfj7cSuIWfJyH_ti98tt5oonpGNFFn8iVo22My5PI2PYDWkULuqFzelWQRNXsC3OpfbJdM/s1600/001.png" data-original-width="425" data-original-height="69" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;a name='more'&gt;&lt;/a&gt;

私は CapsLock キーを Ctrl キーとして使っているので、そのせいだと思われます。
ただ、他のアプリケーションではそのようなことは起こっていません。

調べてみると、既に問題としては認識されていて、解決策も提示されていました。

&gt; 参考
&gt;
&gt; [Keyboard mappings with `setxkbmap` on Linux not working · Issue #23991 · microsoft/vscode · GitHub](https://github.com/Microsoft/vscode/issues/23991#issuecomment-292336504)

具体的には `settings.json` に `"keyboard.dispatch": "keyCode"` を加えて、VSCode を再起動します。

```js
`title: "$HOME/.config/Code/User/settings.json"; gutter: false; highlight: 2;
{
    "keyboard.dispatch": "keyCode"
}
```

なお、`settings.json` の場所は現時点&lt;sup&gt;&lt;span class='barenote'&gt;VSCode 1.33.1&lt;/span&gt;&lt;/sup&gt;では `$HOME/.config/Code/User/settings.json` です。

&gt; 参考
&gt;
&gt; [Visual Studio Code User and Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations)

この問題、中で使っているキーマップに原因があるようです。
そのキーマップに変更があった場合、また別の不具合が起こる可能性がありますね。
&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/7219044084958719946/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/7219044084958719946" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7219044084958719946" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7219044084958719946" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/05/vscode-ctrl-ctrlcapslock.html" rel="alternate" title="VSCode で Ctrl が Ctrl+CapsLock になってしまう場合の対処方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyjJHheOT2oANrkDQmcw1EcvpkwQfPHkt7L3V4IWHzcEMAa51tudFUn1wKm5YFpTBYfeArjpfj7cSuIWfJyH_ti98tt5oonpGNFFn8iVo22My5PI2PYDWkULuqFzelWQRNXsC3OpfbJdM/s72-c/001.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-5291053222635037485</id><published>2019-05-14T23:15:00.004+09:00</published><updated>2019-05-14T23:17:09.907+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Mozc"/><category scheme="http://www.blogger.com/atom/ns#" term="Ubuntu"/><title type="text">Ubuntu 18.04 でキーボードレイアウトが突然変わってしまった</title><content type="html">&lt;pre class="marked"&gt;
先日のアップデート後だと思うのですが、キーボードレイアウトが英字キーボードになってしまいました。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaP6MF8npq-4JblSTNbZs1ANq4Ndwf6js5N2v17ElUNvVHlfULPDobdfi8c6q3MzWnQgHdqti6TvkpOeWN37rWOx0M0n3AQHD2ztRzqyonZX-kiu6cowtdaHugIKxpEg2HjV3-DQpkal0/s1600/%253F_003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaP6MF8npq-4JblSTNbZs1ANq4Ndwf6js5N2v17ElUNvVHlfULPDobdfi8c6q3MzWnQgHdqti6TvkpOeWN37rWOx0M0n3AQHD2ztRzqyonZX-kiu6cowtdaHugIKxpEg2HjV3-DQpkal0/s640/%253F_003.png" width="640" height="282" data-original-width="1464" data-original-height="646" /&gt;&lt;/a&gt;&lt;/div&gt;

Mozc の `Show Keyboard Layout` で確認してみると、確かに英字キーボードになっています。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu-fy2W2nwCA-59fprFimjbEBSC7_JxaDIGA0UxaI4HzlrkymHUtrXJTDBtDsdIOWFydg3Qi3g7VWqNoCfA3yBmLFkD7Z9PoZW4dR8O7JnRv7QS0b8_gH0Tpc13YHqM26sd3Ou29Q_q4w/s1600/Workspace+1_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu-fy2W2nwCA-59fprFimjbEBSC7_JxaDIGA0UxaI4HzlrkymHUtrXJTDBtDsdIOWFydg3Qi3g7VWqNoCfA3yBmLFkD7Z9PoZW4dR8O7JnRv7QS0b8_gH0Tpc13YHqM26sd3Ou29Q_q4w/s1600/Workspace+1_002.png" data-original-width="229" data-original-height="430" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;a name='more'&gt;&lt;/a&gt;

### Mozc の設定を修正する
以下の `default` の文字を
```xml
`title: "/usr/share/ibus/component/mozc.xml"; first-line:19;
&amp;lt;layout&amp;gt;default&amp;lt;/layout&amp;gt;
```

`jp` に変更。
```xml
`title: "/usr/share/ibus/component/mozc.xml"; first-line:19;
&amp;lt;layout&amp;gt;jp&amp;lt;/layout&amp;gt;
```

その後、ログオフ/ログオンで復活しました。
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLKtiTe2A2TIvVLx8ptKDBRFp4VZdg5Sqbtg6vwf113-AHTwwWwVYryZg86y_ZgsJsY-15S5otEp_hHRUhSkHKzY6txoD7nJlRlQf38ZYKUUMxaM-0U_o0vQH2aLtifER-czcC2NoLl9w/s1600/Japanese_004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLKtiTe2A2TIvVLx8ptKDBRFp4VZdg5Sqbtg6vwf113-AHTwwWwVYryZg86y_ZgsJsY-15S5otEp_hHRUhSkHKzY6txoD7nJlRlQf38ZYKUUMxaM-0U_o0vQH2aLtifER-czcC2NoLl9w/s640/Japanese_004.png" width="640" height="282" data-original-width="1464" data-original-height="646" /&gt;&lt;/a&gt;&lt;/div&gt;


### dpkg-reconfigure とは作用している項目が違う
"Ubuntu キーボードレイアウト" 等で検索すると、大抵 `sudo dpkg-reconfigure keyboard-configuration` を使う方法が紹介されています。
これはシステム全体のキーボードレイアウトに作用します。

具体的には `/etc/default/keyboard` を変更します。
```text
`title: "/etc/default/keyboard";
BACKSPACE="guess"
XKBMODEL="pc105"
XKBLAYOUT="jp"
XKBVARIANT=""
XKBOPTIONS=""
```

私の経験上、IME (Mozc) かこちらのどちらかが原因でキーボードレイアウトがおかしくなります。
上記の Mozc の修正で直らない場合はこちらも確認してみると良いでしょう&lt;sup&gt;&lt;span class='barenote'&gt;IME に Mozc 以外を使っている場合は、定かではありません&lt;/span&gt;&lt;/sup&gt;。

&gt; 参考
&gt;
&gt; [Ubuntu日本語フォーラム / キーボードレイアウトを設定してもすぐに戻ってしまう](https://forums.ubuntulinux.jp/viewtopic.php?pid=112097)

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/5291053222635037485/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/5291053222635037485" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/5291053222635037485" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/5291053222635037485" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/05/ubuntu-1804.html" rel="alternate" title="Ubuntu 18.04 でキーボードレイアウトが突然変わってしまった" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaP6MF8npq-4JblSTNbZs1ANq4Ndwf6js5N2v17ElUNvVHlfULPDobdfi8c6q3MzWnQgHdqti6TvkpOeWN37rWOx0M0n3AQHD2ztRzqyonZX-kiu6cowtdaHugIKxpEg2HjV3-DQpkal0/s72-c/%253F_003.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-10111420850228341</id><published>2019-04-03T00:31:00.000+09:00</published><updated>2019-04-03T00:31:12.593+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><category scheme="http://www.blogger.com/atom/ns#" term="Android Emulator"/><title type="text">コマンドラインから Android emulator を起動しようとしたら "Missing emulator engine ..." が出てしまった場合の対処方法</title><content type="html">&lt;pre class="marked"&gt;
emulator 28.0.25.0 (build_id 5395263) on Ubuntu 18.04


久しぶりにコマンドラインから Emulator を起動してみると以下のようなエラーが。（Android Studio から起動した場合は問題なく起動しています）

```console
`gutter: false;
$ emulator -avd Pixel_2_API_28
PANIC: Missing emulator engine program for 'x86' CPU.
```

調べてみると、2017年3月の v25.3.0 以降、emulator コマンドのパスが変わったらしい&lt;sup&gt;&lt;span class='barenote'&gt;2年もコマンドラインから Emulator 起動してなかったのか&lt;/span&gt;&lt;/sup&gt;。

&lt;a name='more'&gt;&lt;/a&gt;

&gt; 参考
&gt;
&gt; [Update Your Path For The New Android Emulator Location | Stuart Kent on Software](https://www.stkent.com/2017/08/10/update-your-path-for-the-new-android-emulator-location.html)

確認してみると、確かに `${ANDROID_HOME}/tools/emulator` 以下にパスが通っていました。

```console
`gutter: false;
$ which emulator
${ANDROID_HOME}/tools/emulator
```

というわけで、フルパス指定にしたところ、問題なく起動しました。
パスもアップデートして、問題解決です。

```console
`gutter: false;
$ ${ANDROID_HOME}/emulator/emulator -avd Pixel_2_API_28
```

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiSL1SYXaFOe6T0lAWErThX9GYA-qv1AnUbnZk0FobMZCUW92jsQRkbZdDz5VAIOhulPUCca4H0o2DBpNerP1D6btySswd-iTkcs6ts5nQFT_avaIE3MTb2Zl08mW2I3gJERSJ_EtE_nY/s1600/Selection_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiSL1SYXaFOe6T0lAWErThX9GYA-qv1AnUbnZk0FobMZCUW92jsQRkbZdDz5VAIOhulPUCca4H0o2DBpNerP1D6btySswd-iTkcs6ts5nQFT_avaIE3MTb2Zl08mW2I3gJERSJ_EtE_nY/s640/Selection_002.png" width="362" height="640" data-original-width="517" data-original-height="915" /&gt;&lt;/a&gt;&lt;/div&gt;

参考リンクの考察によると、tools 以下のバイナリが問題なのではなく、ディレクトリ構造に起因した問題のよう。
時間ができたら android sdk 等々、クリーンインストールして確認してみよう&lt;sup&gt;&lt;span class='barenote'&gt;いつになることやら…&lt;/span&gt;&lt;/sup&gt;。

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/10111420850228341/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/10111420850228341" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/10111420850228341" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/10111420850228341" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/04/android-emulator-missing-emulator-engine.html" rel="alternate" title="コマンドラインから Android emulator を起動しようとしたら &quot;Missing emulator engine ...&quot; が出てしまった場合の対処方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiSL1SYXaFOe6T0lAWErThX9GYA-qv1AnUbnZk0FobMZCUW92jsQRkbZdDz5VAIOhulPUCca4H0o2DBpNerP1D6btySswd-iTkcs6ts5nQFT_avaIE3MTb2Zl08mW2I3gJERSJ_EtE_nY/s72-c/Selection_002.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-1576601054304494838</id><published>2019-04-02T08:49:00.000+09:00</published><updated>2019-04-02T08:49:04.536+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="venv"/><title type="text">[Python] venv のプロンプトを任意のものに変更する</title><content type="html">&lt;pre class="marked"&gt;
@Python 3.6.7

Python2 系も使うことが無くなってきたので、遅ればせながら [virtualenv](https://virtualenv.pypa.io/en/latest/) から [venv](https://docs.python.org/ja/3/library/venv.html) に移行してみました。

調べてみると、venv では virtualenvwrapper のような一元管理を使うのではなく、プロジェクト直下に `venv` というディレクトリを作って管理したほうが良いと書いてあるサイトがちらほら。

&gt; 参考
&gt;
&gt; [venv: Python 仮想環境管理 - Qiita](https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e)

&lt;a name='more'&gt;&lt;/a&gt;

その方針自体は良い&lt;sup&gt;&lt;span class='barenote'&gt;virtualenvwrapper を使っている時も、環境とプロジェクトが分かれていると管理しづらいとは思っていた&lt;/span&gt;&lt;/sup&gt;のですが、そうするとシェルのプロンプトが `(venv)` になってしまいます。
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiStzwbZ_C59rCmB3FlV4zzt5BONEcUMix1nUmJwZjcBhRp7OMRZoexMte0Ar1uC4L9E49tL28vC-vybfS8Q3XEje8u8o3UXDr44mq1Wx0asFqvf4nxmf6RxY3ZIEPa22FNN5dGBt6e54k/s1600/001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiStzwbZ_C59rCmB3FlV4zzt5BONEcUMix1nUmJwZjcBhRp7OMRZoexMte0Ar1uC4L9E49tL28vC-vybfS8Q3XEje8u8o3UXDr44mq1Wx0asFqvf4nxmf6RxY3ZIEPa22FNN5dGBt6e54k/s1600/001.png" data-original-width="441" data-original-height="84" /&gt;&lt;/a&gt;&lt;/div&gt;


### プロンプトは --prompt オプションで変更できる
これだと、「どの仮想環境を使用しているのか分かりづらくらなくなって嫌だな」と思っていたところ、venv のヘルプに `--prompt PROMPT` というオプションが。

これを使うと、プロンプトを任意のものに変更できます。素晴らしい。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpHUSvi6lS5vDjYLq9yy_4QUzktfe20MtIFxVB3YR5sRzKh3T4Kt9K4xrXB0bMtRnq47DKxeLrxJjIo_7S3c9I60-TuCAAP93Lozxt1NU2jYVMrjogc_Jv_IaafBTaqI49dldgALzhoPo/s1600/002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpHUSvi6lS5vDjYLq9yy_4QUzktfe20MtIFxVB3YR5sRzKh3T4Kt9K4xrXB0bMtRnq47DKxeLrxJjIo_7S3c9I60-TuCAAP93Lozxt1NU2jYVMrjogc_Jv_IaafBTaqI49dldgALzhoPo/s1600/002.png" data-original-width="589" data-original-height="84" /&gt;&lt;/a&gt;&lt;/div&gt;


### 既に作成済のものを変える方法1
`venv/bin/activate` 内を見てみると、環境変数`PS1` を書き換えているだけなのがわかります。
ここを変更すれば、任意のプロンプトにすることが可能です。

```shell
`first-line: 57;
if [ "x(myproject) " != x ] ; then
    PS1="(myproject) ${PS1:-}"
```

ただ、以下の「既に作成済のものを変える方法1（未来）」に書いたように、将来的には `pyvenv.cfg` 内に設定が移行することが決定しています。


### 既に作成済のものを変える方法1（未来）
「プロンプトを `activate` に直接書くのではなく `pyvenv.cfg` に記述したほうがよい」という Issue が挙げられ、変更は既に本線にマージ済みのようです。

&gt; 参考
&gt;
&gt; [bpo-35661: Store the venv prompt in pyvenv.cfg by csabella · Pull Request #11440 · python/cpython · GitHub](https://github.com/python/cpython/pull/11440)

将来的に、プロンプトは `venv/bin/activate/` の中に書かれるのではなく `venv/pyvenv.cfg` 内に変数として格納されます。
Python のバージョンによって挙動が変わってくるので注意しましょう。


### 既に作成済のものを変える方法2
以下のように仮想環境を再作成するのもアリかと思います&lt;sup&gt;&lt;span class='barenote'&gt;正確には既に作成済みのものを変更するわけでは無いですが&lt;/span&gt;&lt;/sup&gt;。

```console
`gutter: false;
(venv) $ pip freeze &gt; requirements.txt
(venv) $ deactivate
$ mv venv venv.bak
$ python3 -m venv --prompt myproject venv
$ . venv/bin/activate
(myproject) $ pip install -r requirements.txt
```

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/1576601054304494838/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/1576601054304494838" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1576601054304494838" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1576601054304494838" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/04/python-venv.html" rel="alternate" title="[Python] venv のプロンプトを任意のものに変更する" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiStzwbZ_C59rCmB3FlV4zzt5BONEcUMix1nUmJwZjcBhRp7OMRZoexMte0Ar1uC4L9E49tL28vC-vybfS8Q3XEje8u8o3UXDr44mq1Wx0asFqvf4nxmf6RxY3ZIEPa22FNN5dGBt6e54k/s72-c/001.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-7081095063293797245</id><published>2019-02-27T08:06:00.000+09:00</published><updated>2019-02-27T08:06:01.867+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Ubuntu"/><title type="text">RTNETLINK answers: File exists が出てしまった時の対処方法</title><content type="html">&lt;pre class="marked"&gt;
@Ubuntu  18.04.1 LTS

一度 DHCP で IPアドレスを取得してしまったネットワークデバイスを静的アドレスに変更すると、以下のようなエラーが出ることがあります。

```console
`gutter: false;
$ sudo ifup eth0
RTNETLINK answers: File exists 
Failed to bring up eth0
```

これを解消するには、登録されているIPアドレスを一度消去してやります。

```console
`gutter: false;
$ sudo ip addr flush dev eth0
```

&gt; 参考
&gt;
&gt; [networking - Solving “RTNETLINK answers: File exists” when running ifup - Raspberry Pi Stack Exchange](https://raspberrypi.stackexchange.com/questions/13895/solving-rtnetlink-answers-file-exists-when-running-ifup/51947#51947)

&lt;/pre&gt;
</content><link href="https://kokufu.blogspot.com/feeds/7081095063293797245/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/7081095063293797245" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7081095063293797245" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7081095063293797245" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2019/02/rtnetlink-answers-file-exists.html" rel="alternate" title="RTNETLINK answers: File exists が出てしまった時の対処方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-4012603946908100390</id><published>2018-12-15T08:26:00.000+09:00</published><updated>2018-12-15T08:26:02.913+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="Ubuntu"/><title type="text">Android Studio で "Argument for @NotNull parameter 'message' of ..." が発生するようになってしまった</title><content type="html">&lt;pre class="marked"&gt;
@Android Studio 3.2.1 on Ubuntu 18.04 LTS

Android Studio を 3.2.1 にしたところ、IDE Fatal Errors が発生するようになってしまいました&lt;sup&gt;&lt;span class='barenote'&gt;Android Studio をアップデートするまでは特に問題がなかったプロジェクトです&lt;/span&gt;&lt;/sup&gt;。

具体的には以下のようなエラー。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxEubv3_J7T97CrBnmPg7SN68f8zIVD_YrxJl-2Kc_FGeUiK8xGPdz0oKiHfCGbFGQYyqwC4-Y3jNx87ITOQSnlu9PejbExGBfSP5WAHo2tcYww0dLpWnZvkyLtMD6FhY4VEoRvdXQLko/s1600/IDE+Fatal+Errors_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxEubv3_J7T97CrBnmPg7SN68f8zIVD_YrxJl-2Kc_FGeUiK8xGPdz0oKiHfCGbFGQYyqwC4-Y3jNx87ITOQSnlu9PejbExGBfSP5WAHo2tcYww0dLpWnZvkyLtMD6FhY4VEoRvdXQLko/s640/IDE+Fatal+Errors_001.png" width="640" height="288" data-original-width="1327" data-original-height="598" /&gt;&lt;/a&gt;&lt;/div&gt;

```
Argument for @NotNull parameter 'message' of com/android/tools/idea/gradle/project/sync/GradleSyncState.syncFailed must not be null
java.lang.IllegalArgumentException: Argument for @NotNull parameter 'message' of com/android/tools/idea/gradle/project/sync/GradleSyncState.syncFailed must not be null
 at com.android.tools.idea.gradle.project.sync.GradleSyncState.$$$reportNull$$$0(GradleSyncState.java)
 at com.android.tools.idea.gradle.project.sync.GradleSyncState.syncFailed(GradleSyncState.java)
 at com.android.tools.idea.gradle.project.sync.idea.IdeaSyncPopulateProjectTask.doPopulateProject(IdeaSyncPopulateProjectTask.java:135)
 at com.android.tools.idea.gradle.project.sync.idea.IdeaSyncPopulateProjectTask.populate(IdeaSyncPopulateProjectTask.java:97)
 at com.android.tools.idea.gradle.project.sync.idea.IdeaSyncPopulateProjectTask.access$000(IdeaSyncPopulateProjectTask.java:39)
 at com.android.tools.idea.gradle.project.sync.idea.IdeaSyncPopulateProjectTask$1.run(IdeaSyncPopulateProjectTask.java:86)
 at com.intellij.openapi.progress.impl.CoreProgressManager$TaskRunnable.run(CoreProgressManager.java:750)
 at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$1(CoreProgressManager.java:157)
 at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:580)
 at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:525)
 at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:85)
 at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:144)
 at com.intellij.openapi.progress.impl.CoreProgressManager$4.run(CoreProgressManager.java:395)
 at com.intellij.openapi.application.impl.ApplicationImpl$1.run(ApplicationImpl.java:305)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 at java.util.concurrent.FutureTask.run(FutureTask.java:266)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at java.lang.Thread.run(Thread.java:745)
```

&lt;a name='more'&gt;&lt;/a&gt;

Android Support plugin の中で発生していて、どうも Gradle と Android Studio の同期がとれない様子。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvAf8S1D__4g9EIRPdzGoXOp8qeE8v36_qzuKRQrRgpqoZWjsZO4T1-HBa6_b0bpTFrsfIjR8Op-QS9SjKmu6-YvVt-4oRnEGP-Sp3-Y3AYvdWH65LygZkwxwevoo-MJk071mATDQRLcc/s1600/002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvAf8S1D__4g9EIRPdzGoXOp8qeE8v36_qzuKRQrRgpqoZWjsZO4T1-HBa6_b0bpTFrsfIjR8Op-QS9SjKmu6-YvVt-4oRnEGP-Sp3-Y3AYvdWH65LygZkwxwevoo-MJk071mATDQRLcc/s1600/002.png" data-original-width="338" data-original-height="449" /&gt;&lt;/a&gt;&lt;/div&gt;

### 原因はシンボリックリンク
探してみると、同じ問題が指摘されていました。
プロジェクトパスの中にシンボリックリンクが含まれていると起こるようです。

&gt; 参考
&gt;
&gt; [linux - Android Studio 3.2.1 - Cannot sync project with gradle files: Argument for @NotNull parameter 'message' of ... must not be null - Stack Overflow](https://stackoverflow.com/a/52952148)

確かに、問題のプロジェクトのパスはシンボリックリンクを含んでいました。


### 解決策1 シンボリックリンク先のオリジナルを使う
シンボリックリンク先のオリジナルを開くと問題なく動作します。
今の所、これが一番簡単な解決策だと思われます。


### 解決策2 マウントする1
何らかの理由でどうしてもオリジナルを指定したくない場合は、シンボリックリンクを使うのではなく、マウントしてしまうという手があります。

[bindfs](https://bindfs.org/) を使えばユーザー権限で手軽にマウント出来ます。

```console
`gutter: false;
$ bindfs --no-allow-other org_path dst_path
```

### 解決策3 マウントする2
root 権限を持っているのであれば、fstab に書いてしまうという手もあります。

```text
`gutter: false; title: "/etc/fstab";
org_path   dst_path   none  bind       0       0
```

fstab の記述を間違えるとシステムが正しく起動しなくなったりします。
ご注意を。

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/4012603946908100390/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/4012603946908100390" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4012603946908100390" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4012603946908100390" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/12/android-studio-argument-for-notnull.html" rel="alternate" title="Android Studio で &quot;Argument for @NotNull parameter 'message' of ...&quot; が発生するようになってしまった" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxEubv3_J7T97CrBnmPg7SN68f8zIVD_YrxJl-2Kc_FGeUiK8xGPdz0oKiHfCGbFGQYyqwC4-Y3jNx87ITOQSnlu9PejbExGBfSP5WAHo2tcYww0dLpWnZvkyLtMD6FhY4VEoRvdXQLko/s72-c/IDE+Fatal+Errors_001.png" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-1740817149495101200</id><published>2018-12-04T08:48:00.000+09:00</published><updated>2018-12-04T08:48:00.529+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type="text">[Python] date と datetime オブジェクトの相互変換まとめ</title><content type="html">&lt;pre class="marked"&gt;
[datetime オブジェクト、Unix time、文字列の相互変換まとめ](https://kokufu.blogspot.com/2018/12/python-datetime-unix-time.html) を書いたので、ついでと言っては何ですが date と datetime の変換についてもまとめてみました。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="https://docs.google.com/drawings/d/1L9B8IM7PUe6zUI5ByiCbIAOfNM_jUjhfip-DgN8Y2q8/edit?usp=sharing"&gt;
&lt;img src="https://docs.google.com/drawings/d/e/2PACX-1vTPWvai84qdfNjldg4zj1fxHmArjvsf_DBTU-OMLOrZqjKYV7pmiPkJ8DYNOLBIm0n4Yae9J8FW9qJH/pub?w=600&amp;amp;h=4500"&gt;&lt;/a&gt;
&lt;div style="text-align: right;"&gt;&lt;a href="https://docs.google.com/drawings/d/e/2PACX-1vTPWvai84qdfNjldg4zj1fxHmArjvsf_DBTU-OMLOrZqjKYV7pmiPkJ8DYNOLBIm0n4Yae9J8FW9qJH/pub?w=1440&amp;h=1080"&gt;画像としてダウンロード&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;a name='more'&gt;&lt;/a&gt;

なお、掲載コードは以下のように import 済みとします。
```python
from datetime import datetime, date, time
import pytz
```

## 1. date と datetime の相互変換
### datetime → date
datetime から date に変換するには [dateteime.date()](https://docs.python.org/3/library/datetime.html#datetime.date) を使用します。
```python
dt_native = datetime.utcfromtimestamp(1541030400)  # native
# 2018-11-01 00:00:00

dt_native.date()
# 2018-11-01 
```

aware な datetime オブジェクトにも使用できます。当然、タイムゾーンの影響は受けます。
```python
dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('America/New_York'))  # aware
# 2018-10-31 20:00:00-04:00

dt_aware.date()
# 2018-10-31
```

### date → datetime
date から datetime に変換するには [datetime.combine()](https://docs.python.org/3/library/datetime.html#datetime.datetime.combine) を使用します。

しかし、`combine()` にタイムゾーンを指定して aware なオブジェクトを作成するのは危険です。

一度、native に変換してから aware にした方が良いでしょう。
```python
d = date(2018, 11, 1)
dt_native = datetime.combine(d, time())
print(pytz.timezone('Asia/Tokyo').localize(dt_native))
# 2018-11-01 00:00:00+09:00

# tzinfo を指定すると +9:19 になってしまう
datetime.combine(d, time(), tzinfo=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 00:00:00+09:19
```

&gt; 参考
&gt;
&gt; [[Python] datetime.combine() でタイムゾーンは指定しない方がよい | 穀風](https://kokufu.blogspot.com/2018/12/python-datetimecombine.html)


## 2. date と文字列の変換
### date → 文字列
date から文字列に変換するには [date.strftime()](https://docs.python.org/3/library/datetime.html#datetime.date.strftime) を使用します。
```python
d = date(2018, 11, 1)
d.strftime("%Y/%m/%d")
# 2018/11/01
```

### 文字列 → date
date オブジェクトには `datetime.strptime()` 相当のメソッドが用意されていません。
そのため、一度 datetime を経由してから、date に変換するしかなさそうです。

```python
date_string = "2018/11/01"
dt = datetime.strptime(date_string, "%Y/%m/%d")
dt.date()
# 2018-11-01
```

## 3. ISO フォーマット文字列と date の相互変換
[ISO 8601 形式](https://ja.wikipedia.org/wiki/ISO_8601) と date の相互変換も提供されています。
### ISO フォーマット文字列 → date
ISOフォーマット文字列から date オブジェクトの変換は [date.fromisoformat()](https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat) を使用します。

"YYY-MM-DD" のみ対応。
Python 3.7 で導入されました。
```python
date.fromisoformat("2018-11-01")
# 2018-11-01
```

### date → ISO フォーマット文字列
```python
d = date(2018, 11, 1)
d.isoformat()
# 2018-11-01
```


## 4. Unix time から date への変換
[Unix time](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93) から date オブジェクトの変換には [date.fromtimestamp()](https://docs.python.org/3/library/datetime.html#datetime.date.fromtimestamp) が使えます。

ただ、**ローカルタイムゾーンを適用**して変換されてしまうのでなるべく使わない方が良いでしょう。
```python
date.fromtimestamp(1541030400)
# 2018-11-01  (+09:00 のタイムゾーン設定されているマシンで実行した場合)
# 2018-10-31  (-05:00 のタイムゾーン設定されているマシンで実行した場合)
```

例外的に、 [datetime.now()](https://docs.python.org/3/library/datetime.html#datetime.datetime.now) 等を用いてローカルタイムを処理する場合などは使えると思います。
```python
now = datetime.now()
# 2018-11-30 10:44:35.469159

date.fromtimestamp(now.timestamp())
# 2018-11-30
```
&lt;/pre&gt;
</content><link href="https://kokufu.blogspot.com/feeds/1740817149495101200/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/1740817149495101200" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1740817149495101200" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1740817149495101200" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/12/python-date-datetime.html" rel="alternate" title="[Python] date と datetime オブジェクトの相互変換まとめ" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-1844308499931844365</id><published>2018-12-03T08:01:00.000+09:00</published><updated>2018-12-04T01:24:23.231+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type="text">[Python] datetime オブジェクト、Unix time、文字列の相互変換まとめ</title><content type="html">&lt;pre class="marked"&gt;
Python で [datetime オブジェクト]() 、 [Unix time](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93)、文字列を相互変換する方法はいろいろあります。
中には直感に反するものもあり&lt;sup&gt;&lt;span class='barenote'&gt;native になりそうなのに、ローカルのタイムゾーンを適用しちゃうとか&lt;/span&gt;&lt;/sup&gt;、
過去に1度間違えたのに、再度間違えるということもしばしば。

そこで、2度と間違えないように、個人的にベストと思う方法をまとめてみました。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="https://docs.google.com/drawings/d/1tt0jKISn4yMsFeT39SVTQRABdEOt9hpNoO7chuScgBM/edit?usp=sharing"&gt;
&lt;img src="https://docs.google.com/drawings/d/e/2PACX-1vR7VhOqeq5WpiYrywrgamgtAKtB9j9yTJEyiT2v0eaw7Y6Fm34yt1Hw-v6szpGSz2glLObjxkZ8FBjP/pub?w=600&amp;amp;h=450"&gt;&lt;/a&gt;
&lt;div style="text-align: right;"&gt;&lt;a href="https://docs.google.com/drawings/d/e/2PACX-1vR7VhOqeq5WpiYrywrgamgtAKtB9j9yTJEyiT2v0eaw7Y6Fm34yt1Hw-v6szpGSz2glLObjxkZ8FBjP/pub?w=1440&amp;h=1080"&gt;画像としてダウンロード&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

変換には様々な方法があるので、上記が唯一の方法というわけではありませんが、
なるべく、安全かつシンプルになるものを選んだつもりです。

また、変換可能でも、安全では無いと判断したものは矢印を引いていません&lt;sup&gt;&lt;span class='barenote'&gt;安全な方法をご存じの方は教えていただけると幸いです&lt;/span&gt;&lt;/sup&gt;。

&lt;a name='more'&gt;&lt;/a&gt;

以下2点は Python のバージョンによっては使用できないので、代替案を掲載しておきました。
- `datetime.timestamp()` （3.3 以降で使用可）
- `datetime.fromisoformat()` （3.7 以降で使用可）

なお、掲載コードは以下のように import 済みとします。
```python
from datetime import datetime
import pytz
```

## 1. Unix time と datetime オブジェクトの相互変換
### Unix time → datetime (aware)
Unix time から datetime オブジェクトを生成するには、[fromtimestamp()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.fromtimestamp) を使用します。

この時、`tz` は必ず指定するようにしましょう。そうしないと、**ローカルのタイムゾーンを適用した native オブジェクト** が生成されてしまいます。
例外的に、[datetime.now()](https://docs.python.org/3/library/datetime.html#datetime.datetime.now) 等でローカル時間を取得して処理する時は `tz` を指定しなくても良いかもしれません。
```python
datetime.fromtimestamp(1541030400, tz=pytz.utc)
# 2018-11-01 00:00:00+00:00

datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 09:00:00+09:00

datetime.fromtimestamp(1541030400)  # native
# 2018-11-01 09:00:00  (+09:00 のタイムゾーン設定されているマシンで実行した場合)
# 2018-10-31 20:00:00  (-05:00 のタイムゾーン設定されているマシンで実行した場合)
```

### Unix time → datetime (native)
Unix Time から native オブジェクトを生成したい場合は、[utcfromtimestamp()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.utcfromtimestamp) が使えます。
```python
datetime.utcfromtimestamp(1541030400)
# 2018-11-01 00:00:00
```

直感的には、UTC な aware オブジェクトが生成されて欲しいところ。
間違えないように気をつけましょう。

### datetime (aware) → Unix time
datetime オブジェクトから Unix time に変換する場合は、[timestamp()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.timestamp) を使用します。

この時、native オブジェクトから変換すると、**ローカルのタイムゾーンを適用して変換**されてしまいます。
こちらも、[fromtimestamp()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.fromtimestamp) と同様、[datetime.now()](https://docs.python.org/3/library/datetime.html#datetime.datetime.now) 等で取得したローカル時間の native オブジェクトを処理する時は良いかもしれませんが、基本的には使わないようにした方が良いでしょう。
```python
dt = datetime.fromtimestamp(1541030400, tz=pytz.utc)
int(dt.timestamp())
# 1541030400

dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
int(dt.timestamp())
# 1541030400

dt = datetime.utcfromtimestamp(1541030400)  # native
int(dt.timestamp())
# 1540998000  (+09:00 のタイムゾーン設定されているマシンで実行した場合)
# 1541044800  (-05:00 のタイムゾーン設定されているマシンで実行した場合)
```

### timestamp() が使えない時
[timestamp()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.timestamp) は Python 3.3 以降で導入されました。
それ以前のバージョンで Unix time に変換したい場合は、以下のように calendar を使うと良いようです。
この時、UTC の aware に変換しないと想定外の値に変換されてしまうので注意が必要です。
```python
import calendar
dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
calendar.timegm(dt.astimezone(pytz.utc).timetuple())
# 1541030400
```

&gt; 参考
&gt;
&gt; [Pythonの日付処理とTimeZone | Nekoya press](http://nekoya.github.io/blog/2013/06/21/python-datetime/)


## 2. aware と native の相互変換
### datetime (aware) → datetime (native)
aware から native にする場合は `replace(tzinfo=None)` を使用します。
```python
dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 09:00:00+09:00

dt.replace(tzinfo=None)
# 2018-11-01 09:00:00
```

### datetime (native) → datetime (aware)
native から aware に変換する時は `replace(tzinfo=xxx)` を**使ってはいけません**。
代わりに `pytz.localize()` を使用します。
```python
dt = datetime.utcfromtimestamp(1541030400)
# 2018-11-01 00:00:00

pytz.timezone('Asia/Tokyo').localize(dt)
# 2018-11-01 00:00:00+09:00
```

タイムゾーンは日付によってオフセットが変わるので&lt;sup&gt;&lt;span class='barenote'&gt;サマータイムのある地域だと特に&lt;/span&gt;&lt;/sup&gt;、このような仕様になっているのですが、
かなり紛らわしく間違いやすいので注意が必要です。

&gt; 参考
&gt; - [Pythonでdatetimeにtzinfoを付与するのにreplaceを使ってはいけない | Nekoya press](http://nekoya.github.io/blog/2013/07/05/python-datetime-with-jst/)
&gt; - [pytzの仕様が変わっている - Qiita](https://qiita.com/higitune/items/0ca244373d380cf1c060)


## 3. aware のタイムゾーンを変更する
同じ Unix time を持つ別のタイムゾーンに変換するには [astimezone()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.astimezone) を使用します。
```python
dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 09:00:00+09:00

dt.astimezone(pytz.timezone('America/New_York'))
# 2018-10-31 20:00:00-04:00
```

なお、`astimezone(None)` は native に変換するのではなく、**ローカルのタイムゾーンに変更**します。
最初、メリットがわからなかったのですが、これを使ってローカルタイムゾーンを取得するという技が紹介されていました&lt;sup&gt;&lt;span class='barenote'&gt;かなりトリッキー。普通にローカルタイムゾーンを取得出来ればそれで良いのだが…&lt;/span&gt;&lt;/sup&gt;。

&gt; 参考
&gt;
&gt; [datetime - Python: Figure out local timezone - Stack Overflow](https://stackoverflow.com/a/39079819)


## 4. 文字列と datetime オブジェクトの相互変換
書式文字列を使用して、任意の文字列と datetime を相互変換することが出来ます。

&gt; 書式文字列のディレクティブ一覧は以下
&gt;
&gt; [strftime() and strptime() Behavior](https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior)


### 文字列 → datetime
文字列から datetime オブジェクトに変換する場合は [strptime()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.strptime) を使用します。

`%z` を含む場合は aware になり、含まない場合は native になります。
なお、`%Z`（大文字）は `strptime()` では使用できないようです。

```python
datetime.strptime("2018/11/01 09:00", "%Y/%m/%d %H:%M")
# 2018-11-01 09:00:00

datetime.strptime("2018/11/01 09:00+0900", "%Y/%m/%d %H:%M%z")
# 2018-11-01 09:00:00+09:00
```

### datetime → 文字列
datetime から文字列に変換するには [strftime()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.strftime) を使用します。

native に対し `%z` を指定しても何も表示されません。
```python
dt = datetime.utcfromtimestamp(1541030400)
# 2018-11-01 00:00:00
dt.strftime("%Y/%m/%d %H:%M%z")
# 2018/11/01 00:00

dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 09:00:00+09:00
dt.strftime("%Y/%m/%d %H:%M%z")
# 2018/11/01 09:00+0900
dt.strftime("%Y/%m/%d %H:%M(%Z)")
# 2018/11/01 09:00(JST)
```


## 5. ISO フォーマット文字列と datetime オブジェクトの相互変換
[ISO 8601 形式](https://ja.wikipedia.org/wiki/ISO_8601) と datetime の相互変換も提供されています。

ただ、ISO 8601 にも基本と拡張が存在し、拡張形式にしか対応していない様子。
さらに UTC を表す "Z" には対応していないなど、使い勝手はイマイチです。

個人的には [strptime()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.strptime) と [strftime()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.strftime) を使用した方が柔軟性が高く、便利かと思います。

### ISO フォーマット文字列 → datetime オブジェクト
ISO フォーマット文字列から datetime に変換するには [fromisoformat()](https://docs.python.org/3.7/library/datetime.html#datetime.datetime.fromisoformat) を使用します。

上述のように、拡張形式にしか対応していません&lt;sup&gt;&lt;span class='barenote'&gt;セパレータは "T" に限定されず、任意の1文字で良い&lt;/span&gt;&lt;/sup&gt;。
```python
iso_string = "2018-11-01T09:00:00+09:00"
datetime.fromisoformat(iso_string)
# 2018-11-01 09:00:00+09:00
```

[fromisoformat()](https://docs.python.org/3.7/library/datetime.html#datetime.datetime.fromisoformat) は Python 3.7 で導入されたので、
それ以前の場合は [strptime()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.strptime) で代用することになります。

厄介なのは ISO フォーマットと `strptime()` のタイムゾーンの記述方法が異なる点です。
ISO は `+09:00` なのに対し、 `strptime()` は `+0900` でコロンがありません。

あまり良い方法は提供されていないようなので、愚直に置換してやるのがよさそうです

&gt; 参考
&gt;
&gt; [python - parsing timezone with colon - Stack Overflow](https://stackoverflow.com/a/45300534)

```python
iso_string = "2018-11-01T09:00:00+09:00"

# タイムゾーンのフォーマットを変更
dt_string = iso_string if ":" != iso_string[-3:-2] else iso_string[:-3]+iso_string[-2:]
# 2018-11-01T09:00:00+0900

datetime.strptime(dt_string, "%Y-%m-%dT%H:%M:%S%z")
# 2018-11-01 09:00:00+09:00
```

### datetime オブジェクト → ISO フォーマット文字列
datetime から IOSフォーマット文字列に変換するには [isoformat()](https://docs.python.org/3.6/library/datetime.html#datetime.datetime.isoformat) を使用します。

```python
dt = datetime.fromtimestamp(1541030400, tz=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 09:00:00+09:00

dt.isoformat()
# 2018-11-01T09:00:00+09:00

dt.isoformat(timespec='hours')
# 2018-11-01T09+09:00
```

`timespec` や `sep` を用いて多少フォーマットを変更できますが、上述のように全ての ISO 8601 形式をカバーしているわけではないので、使用できるシーンは限られそうです。


## 6. コンストラクタ
コンストラクタでタイムゾーンを指定すると、native に対し `replace()` したのと同等になってしまいます。

コンストラクタで `tzinfo` を指定するのではなく、native で作成してから `pytz.localize()` で aware に変換するようにしましょう。

```python
# こちらが正解
dt = datetime(2018, 9, 1)  # native
pytz.timezone('Asia/Tokyo').localize(dt)
# 2018-09-01 00:00:00+09:00

# 以下のようにコンストラクタで tzinfo を指定すると19分というずれが発生
datetime.datetime(2018, 9, 1, tzinfo=pytz.timezone('Asia/Tokyo'))
# 2018-09-01 00:00:00+09:19
```

&gt; 参考
&gt;
&gt; - [[Python] datetime のコンストラクタでタイムゾーンは指定しない方がよい](https://kokufu.blogspot.com/2018/11/python-datetime.html)
&gt; - [pytzの仕様が変わっている - Qiita](https://qiita.com/higitune/items/0ca244373d380cf1c060)
&gt; - [Pythonでdatetimeにtzinfoを付与するのにreplaceを使ってはいけない | Nekoya press](http://nekoya.github.io/blog/2013/07/05/python-datetime-with-jst/)

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/1844308499931844365/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/1844308499931844365" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1844308499931844365" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/1844308499931844365" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/12/python-datetime-unix-time.html" rel="alternate" title="[Python] datetime オブジェクト、Unix time、文字列の相互変換まとめ" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-6108183827953144365</id><published>2018-12-02T00:58:00.000+09:00</published><updated>2018-12-02T00:58:00.282+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type="text">[Python] datetime.combine() でタイムゾーンは指定しない方がよい</title><content type="html">&lt;pre class="marked"&gt;
先日、「[datetime のコンストラクタでタイムゾーンは指定しない方がよい](https://kokufu.blogspot.com/2018/11/python-datetime.html)」と書いたのですが、
`datetime.combine()` でも同様の問題が起こりました。

以下のように、日本のタイムゾーンが `+9:19` になってしまうのです。
```python
from datetime import datetime, date, time
import pytz

d = date(2018,11,1)
datetime.combine(d, time(), tzinfo=pytz.timezone('Asia/Tokyo'))
# 2018-11-01 00:00:00+09:19
```

&lt;a name='more'&gt;&lt;/a&gt;

コンストラクタと同様、一度 native で作成してから aware に変換するのが良さそうです。
```python
`first-line: 4;
d = date(2018,11,1)
dt = datetime.combine(d, time())
pytz.timezone('Asia/Tokyo').localize(dt)
# 2018-11-01 00:00:00+09:00
```
&lt;/pre&gt;
</content><link href="https://kokufu.blogspot.com/feeds/6108183827953144365/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/6108183827953144365" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/6108183827953144365" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/6108183827953144365" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/12/python-datetimecombine.html" rel="alternate" title="[Python] datetime.combine() でタイムゾーンは指定しない方がよい" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-4622912215630305457</id><published>2018-12-01T00:49:00.000+09:00</published><updated>2018-12-04T01:25:13.966+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type="text">[Python] サマータイムのある地域で時刻計算する時は、一度 UTC に変換する</title><content type="html">&lt;pre class="marked"&gt;
「サマータイムのある地域で」とタイトルに入れたのですが、地域に関わらず、時刻計算する時は UTC に変換してから行った方が良いです。
特にサマータイムのある地域では問題が起こりやすいので、注意が必要ということです。


### 問題が起こる例
2018年の ニューヨークは 11/4 2:00 am までがサマータイム&lt;sup&gt;&lt;span class='barenote'&gt;アメリカでは Daylight Saving Time という&lt;/span&gt;&lt;/sup&gt;でした。

```
2018/11/4 2:00 am より前
EDT -04:00

2018/11/4 1:00 am 以降
EST -05:00
```

このような時刻をまたぐ計算を aware のまま行うと、結果がおかしくなります。

```python
&gt;&gt;&gt; import datetime, pytz
&gt;&gt;&gt; dt = datetime.datetime(2018, 11, 4, 7, 0, 0, 0)  # 2018/11/4 7:00 am native
&gt;&gt;&gt; dt_aware = pytz.timezone('America/New_York').localize(dt)
&gt;&gt;&gt; dt_aware
datetime.datetime(2018, 11, 4, 7, 0, tzinfo=&lt;DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD&gt;)
&gt;&gt;&gt; str(dt_aware)
'2018-11-04 07:00:00-05:00'
```

上記の `dt_aware` から 1日前を計算すると以下のようになります。

```python
&gt;&gt;&gt; calculated = dt_aware - datetime.timedelta(days=1)
&gt;&gt;&gt; calculated
datetime.datetime(2018, 11, 3, 7, 0, tzinfo=&lt;DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD&gt;)
&gt;&gt;&gt; str(calculated)
'2018-11-03 07:00:00-05:00'
```

ぱっと見正しそうですが、11月3日なのに EST になっていて、間違っています。

&lt;a name='more'&gt;&lt;/a&gt;

これを回避する方法を以下に挙げておきます。
基本的にはタイトルにあるように、UTC に変換するのが一番ですが、全てのケースで使えるわけではありません。

なお、タイムゾーンをコンストラクタで指定すると結果がおかしくなるので、必ず `localize()` を使用します。
&gt; 参考
&gt;
&gt; [[Python] datetime のコンストラクタでタイムゾーンは指定しない方がよい | 穀風](https://kokufu.blogspot.com/2018/11/python-datetime.html)

### 回避策1: もう一度 タイムゾーンを適用する
`datetime` オブジェクトの中の [Unix time](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93) は保持されているので、タイムゾーンを再度適用すると正しい結果になります。

```python
&gt;&gt;&gt; calculated2 = calculated.astimezone(pytz.timezone('America/New_York'))
&gt;&gt;&gt; calculated2
datetime.datetime(2018, 11, 3, 8, 0, tzinfo=&lt;DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST&gt;)
&gt;&gt;&gt; str(calculated2)
'2018-11-03 08:00:00-04:00'
```

これでも大抵の場合はうまくいきます。
ただ、本来存在しない時刻が途中に出てくるのがイマイチです。


### 回避策2: UTC に変換してから計算する
計算する時には必ず UTC にし&lt;sup&gt;&lt;span class='barenote'&gt;原理的にはオフセットが変わらないタイムゾーンなら何でも良い。ただ、そんなことが保証されているのは UTC だけだと思う。&lt;/span&gt;&lt;/sup&gt;、その後、再度タイムゾーンを適用します。

```python
&gt;&gt;&gt; dt_aware_utc = dt_aware.astimezone(pytz.utc)
&gt;&gt;&gt; dt_aware_utc
datetime.datetime(2018, 11, 4, 12, 0, tzinfo=&lt;UTC&gt;)
&gt;&gt;&gt; calculated_utc = dt_aware_utc - datetime.timedelta(days=1)
&gt;&gt;&gt; calculated_utc
datetime.datetime(2018, 11, 3, 12, 0, tzinfo=&lt;UTC&gt;)
&gt;&gt;&gt; calculated3 = calculated_utc.astimezone(pytz.timezone('America/New_York'))
&gt;&gt;&gt; calculated3
datetime.datetime(2018, 11, 3, 8, 0, tzinfo=&lt;DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST&gt;)
&gt;&gt;&gt; str(calculated3)
'2018-11-03 08:00:00-04:00'
```

少々面倒ですが、常に UTC で計算するようにすると、タイムゾーンに関する諸問題を適切に回避できる方法かと思います。


### 回避策3: native に変換してから計算する
大抵の場合は UTC に変換する方法を使用しておけば良いと思います。

ただ、UTC に直す方法がうまく機能しない場合があります。
例えば、「その地域で3日前の17時が Unix time ではどう表されるか」を知りたい場合等&lt;sup&gt;&lt;span class='barenote'&gt;かなり限られた条件だが、実際にそういう需要があったので&lt;/span&gt;&lt;/sup&gt;です。

そういった場合は、native にしてしまうという方法があります。

```python
&gt;&gt;&gt; dt_native = dt_aware.replace(tzinfo=None)  # dt と同等
&gt;&gt;&gt; dt_native
datetime.datetime(2018, 11, 4, 7, 0)
&gt;&gt;&gt; calculated_native = dt_native - datetime.timedelta(days=3)
&gt;&gt;&gt; calculated_native
datetime.datetime(2018, 11, 1, 7, 0)
&gt;&gt;&gt; calculated_native2 = calculated_native.replace(hour=17, minute=0, second=0, microsecond=0)
&gt;&gt;&gt; calculated_native2
datetime.datetime(2018, 11, 1, 17, 0)
&gt;&gt;&gt; calculated4 = pytz.timezone('America/New_York').localize(calculated_native2)
&gt;&gt;&gt; calculated4
datetime.datetime(2018, 11, 1, 17, 0, tzinfo=&lt;DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST&gt;)
&gt;&gt;&gt; str(calculated4)
'2018-11-01 17:00:00-04:00'
&gt;&gt;&gt; int(calculated4.timestamp())  # Unix time (Python 3.3 以降)
1541106000
```

かなり面倒ですね。

この場合は native な datetime オブジェクトではなく、date オブジェクトに直して、また datetime オブジェクトに戻すという手もつかえます&lt;sup&gt;&lt;span class='barenote'&gt;これも、結構面倒&lt;/span&gt;&lt;/sup&gt;。
また、最初に書いたタイムゾーンを適用し直すという方法も使えますが、タイムゾーンを適用するタイミングによって結果が変わるので注意が必要です。

### まとめ
どの方法を使うせよ、**計算結果に対し自動的にオフセットが変わることは無い** ということを念頭においておくと良いでしょう。

その上で、基本的には UTC を基準にした aware で計算するのがベスト&lt;sup&gt;&lt;span class='barenote'&gt;サマータイムのあるなしに関わらず、計算する時は UTC に直すのがベストプラクティス&lt;/span&gt;&lt;/sup&gt;。

native を扱わなければいけないケースもありますが、タイムゾーンが落ちることでミスが発生しやすくなります。
どうしても必要な場合、変換はギリギリまで行わず、計算後は aware&lt;sup&gt;&lt;span class='barenote'&gt;なるべく UTC&lt;/span&gt;&lt;/sup&gt; に戻すようにすると良いかと思います。
&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/4622912215630305457/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/4622912215630305457" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4622912215630305457" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4622912215630305457" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/12/python-utc.html" rel="alternate" title="[Python] サマータイムのある地域で時刻計算する時は、一度 UTC に変換する" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-7279184381791784365</id><published>2018-11-30T07:53:00.000+09:00</published><updated>2018-12-18T07:15:44.956+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><title type="text">[Python] datetime のコンストラクタでタイムゾーンは指定しない方がよい</title><content type="html">&lt;pre class="marked"&gt;
以下を見てください。
日本のタイムゾーンが `+9:19` になってしまいました。

```python
&gt;&gt;&gt; import datetime, pytz
&gt;&gt;&gt; datetime.datetime(2018, 9, 1, tzinfo=pytz.timezone('Asia/Tokyo'))
datetime.datetime(2018, 9, 1, 0, 0, tzinfo=&lt;DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD&gt;)
```

調べてみると、pytz 2017.2 以降を使う場合、上記のような動作になるようです。

原因については、以下のコメント欄が参考になります。（以下の記事で触れているのは `replace` でタイムゾーン指定した場合ですが、コンストラクタで指定した場合も同様)

&gt; 参考
&gt;
&gt; [pytzの仕様が変わっている - Qiita](https://qiita.com/higitune/items/0ca244373d380cf1c060)


簡単に言うと、「複数のタイムゾーン（UTC オフセット）を持つエリアでは、日付が決まらないとオフセットが決定できないので、
勝手にデフォルトのオフセットが採用される」ということのようです。

一般的に「複数のタイムゾーンを持つエリア」といえば、サマータイムがあるようなエリアで、日本は当てはまらない気がします。
ところが、日本は 1887年以前は今とは異なるタイムゾーン (`+09:18:59`&lt;sup&gt;&lt;span class='barenote'&gt;9:19:00 ではない。pytz は秒まで持てないからね…&lt;/span&gt;&lt;/sup&gt;) だったそうです。

&gt; 参考
&gt;
&gt; [一行入魂　PostgreSQLのtime zoneについて](http://aoyagikouhei.blog8.fc2.com/blog-entry-61.html)

pytz 2017.2 以降はこれを厳密に守るようになったということみたいですね&lt;sup&gt;&lt;span class='barenote'&gt;とはいえ、100年以上前のタイムゾーンがデフォルトってどうなのよ？と思いますが…&lt;/span&gt;&lt;/sup&gt;。

というわけで、aware な datetime オブジェクトを作成するには、
一度 native で作成してから aware に変換するのが正しいやり方のようです。

```python
&gt;&gt;&gt; import datetime, pytz
&gt;&gt;&gt; dt = datetime.datetime(2018, 9, 1)
&gt;&gt;&gt; pytz.timezone('Asia/Tokyo').localize(dt)
datetime.datetime(2018, 9, 1, 0, 0, tzinfo=&lt;DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD&gt;)
```

今回、たまたま日本のタイムゾーンで問題が起こって調べましたが、他のタイムゾーン&lt;sup&gt;&lt;span class='barenote'&gt;サマータイムがあるなど&lt;/span&gt;&lt;/sup&gt;では同じ問題が前からあったということですね&lt;sup&gt;&lt;span class='barenote'&gt;多分。確認はしていないけど。&lt;/span&gt;&lt;/sup&gt;。

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/7279184381791784365/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/7279184381791784365" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7279184381791784365" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/7279184381791784365" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/11/python-datetime.html" rel="alternate" title="[Python] datetime のコンストラクタでタイムゾーンは指定しない方がよい" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6154025142032985882.post-4687820215713976230</id><published>2018-11-22T07:48:00.000+09:00</published><updated>2018-11-22T07:48:05.513+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="GNOME"/><category scheme="http://www.blogger.com/atom/ns#" term="Nautilus"/><title type="text">GNOME 3 でマウントドライブをデスクトップに表示しないようにする方法</title><content type="html">&lt;pre class="marked"&gt;
@GNOME 3.28.2

gvfs でいろいろマウントしているとデスクトップがゴチャゴチャになってくる&lt;sup&gt;&lt;span class='barenote'&gt;アイコンが被ったりする。バグだよなぁ…&lt;/span&gt;&lt;/sup&gt;ので、アイコンを表示しないように出来ないか調べてみた。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPPBNvjxAY9v-UDQRjmPqMva8Z9yrSyhpUO7jrzF7E0WyAelOWWLGyp73VlWp0A8c4TfBg9Hu4cXxWMRQ79OE3OIz-lY_9ZJeXn2y5QXpUdWDhtWJER-3T50-cI3XTuXcpSzaFgL5h59A/s1600/Selection_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPPBNvjxAY9v-UDQRjmPqMva8Z9yrSyhpUO7jrzF7E0WyAelOWWLGyp73VlWp0A8c4TfBg9Hu4cXxWMRQ79OE3OIz-lY_9ZJeXn2y5QXpUdWDhtWJER-3T50-cI3XTuXcpSzaFgL5h59A/s1600/Selection_001.png" data-original-width="141" data-original-height="232" /&gt;&lt;/a&gt;&lt;/div&gt;

結論から言うと、dconf の `/org/gnome/nautilus/desktop/volumes-visible` を OFF にすればよい。

&lt;a name='more'&gt;&lt;/a&gt;

`dconf-editor` を使えば簡単。

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV-Z6TpASaBF3S9OzKUxakPQ2i6EVW89CTV8urOML7_QLUpcRPcdzkSpTMAxygYv2zgksSFKgVHI55h-NnucElhVuwOkrznIv2f-llIj-_7TMMGO7mk_JsgrvdlCQPv2klRHnrRfA5Pxw/s1600/dconf+Editor_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV-Z6TpASaBF3S9OzKUxakPQ2i6EVW89CTV8urOML7_QLUpcRPcdzkSpTMAxygYv2zgksSFKgVHI55h-NnucElhVuwOkrznIv2f-llIj-_7TMMGO7mk_JsgrvdlCQPv2klRHnrRfA5Pxw/s1600/dconf+Editor_002.png" data-original-width="630" data-original-height="420" /&gt;&lt;/a&gt;&lt;/div&gt;

なお、`gsettings` 等で変更したい場合、 schema id は `org.gnome.nautilus.desktop` 。

&gt; 参考
&gt;
&gt; - [GSettings と dconf の関係 | 穀風](https://kokufu.blogspot.com/2018/11/gsettings-dconf.html)
&gt; - [dconf の schema id と schema path の関係 | 穀風](https://kokufu.blogspot.com/2018/11/dconf-schema-id-schema-path.html)

&lt;/pre&gt;
&lt;div class='barenote_ref_list'&gt;&lt;/div&gt;</content><link href="https://kokufu.blogspot.com/feeds/4687820215713976230/comments/default" rel="replies" title="コメントの投稿" type="application/atom+xml"/><link href="https://www.blogger.com/comment/fullpage/post/6154025142032985882/4687820215713976230" rel="replies" title="0 件のコメント" type="text/html"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4687820215713976230" rel="edit" type="application/atom+xml"/><link href="https://www.blogger.com/feeds/6154025142032985882/posts/default/4687820215713976230" rel="self" type="application/atom+xml"/><link href="https://kokufu.blogspot.com/2018/11/gnome-3.html" rel="alternate" title="GNOME 3 でマウントドライブをデスクトップに表示しないようにする方法" type="text/html"/><author><name>Yusuke</name><uri>http://www.blogger.com/profile/01684623000819625562</uri><email>noreply@blogger.com</email><gd:image height="32" rel="http://schemas.google.com/g/2005#thumbnail" src="//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiquu1685bSenxd0V6J4yvcDUazx1i_-RXZ3mHILzptpdAadwhXL6RpRbkJhVr-VrP7UHwR1C7mRJI9SOtd0MVLuJUrViIjX3yx1naUiblUPwk_ipnm72-mmGf7QELWnl0/s1600/ACg8ocKlDAVFDimnAUyl7Ji05wSkUNwgbwPLydcKfPbsqMxx20o%253Ds500-c" width="32"/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPPBNvjxAY9v-UDQRjmPqMva8Z9yrSyhpUO7jrzF7E0WyAelOWWLGyp73VlWp0A8c4TfBg9Hu4cXxWMRQ79OE3OIz-lY_9ZJeXn2y5QXpUdWDhtWJER-3T50-cI3XTuXcpSzaFgL5h59A/s72-c/Selection_001.png" width="72"/><thr:total>0</thr:total></entry></feed>