月別アーカイブ: 2023年10月

FireMonkeyのMessageDialog

FMX事始め

1.FMXでMessageDialogを表示する
2.TMsgDlgTypeに指定できる値はVCLと同じ
3.表示できるボタン
4.表示できないボタン
5.ダイアログ右上の閉じるボタンの挙動
6.まとめ
7.お願いとお断り

1.FMXでMessageDialogを表示する

いろいろな事情からFMXプラットフォームで、あるプログラムを書くことになった。使い慣れたVCLと違って、FireMonkeyはずっと以前に一度だけWAVファイルの再生プログラムを作った時触れたことがあるだけで、まともに触るのは今回が初めて。

最初にいちばん困ったのはユーザーへのメッセージの出し方。なんでかわからないけれど、普通にShowMessageすると、その直後、FMXプラットフォームでは、結構な頻度でエラーが発生する気が・・・。

だから、最初に書いたデータベース接続のプログラムは、極力ShowMessageを使わない方向で書いたんだけど、2作目のテキスト入力練習プログラムではそうも行かず、良い機会だと思ってShowMessageより見た目が華やかなMessageDialogの正しい使い方を調べてみた。

思った以上に情報が少ない気がしたので、学んだことを備忘録として、まとめておく。

まずは、iマーク付きのMessageDialogの出し方。

implementation

uses
  FMX.Platform, FMX.DialogService;

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
var
  ASyncService:IFMXDialogServiceASync;
begin
  //mtConfirmationだとBeep音が鳴らないが、mtInformationだとBeep音が鳴る
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync,
    IInterface(ASyncService)) then
  begin
    TDialogService.MessageDialog('Do you know Delphi?',
      TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], TMsgDlgBtn.mbOK, 0,
      procedure(const AResult: TModalResult)
      begin
        if AResult = mrOK then
        begin

        end;
      end);
  end;
end;

実行すると・・・

ここにたどり着くまで、結構長かった・・・
ほんとに、ようやくって感じ。

調べてわかったことは・・・

var
  ASyncService:IFMXDialogServiceASync;

・・・と宣言するためには、

uses
  FMX.Platform;

uses に FMX.Platform が必要で、さらに、サポートの有無を調査するif文の・・・

if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync,
    IInterface(ASyncService)) then

TPlatformServices も FMX.Platform を参照している。

で、本命の MessageDialog を表示するには、さらに・・・

uses
  FMX.Platform, FMX.DialogService;

uses に FMX.DialogService も追加しなければならない。

2.TMsgDlgTypeに指定できる値はVCLと同じ

上のユーザーへの情報提供(Info)に加えて、ユーザーに確認する場合は、

TMsgDlgType.mtConfirmation

ユーザーに警告。

TMsgDlgType.mtWarning

ユーザーにエラーを報告。

TMsgDlgType.mtError

mtCustomってのもあったけど・・・

TMsgDlgType.mtCustom
画像は、何も出てこなかった・・・。
キャプションもProject1(アプリケーション名)になってる・・・。

実質、「情報提供・確認・警告・エラー」の4つ型があり、これはVCLと変わらない。

3.表示できるボタン

ユーザーの応答が「OKボタン押し下げのみ」であれば、MessageDialogの最後の引数を別手続きにして、それを呼び出す形にすればいいのかと・・・

  private
    { private 宣言 }
    procedure MsgDlgProc(const AResult: TModalResult);

として、Shift+Ctrl+Cで手続きを作成。

procedure TForm1.MsgDlgProc(const AResult: TModalResult);
begin
  //何もしない手続き

end;

応答が「OK」のみの場合は、これを呼び出し。

procedure TForm1.Button2Click(Sender: TObject);
var
  ASyncService:IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(ASyncService)) then begin
    TDialogService.MessageDialog('Do you know Delphi?',
      TMsgDlgType.mtInformation, 
      [TMsgDlgBtn.mbOK], TMsgDlgBtn.mbOK, 0, MsgDlgProc);
  end;
end;

コードが短くなって、なんとなくすっきりした。

でも、「はい」・「いいえ」・「キャンセル」のようにボタンを複数表示するとそうもいかない。

