Open46

Tips

Hiroyuki KomatsuHiroyuki Komatsu

Qt を使った macOS アプリで "cocoa" が見つからないというエラーが出る

Qt を同梱する macOS アプリで、下記の「"cocoa" が見つからない」というエラーが出る時の対応方法。

qt.qpa.plugin: Could not find the Qt platform plugin "cocoa" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

QtCore, QtGui, QtPrintSupport, QtWidgets の Frameworks に加えて、 qt.conf と libqcocoa.dylib を Resources に配置する必要がある。

  • MyApp.app/Contents/
    • Frameworks/
      • QtCore.framework/
      • QtGui.framework/
      • QtPrintSupport.framework/
      • QtWidgets.framework/
    • Resources/
      • plugins/platforms/libqcocoa.dylib
      • qt.conf
qt.conf の中身
[Paths]
Plugins = Resources/plugins
Hiroyuki KomatsuHiroyuki Komatsu

C++ で桁数以上のビットシフトの返り値は、ゼロではなく不定

static_cast<uint32_t>(1) << 31;  // 0b1000...0000 (0 が 31 個) 期待通り
static_cast<uint32_t>(1) << 32;  // 0 にならない。不定
static_cast<uint32_t>(1) << 33;  // 0 にならない。不定
static_cast<uint64_t>(1) << 63;  // 0b1000...0000 (0 が 63個) 期待通り
static_cast<uint64_t>(1) << 64;  // 0 にならない。不定
static_cast<uint64_t>(1) << 65;  // 0 にならない。不定

Colab の clang で試してみると不定の方は実行のたびに値が変わる。

そのため下記のコードは期待通りに動かない。

// index よりも小さい桁にあるビット数を数える意図の関数だが、
// index = 0 の時の返り値が不定になるバグがある
int PopCountRhs(uint64_t bitmap, int index) {
  return popcount(bitmap << (64 - index));  // INVALID CODE
}

参考:
c++ - Why doesn't left bit-shift, "<<", for 32-bit integers work as expected when used more than 32 times? - Stack Overflow

Hiroyuki KomatsuHiroyuki Komatsu

Python で入出力を継続するコマンドをラップする方法

シェルのような入力と出力を交互に繰り返すコマンドを、Python から制御するためのサンプルコード。

Stack overflow の Timeout on subprocess readline in Python を参考にした。

import asyncio
import sys
from asyncio.subprocess import PIPE

async def run_shell():
  process = await asyncio.create_subprocess_exec(
    '/bin/sh', stdin=PIPE, stdout=PIPE)

  while process.returncode is None:
    command = input('> ')
    process.stdin.write(command.encode('utf-8') + b'\n')

    while True:
      try:
        line = await asyncio.wait_for(process.stdout.readline(), timeout=0.1)
      except asyncio.TimeoutError:
        break

      if not line:
        break

      print(line.decode('utf-8'), end='')

  return await process.wait()


if sys.platform == 'win32':
  loop = asyncio.ProactorEventLoop()
  asyncio.set_event_loop(loop)
else:
  loop = asyncio.get_event_loop()


returncode = loop.run_until_complete(run_shell())
loop.close()

0.1 秒間出力がない場合に、入力を受け取るようにしている。

もっと正しい方法があるとは思う。

Hiroyuki KomatsuHiroyuki Komatsu

Python でコマンドの出力を取得しつつ、エンターキーで終了する方法

上記 Python で入出力を継続するコマンドをラップする方法 から、非同期で標準入力を受け取れるように変更。

import asyncio
import concurrent.futures
import subprocess
import sys
import threading
from asyncio.subprocess import PIPE


is_terminate = threading.Event()


async def check_interuption(loop):
  reader = asyncio.StreamReader()
  protocol = asyncio.StreamReaderProtocol(reader)
  await loop.connect_read_pipe(lambda: protocol, sys.stdin)

  line = await reader.readline()
  print('read:' + line.decode('utf-8'))
  is_terminate.set()


