プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

C++でタスクトレイに格納するアプリケーションを作る

やりたいこと

 別に立派なGUIが作りたいんじゃない。作りたいのは、コンソールアプリで十分だ。

 しかし、そのコンソールアプリが常に何かを出力し続けていたら?ちょっとモード変更したいとか、そのための入力処理を作るのも億劫だし、そもそも流れ続けるコンソールに入力したくもない。だが、コンソールアプリで十分なのに、画面なんて作りたくない。

 これって、タスクトレイにボタン一つ置ければ、話は簡単じゃないか。

 しかし、そんなことをC++でやろうとすると、途端に非常に面倒くさいことが要求される。Windowsならメッセージを受け取る為の見えないウィンドウを作り、Shell_NotifyIconでNOTIFYICONDATAを登録し…。

 違う。違うんだ、私がやりたいのはそんなことじゃないんだ。ボタン押したら対応するイベントハンドラが発火して、後はチェックボックスぐらいがあれば、それで良いんだ。

 

zserge/tray


 WINAPIを叩いたとしても、まぁ、数百行あればかけるっちゃかけるだろう。
 でも私はWin APIを叩きたくないのだ。何故なら私はGUIを作りたい訳じゃないからだ。そこは本質ではないのだ。情けないことを言えば、ぶっちゃけWinAPIは得意ではないのだ。

 かといって、コンソールアプリを作ってるのに、Qtの出る幕は無い。


 というわけで、今回紹介するzserge/trayを使います。

github.com

 C++でタスクトレイに格納したアプリケーションを、ヘッダファイル1つをインクルードするだけで簡単に作れてしまうライブラリ。リンクは不要。しかもクラスプラットフォームだ。
 私はC++で使ったが、Cでも使える。(というか、C用だから、C++用も欲しいな)

 

サンプルコード

 永遠と「Hoge Hoge」と言い続けるが、タスクトレイから「Piyo Piyo」モードに変更も出来て、終了も出来るプログラムの例を示す。これだけで、もう何となく使うことが出来ると思う。

f:id:nodamushi:20180917020301p:plain

//↓ OS設定。LinuxならTRAY_APPINDICATOR、MacならTRAY_APPKIT。Makefileに書いとくといい。
#define TRAY_WINAPI 1
#include "tray.h"
#include <iostream>
#include <thread>
#include <chrono>

using namespace std;
using namespace std::chrono;

volatile bool piyoMode;    //!< Piyo Piyoとコンソールに出力するモード
volatile bool processDone; //!< 処理完了フラグ
void piyo(tray_menu *);    //!< Piyoモードのトグル
void finish(tray_menu *);  //!< 終了処理
void _main();              //!< Hoge Hogeと出力するメイン処理
tray tray ={               //!< タスクトレイの設定
  (char*)"icon.png", // タスクトレイのアイコン画像。別途用意しておく。
  (tray_menu[]){
    {(char*)"piyoMode",0,0,piyo}, //サブメニューを指定すれば、階層化も可能
    {(char*)"-"},                 // セパレータ
    {(char*)"exit",0,0,finish},   //終了
    {nullptr}                     //最後の要素。必須。
  }
};


int main(int argc, char *argv[]){
  piyoMode=processDone=false;
  thread t(_main);// 表示処理は別スレッド化
  tray_init(&tray); //タスクトレイに登録
  while(tray_loop(1) == 0); //タスクトレイが表示されている間ループ
  t.join();
  return 0;
}

void piyo(tray_menu * item){
  bool b =!item->checked;
  piyoMode = b;
  item->checked = b; //チェックボックスの表示非表示変更
  tray_update(&tray); //更新
}

void finish(tray_menu *){
  processDone=true;
  tray_exit();  // タスクトレイから削除
}

void _main(){
  while(!processDone){
    cout << (piyoMode? "Piyo Piyo" : "Hoge Hoge") << endl; // piyoかhogeを出力
    this_thread::sleep_for(seconds(2)); 
  }
  cout << "Done"<<endl;
}

tray構造体

 タスクトレイの情報。メニューとアイコンを設定する。Windowsでもアイコンってpngでいけるんやね。

static tray tray ={
  (char*)"icon.png",
  (tray_menu[]){
    {nullptr}      //最後の要素。必須。
  }
};

tray_menu構造体

 タスクトレイのメニュー。入れ子構造に出来たり、Check、Disable/Enableも設定出来る。textが"-"だとセパレータになる。

要素 説明
char* text メニュー名。"-"だとセパレータ
int disabled 有効無効の設定
int checked チェックの設定
void (*cb)(struct tray_menu*) コールバック関数。
void *context お好きな様にお使い下さい。
tray_menu* submenu サブメニュー

関数

  • tray_init(tray*): 初期化する。1が返ると失敗
  • tray_update(tray*): disabledとか、checkedとかを変更した時に呼び出して、画面を更新
  • tray_loop(int) : UIのループ。引数はブロッキングするかどうか。tray_exitが呼ばれると1が返る。
  • tray_exit() : UIループを終了する