procedure TForm1.Button3Click(Sender: TObject);
var
  ASyncService:IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(ASyncService)) then
  begin
    TDialogService.MessageDialog('Do you know Delphi?',
      TMsgDlgType.mtInformation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo, TMsgDlgBtn.mbCancel], TMsgDlgBtn.mbYes, 0,
      procedure(const AResult: TModalResult)
      begin
        if AResult = mrYes then
        begin
          ShowMessage('Goooooooood!');
        end;
        if AResult = mrNo then
        begin
          ShowMessage('No Good!');
        end;
        if AResult = mrCancel then
        begin
          ShowMessage('Cancel');
        end;
      end);
  end;
end;

case文でもよいようだ。

procedure TForm1.Button3Click(Sender: TObject);
var
  ASyncService:IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(ASyncService)) then
  begin
    TDialogService.MessageDialog('Do you know Delphi?',
      TMsgDlgType.mtInformation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo, TMsgDlgBtn.mbCancel], TMsgDlgBtn.mbYes, 0,
      procedure(const AResult: TModalResult)
      begin
        case AResult of
          mrYes:ShowMessage('Goooooooood!');
          mrNo:ShowMessage('No Good!');
          mrCancel:ShowMessage('Cancel');
        end;
      end);
  end;
end;

caseのリストが表す値は、case文内で一意、部分範囲またはリストの重複がなければ昇順とかリストの並びは関係ないようだ。また、このようによく使用されるボタン値は、セットになった定数として用意されていて、例えば上の場合は次のように指定できる。

TMsgDlgType.mtInformation, mbYesNoCancel, TMsgDlgBtn.mbYes, 0,
こっちの方がカンタン!

embarcaderoさんのWebサイトでは、TMsgDlgBtnは種類がたくさん紹介されていて、

定数意味
mrNone0結果なし。ユーザーがフォームを終了するまでのデフォルト値として使用されます。
mrOkidOK = 1ユーザーは[OK]ボタンでフォームを終了しました。
mrCancelidCancel = 2ユーザーは[キャンセル]ボタンでフォームを終了しました。
mrAbortidAbort = 3ユーザーは[中止]ボタンでフォームを終了しました。
mrRetryidRetry = 4ユーザーは[再試行]ボタンでフォームを終了しました。
mrIgnoreidIgnore = 5ユーザーは[無視]ボタンでフォームを終了しました。
mrYesidYes = 6ユーザーは[はい]ボタンでフォームを終了しました。
mrNoidNo = 7ユーザーは[いいえ]ボタンでフォームを終了しました。
mrCloseidClose = 8ユーザーは[閉じる]ボタンでフォームを終了しました。
mrHelpidHelp = 9ユーザーは[ヘルプ]ボタンでフォームを終了しました。
mrTryAgainidTryAgain = 10ユーザーは[やり直し]ボタンでフォームを終了しました。
mrContinueidContinue = 11ユーザーは[続行]ボタンでフォームを終了しました。
mrAllmrContinue + 1(12 つまり $C)ユーザーは[すべて]ボタンでフォームを終了しました。
mrNoToAllmrAll + 1(13 つまり $D)ユーザーは[すべていいえ]ボタンでフォームを終了しました。
mrYesToAllmrNoToAll + 1(14 つまり $E)ユーザーは[すべてはい]ボタンでフォームを終了しました。
https://docwiki.embarcadero.com/Libraries/Sydney/ja/FMX.StdCtrls.TCustomButton.ModalResultより引用

さらに、セットになった定数が5つあるとのこと。

定数意味
mbYesNoCancelmbYes、mbNo、および mbCancel
mbYesAllNoAllCancelmbYes、mbYesToAll、mbNo、mbNoToAll、および mbCancel
mbOKCancelmbOK および mbCancel
mbAbortRetryIgnorembAbort、mbRetry、および mbIgnore
mbAbortIgnorembAbort、mbIgnore
https://docwiki.embarcadero.com/Libraries/Alexandria/ja/Vcl.Dialogs.TMsgDlgBtnより引用

4.表示できないボタン

僕のPCだけ、そうなのかもしれないけど。中には表示できないボタンが・・・。例えば、