async def run_command(args):
  process = await asyncio.create_subprocess_exec(*args, stdout=PIPE)
  output = []

  while not is_terminate.is_set():
    try:
      line = await asyncio.wait_for(process.stdout.readline(), timeout=0.1)
    except asyncio.TimeoutError:
      continue

    line = line.decode('utf-8')
    output.append(line)
    print(line, end='')

  return ''.join(output)


def get_event_loop():
  if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
  else:
    loop = asyncio.get_event_loop()
  return loop


loop = get_event_loop()

args = ['/usr/local/bin/adb', 'shell', 'getevent', '/dev/input/event1']
futures = asyncio.gather(run_command(args), check_interuption(loop))
output = loop.run_until_complete(futures)[0]

print(output)
loop.close()

参考: python - Listen to keypress with asyncio - Stack Overflow

Hiroyuki KomatsuHiroyuki Komatsu

Qt で最前面でフォーカスを取らない枠なしウインドウを作成する

  QWidget window;
  window.setWindowFlags(Qt::ToolTip |
                        Qt::FramelessWindowHint |
                        Qt::WindowStaysOnTopHint);
Hiroyuki KomatsuHiroyuki Komatsu

MSVC で "cannot subtract incompatible string_view iterators." というエラーが出る

string_viewbegin()end() で引き算をしているのが原因。 data() および data() + size() を使う。

std::string_view sv1(char_pointer);
std::string_view sv2(char_pointer + 5);

// BAD
// int offset = sv2.begin() - sv1.begin();

// GOOD
int offset = sv2.data() - sv1.data();

参照: STL/xstring at main · microsoft/STL

Hiroyuki KomatsuHiroyuki Komatsu

Git のサブモジュールを特定のタグに更新する

cd <submodule_dir>
git fetch --tags
git checkout tags/<tag_name>
git submodule update

cd <main_dir>
git add <submodule_dir>
git commit
Hiroyuki KomatsuHiroyuki Komatsu

サブモジュールが更新された remote_repo を local_repo へ pull したら、local_repo のサブモジュールをアップデートする必要がある。

git submodule update
Hiroyuki KomatsuHiroyuki Komatsu

サブモジュール内で使用しているサブモジュールを更新するために、
サブモジュール内で git checkout tags/<tag_name> のあとに
git submodule update をする必要がある。

Hiroyuki KomatsuHiroyuki Komatsu

Mac の Android Studio でエミュレーターが Not responding で起動しない。

データ消去 & 再インストールが確実らしい。…なるほど?

  1. AVD Manager から Virtual Devices をすべて消す
  2. [Android Studio] > [Preference...] > [Appearance & Behavior] > [System Settings] > [Android SDK] を選ぶ
  3. [SDK Tools] > [Android Emulator] の項目を外してアンインストールする
  4. Android Studio の再起動
  5. [SDK Tools] > [Android Emulator] の項目を加えて再インストールする
  6. AVD Manager に Virtual Devices を追加する

参考 Android Emulator not working on MAC - Stack Overflow

Hiroyuki KomatsuHiroyuki Komatsu

Qt でスレッド間通信 (QThread + connect)

my_qt_thread.h
#include <QtWidgets/QtWidgets>

class Receiver : public QObject {
  Q_OBJECT

 public:
  void HandleValue(int i);
};

class Sender : public QObject {
  Q_OBJECT

 public:
  Sender(Receiver *receiver);

  void RunLoop(int times);
  void SetValue(int i);

 signals:
  void EmitRunLoop(int times);
  void EmitValue(int i);
};
my_qt_thread.cc
#include "my_qt_thread.h"

#include <unistd.h>
#include <iostream>

Sender::Sender(Receiver *receiver) {
  connect(this, &Sender::EmitValue, receiver, &Receiver::HandleValue);
  connect(this, &Sender::EmitRunLoop, this, &Sender::RunLoop);
}

void Sender::RunLoop(int times) {
  for (int i = 0; i < times; ++i) {
    sleep(1);
    SetValue(i);
  }
}

void Sender::SetValue(int i) {
  std::cout << "Sender::SetValue: " << i << std::endl;
  emit EmitValue(i);
}

