Open18

Tips

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

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

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 秒間出力がない場合に、入力を受け取るようにしている。

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

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

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

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

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

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

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

cd <main_dir>
git add <submodule_dir>
git commit

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

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;
}

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

問題

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

解決方法

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

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

Android エミュレーターから、開発マシン上のサーバーへのアクセス

10.0.2.2 を使えばよい。

エミュレーター上のウェブブラウザから、開発マシン上のウェブサーバーへアクセスするなら、https://10.0.2.2/ でアクセス可能

Set up Android Emulator networking  |  Android Developers

ファイルを改行単位で 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 なら、サイズをできるだけ均等にしたうえで改行単位で分割する

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

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

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

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

疑問

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

参考

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

ログインするとコメントできます