procedure TForm1.Button7Click(Sender: TObject);
var
  ASyncService:IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(ASyncService)) then
  begin
    TDialogService.MessageDialog('Do you know Delphi?',
      TMsgDlgType.mtInformation,[TMsgDlgBtn.mbRetry],TMsgDlgBtn.mbRetry,0,
      procedure(const AResult: TModalResult)
      begin
        case AResult of
          mrOK:ShowMessage('OK!:了解');
          mrCancel:ShowMessage('Cancel:取消');
          mrAbort:ShowMessage('Abort:中止');
          mrRetry:ShowMessage('Retry:再試行');
          mrIgnore:ShowMessage('Ignore:無視');
          mrYes:ShowMessage('Yes:はい');
          mrNo:ShowMessage('No:いいえ');
          mrClose:ShowMessage('Close:閉じる');
          mrHelp:ShowMessage('Help:要援助');
          mrAll:ShowMessage('All:すべて');
          mrNoToAll:ShowMessage('NoToAll:すべていいえ');
          mrYesToAll:ShowMessage('YesToAll:すべてはい');
        else
          //ないと思うけど、
          ShowMessage(IntToStr(AResult));
        end;
      end);
  end;
end;

ボタンに mbRetry を指定しても、上の手続きを実行すると表示されたダイアログは・・・

普通のOKボタン!

で、OKを押し下げ。・・・ると

なんでかなー?

でも、次のように指定すると、

TMsgDlgType.mtInformation, [TMsgDlgBtn.mbCancel,TMsgDlgBtn.mbRetry]
指定した順番と並びが逆だけど、「再試行」ボタンが表示された!

で、「再試行(R)」を押し下げ。・・・ると

表示できる場合とできない場合があるらしい。つまり、これはダイアログに表示可能なボタンの設定(組み合わせ)を FMX の MessageDialog は内部的に持っているということ? それから、キャンセルボタンは必ず右側へ設置される?・・・から、ボタンの表示される順番もまた、決まっているという理解でいいのかな?・・・みたいな。

他にも、mbAbortRetryIgnore を指定して、デフォルトで選択状態にするボタンに mbRetry を指定しても・・・

TMsgDlgType.mtInformation, mbAbortRetryIgnore, TMsgDlgBtn.mbRetry, 0,
なぜか「再試行」ボタンが表示されない!

しかも、ダイアログ右上の「閉じる」ボタンが押せなくなってる(勝手にEnabled?がFalseに設定されてしまう)。これは、キャンセルがないから、閉じるボタンはその必要がないという意味に思えてくる・・・。だから、閉じるボタンの無効化も、VCLならその方法が紹介されているんだけど、FMXでの情報は見当たらないのか・・・

実は、この記事を書こうと思ったのは、この閉じるボタンをクリックした時の戻り値が何なのか、どんなに調べても(僕が調べた範囲では)見つけることが出来なかったので、実験してみた結果を記録しておこうと思ったことがきっかけというか、はじまり。

FMX の MessageDialog を設計した人の気持ちがだんだん、わかってきた!

5.ダイアログ右上の閉じるボタンの挙動

実験結果から見えてきたこと。それは MessageDialog の閉じるボタンは、そのクリックの可否がダイアログに表示するボタンの組み合わせによって、内部的に制御されているんじゃないか?ってこと。

まず、OKボタンのみの場合、

procedure TForm1.Button9Click(Sender: TObject);
var
  ASyncService:IFMXDialogServiceASync;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXDialogServiceAsync, IInterface(ASyncService)) then
  begin
    TDialogService.MessageDialog('Do you know Delphi?',
      TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], TMsgDlgBtn.mbOK, 0,
      procedure(const AResult: TModalResult)
      begin
        case AResult of
          mrOK:ShowMessage('OK!:了解');
          mrCancel:ShowMessage('Cancel:取消');
          mrAbort:ShowMessage('Abort:中止');
          mrRetry:ShowMessage('Retry:再試行');
          mrIgnore:ShowMessage('Ignore:無視');
          mrYes:ShowMessage('Yes:はい');
          mrNo:ShowMessage('No:いいえ');
          mrClose:ShowMessage('Close:閉じる');
          mrHelp:ShowMessage('Help:要援助');
          mrAll:ShowMessage('All:すべて');
          mrNoToAll:ShowMessage('NoToAll:すべていいえ');
          mrYesToAll:ShowMessage('YesToAll:すべてはい');
        else
          ShowMessage(IntToStr(AResult));
        end;
      end);
  end;
end;


ダイアログ右上の「×」をクリックすると表示されたのは・・・

AResult は mrOKだった!

つまり、OKをクリックするしか、選択肢がない(未来をプログラマ自身が選択した)のだから、閉じるボタンが押された時の戻り値もmrOKでよい・・・ということか!

次に、表示するボタンを「OK」と「キャンセル」にして、デフォルト選択ボタンは「キャンセル」に指定して、再度実行。

TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK, TMsgDlgBtn.mbCancel], TMsgDlgBtn.mbCancel, 0,

右上の「×」をクリックすると・・・

AResult は mrCancel だった!

思った通り、mrCancel が設定されていた! プログラマが「キャンセルという選択肢を未来に与えた」んだから、閉じるボタンが押された時は「キャンセル」と判断してよい・・・ということ?

デフォルト選択ボタンをOKに変えてみた・・・

TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK, TMsgDlgBtn.mbCancel], TMsgDlgBtn.mbOK, 0,

右上の「×」をクリックすると・・・

AResult はmrCancel!

思った通りだ。

ボタンを「はい」・「いいえ」・「キャンセル」の3つにし、デフォルト選択を「はい」に指定して実験・・・

TMsgDlgType.mtInformation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo, TMsgDlgBtn.mbCancel], TMsgDlgBtn.mbYes, 0,

右上の「×」をクリックすると・・・

AResult は mrCancel!

やっぱり、mrCancelが戻り値に設定されている!

ならば、ボタンを「はい」・「いいえ」の2つだけにすると、閉じるボタンは使用不可になるはずだ・・・。だって、プログラマの意向として、未来に「キャンセル」という選択肢は与えられていないから!

実際に動かして確認。

TMsgDlgType.mtInformation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbYes, 0,
思った通り「×」はクリックできない!

僕の中に生まれた予測は、ここで「確信」に変わった!

これはVCLのMessageDlgでも同じなんだろうか?
今度、実験だ。

6.まとめ

(1)OKボタンのみ設置したダイアログでは、閉じるボタンクリックでmrOKが返る。
(2)ダイアログにキャンセルボタンを設置した場合は、閉じるボタンもクリック可能。
(3)キャンセルボタンがある場合、閉じるボタンクリックで返る値はmrCancelになる。
(4)キャンセルボタンがない場合、閉じるボタンはクリックできない。
(5)ボタンの組み合わせは内部的に不可もある(不可でもエラーにはならない)。

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

範囲チェックエラーが出た時は?

{$R-}で範囲チェックさせない!

Delphiで、画像をグレースケール変換するプログラムを作成。実行すると、

表示されたエラーメッセージ

プログラムのコードは、次の通り。
Image1に表示した画像をグレースケールに変換してImage2に表示するというモノ。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Jpeg,
  Vcl.ComCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    Button1: TButton;
    StatusBar1: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function CreateGrayScalePalette(Tone:Byte): HPALETTE;
var
  Palette: ^TLogPalette;
  i: Integer;
begin
  GetMem(Palette, SizeOf(TLogPalette) + SizeOf(TPaletteEntry) * Tone );
  Palette^.palNumEntries:=Tone+1;
  Palette^.palVersion:=$0300;
  for i := 0 to Tone - 1 do begin
    Palette^.palPalEntry[i].peRed:= Tone - i;
    Palette^.palPalEntry[i].peGreen:= Tone - i;
    Palette^.palPalEntry[i].peBlue:= Tone - i;
    Palette^.palPalEntry[i].peFlags:= 0;
  end;
  Result:=CreatePalette(Palette^);
  FreeMem(Palette);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  X, Y: Integer;
  Bmp: TBitmap;
  P: PByte;
begin
  Bmp := TBitmap.Create;
  try
   Bmp.Assign(Image1.Picture.Bitmap);
   Bmp.PixelFormat := pf8bit;
   Bmp.Palette := CreateGrayScalePalette(255);
   Image2.Picture.Bitmap := Bmp;
  finally
   Bmp.Free;
  end;
  Image2.Width:=Image2.Picture.Bitmap.Width;
  Image2.Height:=Image2.Picture.Bitmap.Height;
  Image2.Visible:=True;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  jpg: TJPEGImage;
begin
  StatusBar1.SimplePanel:=true;
  // TJPEGImageオブジェクトをインスタンス化
  jpg := TJPEGImage.Create;
  try
    // Jpegファイル読み込み
    jpg.LoadFromFile('Image.jpg');
    // Image1に割り当てる
    Image1.Picture.Bitmap.Assign(jpg);
    Image1.Width:=Image1.Picture.Bitmap.Width;
    Image1.Height:=Image1.Picture.Bitmap.Height;
    //StatusBar1.SimpleText:=IntToStr(Image1.Width)+'/'+IntToStr(Image1.Height);
  finally
    // TJPEGImageオブジェクトを破棄
    jpg.Free;
  end;