void Receiver::HandleValue(int i) {
  std::cout << "Receiver::HandleValue: " << i << std::endl;
}

int main(int argc, char **argv) {
  QApplication app(argc, argv);

  Receiver receiver;

  Sender sender1(&receiver);
  QThread thread1;
  sender1.moveToThread(&thread1);
  thread1.start();
  emit sender1.EmitRunLoop(10);

  Sender sender2(&receiver);
  QThread thread2;
  sender2.moveToThread(&thread2);
  thread2.start();
  emit sender2.EmitRunLoop(3);

  const int result = app.exec();
  thread1.wait();
  thread2.wait();
  return result;
}
Hiroyuki KomatsuHiroyuki Komatsu

Astro A50 がファームウェアの更新に失敗する

問題

  • ヘッドセットの電源が入らず、ハードリセット (Dolby + Game 長押し) をしても、ファームウェアの更新に失敗する
  • ベースステーションに置くように指示されるが、置いても反応しない

解決方法

  1. Microsoft Store から Windows10 用の ASTRO COMMAND CENTER をインストールする
  2. ヘッドセットをハードリセットする (Dolby + Game 長押し)
  3. Windows10 用の ASTRO COMMAND CENTER から、ヘッドセットのファームウェアを最新版に自動更新する。

サポートページから直接ダウンロードできる macOS 版や Windows 版の ASTRO COMMAND CENTER では解決しない。

Hiroyuki KomatsuHiroyuki Komatsu

ファイルを改行単位で 10 分割するコマンドライン

下記のコマンドラインで data.txtdata00.txt から data09.txt に分割できる

split -n l/10 -d --additional-suffix=.txt data.txt data
  • -n l/10 : 改行単位で 10 分割
  • -d : 分割後のファイルに数字をつける (00 から09 の部分)
  • --additional-suffix=.txt 分割後ファイルの接尾辞
  • data.txt 分割したいファイル
  • data 分割後ファイルの接頭辞

-n 10 とするとサイズが均等になるように (改行によらず) 10 分割される
-n l/10 なら、サイズをできるだけ均等にしたうえで改行単位で分割する

Hiroyuki KomatsuHiroyuki Komatsu

Git で特定のコミットメッセージを変更する

以下は abcd123 のコミットメッセージを変更する方法

  1. git rebase -i abcd123^ でエディタが開く (最後に ^ が必要)
  2. pick abcd123 とある行を r abcd123 に変更して保存 (rreword でもよい)
  3. コミットメッセージの編集画面が開くので、変更して保存

abcd123 を含む以降のコミット ID はすべて変更される

疑問

  • 2 の工程を飛ばして、1 から 3 に直接行く方法はないのだろうか?

参考

Hiroyuki KomatsuHiroyuki Komatsu

Git で特定のコミットからファイルを分離する

以下は abcd123 のコミットに含まれる file.txt を分離する方法。

  1. git rebase -i abcd123^
  2. pick abcd123e abcd123 に変更
  3. git reset HEAD^ -- file.txt
  4. git commit --amend (file.txt 以外の変更がコミットされる。コミットメッセージは保持される)
  5. git add file.txt
  6. git commit
  7. git rebase --continue

参考
version control - How to split last commit into two in Git - Stack Overflow

Hiroyuki KomatsuHiroyuki Komatsu

2 つのファイルを横に結合するコマンドライン

paste を使う。

% cat a.txt
a
b
c
% cat b.txt
0
1
2
% paste a.txt b.txt
a	0
b	1
c	2
Hiroyuki KomatsuHiroyuki Komatsu

Python でシンボリックリンクに対応した zip ファイルの作成

shutil.make_archive を素直に使うとシンボリックリンクは実ファイルに置き換えられてしまう。

zipfile.ZipFileZipInfo と組み合わせて使えば、シンボリックリンクへの対応は可能。

参考:

とはいえ、 zip コマンドを Python 内で呼び出すほうが分かりやすい気がする。

cd <base_dir>
zip -ry archive.zip <target>
Hiroyuki KomatsuHiroyuki Komatsu