end;

end.

グレースケール変換実行のボタン(Button1)をクリックすると・・・
このButton1Click手続き内で呼び出しているCreateGrayScalePalette関数でエラーが発生。

ブレークして確認すると、エラーになるのはココ。

でも、なんでエラーになるのか、わからない・・・

Google先生に訊くと、次のような情報を発見。

[Delphi?][ネタ]透明に見えるパターンを描く

https://qiita.com/pik/items/25276e49fb131425db07

早速、範囲チェックさせないコンパイラ指令 {$R-} を追加。

ナニがどうして、そうなるのか?
原因も、理由も、皆目わからないけれど・・・

範囲チェックエラーは出なくなりました!

範囲チェックを実行しないというコンパイラ指令 {$R-} は知りませんでした。
同じ理由で困ってる方もいるかもしれないと思い、記録だけUpしました。

なお、画像のグレースケール化にあたっては、次のWebサイト様にあった情報を使わせていただきました。24bitのフルカラー画像を256階調のモノクロ画像に変換(グレースケール変換)する処理を行う際に役立つ情報が、そのアルゴリズムも含めて、多数紹介されています。

カラー画像をモノクロ画像に変換

http://rakasaka.fc2web.com/delphi/grayscale.html

また、上のWebサイト様で紹介されている配列要素を動的に確保する必要のない、Delphiが独自に定義しているTMaxLogPalette構造体を使用したCreateGrayScalePalette関数を利用した場合は、範囲チェックエラーは発生しませんでした。

お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

1番が死んじゃった!

【もくじ】

1.1気筒、死んでる!
2.キャブクリーナーを吹く
3.1番復活!
4.まとめ
5.お願いとお断り

1.1気筒、死んでる!

ゼファー400のエンジン。ずっと元気だったんだけど・・・
今日、プリンタのインクを買いに行こうと思って、火を入れたら、なんか、音がヘンで、いつもならエンジンが暖まると「ドゥルルル」って軽快な連続音でアイドリングするのに、今日は「ルルル」がなくて、「ドゥル、ドゥル、ドゥル、ドゥル」って息切れしてる感じ。走り始めても、全然パワーの盛り上がりが感じられない・・・

(もしかして、1気筒死んでる?)

走れないわけじゃないけど、動けなくなってからでは手遅れになるし、ケータイも家に置いてきた。とりあえずUターンして自宅に戻り、エンジンをかけたまま、赤外線温度計でマフラーの温度を測ってみる。

4番(なんとなく測りやすいから)を最初に計測。エンジンとマフラーの接合部で200℃以上、その少し下でも150℃くらいある。とても素手では触れない。

3番も、2番も、同様。でも1番は・・・

エンジンとマフラーの接合部で75℃くらい、その少し下だと45℃くらい(写真撮ってないから説得力に欠けるけど)

ヤバイ。1番、間違いなく死んでる・・・。数km走って自宅へUターンしたから、周りから熱もらって、少しだけ暖まった・・・いや・・・調子が悪いのは、低回転の時だけで、ある程度回すと普通にフケてたような・・・そんな気もする。

・・・ってコトは、原因はもしかして、キャブのスロージェットの詰まり?

とりあえず、1番(バイクに乗った状態で見ていちばん左側の気筒)のプラグを外して、エンジンのフィンに押し当て、スタータースイッチを押してスパークすることを確認。

(大丈夫。火花は飛んでる・・・)

同時に、1番の気筒から元気よく空気が吹き出してくるのを掌に感じる。

(圧縮がないわけでもなさそう・・・)

2、3、4番は元気で、1番もスパークするから、多分電装系のトラブルじゃない。
原因はおそらく、吸気系のどこか・・・

2.キャブクリーナーを吹く

エンジンの左後ろ側をよーく見ると、ナンか液体が流れたような跡も見える・・・。コレって、もしかしてキャブのオーバーフローの跡? 気づかないうちに、また、起きてた?

とりあえず、エアクリーナーを取り外して吸気口を見てみよう。いつか、キャブがオーバーフローした時は、漏れたガソリンがたまってた・・・。

シートを外し、タンクを止めているネジ2本を外し、給油ホースを慎重に抜いて、タンクを外す。ガソリンを給油したばかりだからかなり重たい。傷つけないように、そっとカーペット(廃品をバイク用に使用)の上へ移動。で、さらにタンクを固定するためのステーを外す(このステーがあるとエアクリーナーを入れるケースのカバーが脱着できない)。

ステーを外した後、エアクリーナーケースのカバーを外して、2年前に交換したエアクリーナーと久しぶりにご対面。湿式でまだしっとりしていて、表面にほんの少しだけ埃が付いてる。ついでだから埃を払う。とりあえず、エアクリーナーはキレイになった。

LEDライトを点灯し、エアクリーナーの装着口から1番のキャブの吸気口を覗き見る。

(ガソリンは漏れてないみたい・・・)

さて、どうするか?

いちばん、イイのは、このままキャブを外して、分解・洗浄することだ。エイプ100の単気筒のキャブならやったことがあるんだけど・・・。ゼファー400の4連キャブレターは外したことはあっても、バラしたことはない。自信はまるでないけど、やるなら新品のガスケットを用意したり、それなりの準備が必要だ。素直にバイク屋さんへ、もって行こうか・・・。

(でも、せっかくここまでバラしたから、キャブレタークリーナー吹いてみようか?)

ガレージの工具箱の隣に、メンテナンス用の各種スプレー缶を並べてあるんだけど、確かその中に買い置き未使用のキャブレタークリーナーが1個あったはずだ。いつか、キャブがオーバーフローしたとき、ガソリン抜いて、給油ホースにクリーナー繋いでキャブ内にクリーナー液を噴射。しばらく放置し、さらにクリーナー液を追加(噴射)して、キャブレター内部の汚れを含んだクリーナー液を排出。これを数回繰り返したら、オーバーフローしなくなったことがあって、あの時、使わなかった残りの1缶をとっておいたんだ。

注意! 上の方法で僕のバイクのオーバーフローが直ったことは事実ですが、正しい修理方法ではありません。絶対に真似しないでください。

ガソリンの通り道が詰まってるんじゃなくて、空気の通り道が詰まってるなら、もしかしたらキャブレタークリーナー吹いたら直るかも・・・。このまま、何にもしないで元に戻すよりはいい・・・。

エンジンをかけて、バイクの左側に立ち、右手じゃなく(通常と逆の)左手でアクセルを少し開けながら、右手に持ったキャブレタークリーナーのスプレー缶のノズルを1番の吸気口に向けて、泡状のクリーニング液を噴射する。燃料じゃないから、当然、回転は落ちる。その分だけアクセルを回してエンジンを吹かす。マフラーからは大量の白煙が・・・。事情を知らない人が見たらエンジンが壊れているとしか思えないんじゃないか?

ついでに2、3、4番の吸気口にもクリーニング液を噴射。祈るような気持ちでしばらく放置して、2回、3回と繰り返す。最後に、エアクリーナーケースの底に残って溜まったクリーニング液をきれいにふき取って、清掃完了。

結果は、走ればわかる。エアクリーナーをセットしてカバーを被せ、3箇所ねじ止め。タンクのステーを付けて、タンクを装着、給油ホース等を元通りに接続。

シートを装着しようとして、ふとシートをとめる金具の可動部のあちこちにまるで油分がないことに気づく。ついでだから多目的潤滑剤のスプレー缶を持ってきて、少しずつ潤滑剤を吹きかける。それから可動部を動かして、潤滑剤をなじませる・・・。

なんだか必要以上に可動部が動く気が・・・。よく見るとシートを外すためのキー差込口自体がグラグラしてる。(なんで?)と思い、キー差込口の部品とバイクの接合部を見ると、2本あるネジがどちらも5ミリくらい緩んでる。これもついでに直す。正直、キャブの不具合の原因がどこにあるのか、それはまったくわからないけれど、こっちはネジを増し締めするだけだから簡単だ。1番のトラブルが直ったかどうかはわからないけれど、この不具合に気づいて直せただけでも、今回、キャブレタークリーナーを使った価値があったと思えてくる。あとは、エンジンが元気よく廻ってくれれば・・・。

3.1番復活!

ウェスの上に並べて置いたネジ類が全部元の場所へ戻ったことを、ウェスを眺めて確認。シートを装着して、エンジンをかけてみる。

「ドゥルルルルルルルルルルルルルルルルルルルルル」

聴きなれた、いつも通りの音だ。もしかして・・・、信じられないけど、直った?

祈るような気持ちで空ぶかし。

「フォーン!」

回転計の針は気持ちよくフケ上がる。回転の落ちもイイ。早速、乗って確認。