macOS で PrintScreen を「形式なしのペースト」に割り当てる

Karabiner-Elements の設定ファイルを書いて実現する。

~/.config/karabiner/assets/complex_modifications ディレクトリに json 形式の設定ファイルを書く。
ファイル名は自由。

下記のファイルでは Right-Option もコピーに割り当てている。

plain_paste.json
{
  "title": "Right-Option/PrintScreen to copy/paste w/o format (Shift + Command + v)",
  "rules": [
    {
      "description": "Right-Option/PrintScreen to copy/paste w/o format (Shift + Command + v).",
      "manipulators": [
        {
          "type": "basic",
          "from": {
            "key_code": "right_option"
          },
          "to": [
            {
              "key_code": "c",
              "modifiers": [
                "left_command"
              ]
            }
          ]
        },
        {
          "type": "basic",
          "from": {
            "key_code": "print_screen"
          },
          "to": [
            {
              "key_code": "v",
              "modifiers": [
                "left_command",
                "left_shift"
              ]
            }
          ]
        }
       ]
    }
  ]
}
Hiroyuki KomatsuHiroyuki Komatsu

macOS の .dmg ファイルのフォーマットを確認する

hdiutilimageinfo オプションを使う

% hdiutil imageinfo myimage.dmg
Hiroyuki KomatsuHiroyuki Komatsu

Google Spreadsheets で配列

INDEX{} を使う。1-origin なので、下記の場合は 100 が返り値になる

=INDEX({100, 200, 300}, 1)
Hiroyuki KomatsuHiroyuki Komatsu

Google Spreadsheet で GAS を使って、カタカナをひらがなに変換

セルをまとめて変換する方が早くなる?

function toHiragana(args) {
  if (Array.isArray(args)) {
    let output = [];
    for (let arg of args) {
      output.push(toHiragana(arg));
    }
    return output;
  }

  if (typeof args != "string") {
    return "";
  }

  let codes = [];
  for (let chr of args) {
    const offset = ('ァ' <= chr && chr <= 'ヶ') ? -0x0060 : 0;
    codes.push(chr.charCodeAt() + offset);
  }
  return String.fromCharCode(...codes);
}
Hiroyuki KomatsuHiroyuki Komatsu

VSCode で .tsv ファイルではタブが入力できるようにする

keybindings.json に次のように設定する。

keybindings.json
[
  {
    "key": "tab",
    "command": "type",
    "args": {"text": "\t"},
    "when": "editorTextFocus && editorLangId == plaintext"
  }
]

.tsv ファイルは plaintext として認識されるので、plaintext 全体が適用範囲にはなってしまう。 .tsv ファイルに限定するのは宿題。

参照

Hiroyuki KomatsuHiroyuki Komatsu

git でタグをつけて公開する

% git tag -a {tag} {commit-id}
% git push -u origin master {tag}

具体例

% git tag -a 2.26.4660.102 063c41f1d7c1a877f44c1f8caad6be1897350336
% git push -u origin master 2.26.4660.102
Hiroyuki KomatsuHiroyuki Komatsu

svg から ico ファイルを作成するコマンドライン

ImageMagick の convert と Icoutils の icotool を使う

convert -background none -density 1024 -resize 16x16 -extent 16x16 -gravity Center icon.svg icon_16.png
convert -background none -density 1024 -resize 24x24 -extent 24x24 -gravity Center icon.svg icon_24.png
convert -background none -density 1024 -resize 32x32 -extent 32x32 -gravity Center icon.svg icon_32.png
icotool -c icon_16.png icon_24.png icon_32.png > icon.ico

シェルスクリプトでまとめての実行

for n in icon1 icon2 icon3; do
  PNGS=()
  for s in 16 20 24 32 48 64 128 256; do
    convert -background none -density 1024 -resize ${s}x${s} -extent ${s}x${s} -gravity Center ${n}.svg ${n}_${s}.png
    PNGS+=${n}_${s}.png
  done
  icotool -c $PNGS[@] > ${n}.ico
done
Hiroyuki KomatsuHiroyuki Komatsu