アクセルを開けた分だけ、思い通りに加速する。

家の近くをグルっと一周。自宅に帰って、赤外線温度計でエンジンとマフラーの接合部の温度を計測。今度は1番から4番まで、全部200℃以上になってる。

やったー! なおったー☆

この後、本来の目的だったプリンタのインクを買いに10kmくらい走ったけど、何の問題もなくエンジンは気持ちよく回る、1番は完全復活した模様。本当の原因は何だったのか、皆目わからないけれど、キャブレタークリーナーを吹いて直ったから、空気系の通り道のどこかが詰まっていたのかな? みたいな・・・

追記(20231029)

エンジンは復活したから、状況的には問題は解決。OK!なんだけれど、燃料系もキレイにしたくなって、雨の日曜日に次の作業を実行。ただし、これがメンテナンス方法として正しいか、どうか・・・と言えば、間違いなく「間違い」な気が。でも、どうしてもやってみたくなって、自己責任でやったことなので、こちらも絶対に真似しないでください。

では、ナニをしたかというと「使い残りのキャブレタークリーナーをガソリンに混ぜてキャブ内を通過させ、多少なりとも燃料系を洗浄した気になりたい」という作業。

「満タンのガソリンタンクにキャブレタークリーナーを1缶全部噴射、燃料系の汚れをゆっくり落とす」という記事がWebにあり、雨でほかにすることもなかったし、これをやってみようか・・・と思ったのです(もちろん、結果は自己責任で)。

ただ、キャブレタークリーナーは先の作業で半分くらい使ってしまい、かつ、ガソリンタンクは満タン状態なので、このままWebにあった情報を適用しても効果が薄い気がしたことと、ガソリン使いたくても外は雨で、ガソリンを減らすためだけに走る気がしなかった等々の理由から、バイクからガソリンタンクを外し、ペットボトルにタンクから抜いたガソリンを入れ、エンジンがギリギリ回るくらいの濃さでキャブレタークリーナーを相当量混ぜ、エンジンを始動、ペットボトルに入れた燃料がなくなるまでアクセルを開閉してガソリンの流量を変化させつつ、エンジンを回転させれば、クリーナーが溜まった汚れを溶かすと同時に、燃料が流れる勢いそのもので燃料系を多少なりとも洗浄できるのではないかと・・・

まず、ペットボトルのキャップ(PEだから耐ガソリン性あり)にドリルでフューエルホースと同じ径の穴を開けて、たまたまあった同じポリエチレン製のフィルターを装着したフューエルホースを通し、1.5Lのペットボトルにこのキャップを被せて、今回の清掃作業専用のガソリンタンクを製作。

フィルター下部の出っ張りがフューエルホースの内径を押し広げ、ペットボトルキャップと密着してガソリンが漏れないことを期待したが、ちょっと無理だったようで、試してみるとガソリンがわずかながらも漏れてしまう・・・。やっぱり、水とは全然違って、ガソリンの粘性ってすごく小さいんだなーって。あらためて実感。ガソリン、恐るべし。

(絶対に、引火しないように。細心の注意を払って・・・作業しなきゃ)

対応策として、キャップの外側にビニールテープを巻いてキャップとフューエルホースを一体化させ、ガソリンが漏れないようにする。

ここで心配したのは、キャップから染み出したガソリンで溶けたビニールテープの「糊」が、どれくらい燃料に混入し、キャブ内に流れ込むのか・・・ということ。実際の程はわからないけれど、いったんペットボトル外に染み出たガソリンだから、ほとんど影響はなかろーと勝手に判断。作業を進める・・・

ペットボトル側のフューエルホースと、バイクのキャブレターに繋がっているフューエルホースをどう接続するかも(やったことないから)ここで大問題化。大いに悩む。

手持ちの物品で何かないかといろいろ探してみたが、径がやや小さかったり、材質が不明で耐ガソリン性が心配だったり・・・、あれもダメ、これもダメ、そうこうしているうちに、なんか、以前、そういう目的用の「部品」を近くのホームセンター内で見たことがあるような気がしたので、家のすぐ近くのホームセンターへ直行。店内をウロウロするうちに径7mmのエア配管用の「ホース継手」を発見。(あったー! コレだ。コレだ。) 見た感じ真鍮製(?)なので耐ガソリン性に問題はない。即、購入する。

バイクからガソリンタンクを外し、キャブレター側のフューエルホースを、今、購入してきたホース継手でペットボトル側のフューエルホースに接続。ガソリンタンクから50ccくらいガソリンを抜いてペットボトルに入れ、エンジンを始動する。