macOS で CPU 温度を取得するコマンドライン

sudo powermetrics --samplers smc -n 1

一般ユーザーでは実行できないので、sudo が必要

Hiroyuki KomatsuHiroyuki Komatsu

macOS でインストールされたパッケージの一覧を取得するコマンドライン

pkgutil --pkgs

インストールされたファイルの一覧

pkgutil --files [package_name]

インストールの記録を削除

sudo pkgutil --forget  [package_name]
Hiroyuki KomatsuHiroyuki Komatsu

macOS でアプリケーションのウインドウを移動するコマンドライン

osascript \
    -e 'tell application "Terminal"' \
    -e 'set position of front window to {1, 1}' \
    -e 'end tell'

デスクトップの指定方法と複数ウインドウがある場合の指定方法も確認したい

参照

Hiroyuki KomatsuHiroyuki Komatsu

macOS で pkg ファイルをインストールするコマンドライン

sudo installer -pkg MyApp.pkg -target / 
Hiroyuki KomatsuHiroyuki Komatsu

adb でデバイス内のファイルにアクセス

adb pull /data/data/com.example.package/databases/data.db .

"permission denied" の場合

adb shell "su -c cat /data/data/com.example.package/databases/data.db" > data.db

or

adb shell "run-as com.example.package cat /data/data/com.example.package/databases/data.db" > data.db

"run-as package: not debuggable" の場合

adb backup -noapk com.example.package

参考

Hiroyuki KomatsuHiroyuki Komatsu

Git: サブディレクトリを新規リポジトリにする

SOURCE_REPO/PROJECT を NEW_REPO として登録する

curl "https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo" > ~/git-filter-repo

git clone https://github.com/USER/SOURCE_REPO.git
cd SOURCE_REPO
python3 ~/git-filter-repo --path PROJECT
python3 ~/git-filter-repo --subdirectory-filter PROJECT
git remote add origin https://github.com/USER/NEW_REPO.git
git push -u origin main
Hiroyuki KomatsuHiroyuki Komatsu

Bazel でビルドに必要なファイルをあらかじめダウンロードする

--repository_cache オプションと --registry オプションを使う

Bzlmod の場合

ファイルの準備

bazel fetch --repository_cache="/path/to/repository_cache"
git clone https://github.com/bazelbuild/bazel-central-registry.git /path/to/bcr

使用方法

bazel build --repository_cache="/path/to/repository_cache" --registry=file:///path/to/bcr ...

WORKSPACE の場合

ファイルの準備

bazel sync --repository_cache="/path/to/repository_cache"

使用方法

bazel build --repository_cache="/path/to/repository_cache" ...

参考

Hiroyuki KomatsuHiroyuki Komatsu

Bazel でサブプロセスが Operation not permitted というエラーで失敗する

--spawn_strategy=local をオプションで指定すると回避できることがある。

bazel build ... --spawn_strategy=local

macOS の /usr/bin/hdiutil を実行する genrule が上記のエラーで失敗したが、 --spawn_strategy=local で問題は回避できた。

参考

Hiroyuki KomatsuHiroyuki Komatsu

Bazel の Abseil が absolute paths をインクルードしているというエラーで失敗する

開発環境が更新されると、下記のようなエラーで失敗することがある。

ERROR:  .../.cache/bazel/.../external/abseil-cpp~/absl/debugging/BUILD.bazel:335:11: Compiling absl/debugging/internal/utf8_for_code_point.cc failed: absolute path inclusion(s) found in rule '@@abseil-cpp~//absl/debugging:utf8_for_code_point':
the source file 'absl/debugging/internal/utf8_for_code_point.cc' includes the following non-builtin files with absolute paths (if these are builtin files, make sure these paths are in your toolchain):
  '/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h'
  '/usr/lib/gcc/x86_64-linux-gnu/14/include/limits.h'
  '/usr/lib/gcc/x86_64-linux-gnu/14/include/syslimits.h'
  '/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h'
Target //:package failed to build

bazel clean --expunge でキャッシュを削除すると解決した。