ガソリンを入れたペットボトルは、針金で三脚に吊り下げて、バイクの近くにセット。

外はどしゃ降りの雨。

エンジンをかけて、しばらく暖気(キャブ車だから、暖気しないとアクセルを開けられない・・・ってか、エンジンが冷えてるうちは、アクセルを開けても回転がついてこない。でも、それが楽しくて、うれしい。バイクに乗るのは休日Onlyだから、急いで発進する必要などさらさらない。エンジンを暖める時間そのものが休日らしくて、すごく楽しい)。数分後、頼りなかったアイドリング音が少しずつ力強い音に変化する。頃合い良し。油温計は低いままだけど、きっともう大丈夫。エンジンは暖まった。試しにアクセルを開けると、結構な勢いで、ペットボトル内の燃料が減ってゆく。

(こんなにガソリンが流れるんだ・・・)

心配になるほどの減り方だ(初めて見たけど)。とりあえず、キャブレタークリーナーをペットボトル内に噴射してガソリンに混ぜ、アクセルを開け続けてクリーナーを混入したガソリンがキャブレターを通過してエンジンに入ると回転にどのような変化が生まれるのか、検証する。

ペットボトル(内のクリーナーを混入した燃料)がカラになり、透明なフューエルホース内をエンジンに向かう燃料の油面はどんどん低くなる。やがて燃料がキャブレターに吸い込まれて行くと、思った通りアイドリングが不安定になる。慌ててアクセルを開け、4000回転くらいを維持する。エンジンはなんとか止まらずに、回り続けてくれる。相当濃くクリーナーを混ぜても、回転数を上げればエンジンは止まらないようだ。

次は自分的な本番だ。ペットボトル内に300cc程度ガソリンを追加。相当濃い量になったと思われるまでキャブレタークリーナーを思い切り、大量に噴射して、ガソリンに混ぜあわせる(量は申し訳ないけど、ホントに適当)。エンジンをかけ、アクセルを開けて、クリーナーを含んだ燃料がキャブレターを通過し、エンジンが爆発不調になったことを確認していったんエンジンを停止。キャブレター内の燃料通路の汚れが多少なりとも分解されてくれることを祈りながら、このまま15分ほど休憩。

15分後、(かかってくれー)と願いながらスターターボタンを押すと、調子悪そうな感じでエンジンが始動。アクセルを開けていないとエンジンは今にも止まりそう・・・。この状態でペットボトル内にさらにキャブレタークリーナーを追加噴射。油面が下がるのを眺めつつ、エンジンが停止しないようアクセルを開け続ける。

ガレージの外は結構激しい雨。ドアを解放して、バイクをバックさせて、直管マフラーのおしまいの部分だけガレージの外へ出し、排気ガスが屋外へ出るようにして作業してるから、一酸化炭素中毒にはならないと思うんだけど、ちょっと心配。

クリーナー成分を大量に含んだガソリンがキャブレターを通過、と同時にアクセルをさらに開け続けないと回転が維持できない状態に。エンジンを吹かして4000回転程度をなんとか維持しながら、燃料が尽きてエンジンが自然に停止するのを待つ。

フューエルホース内の油面が見えなくなってからも、キャブレター内には相当量の燃料があるようだ。なかなかエンジンが止まらない。アクセルを開けながら自然停止を待つこと、数十秒。ようやくエンジンが停止する。

これで燃料系がキレイになったか、どうか、まるで確信は持てないけれど、本人が「洗浄した気になる(?)」レベルのクリーニングが完了。

フューエルホース内の燃料が見えなくなったところでエンジンを停止し、キャブレター内にクリーナー成分を含んだガソリンを滞留させて数日間置いた方がさらによかったのかもしれないけど、ガスケットその他の部品への影響が心配で、敢えてそれはしなかった・・・

雨続きで走れなかったので、実際に走れたのは作業の二日後、エンジンはスターターボタンを押すのと同時に一発でかかり、暖気後は気持ちよく回るから、とりあえず、これでよかったのかなー、みたいな。もちろん、今日も、全気筒が生きてます!

4.まとめ

キャブレタークリーナーを直接吸気口へ向けて吹く形式の簡単なクリーニングで、1気筒死んでいたエンジンが直ることあるみたいだ。本当の原因は今でもわからないけれど。

5.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。