棒読みちゃんプラグインの作成法 02

棒読みちゃん
棒読みちゃんは、テキストを音声で読み上げてくれるソフトです。プラグインによる機能拡張に対応しており、クリップボードや、Twitterのタイムライン、ニコニコ生放送のコメント等を読み上げさせることができます。
 
前回:棒読みちゃんプラグインの作成法 01では、何もしない棒読みちゃんのプラグイン作成方法をご紹介しました。
今回は、RSS読み上げプラグインの作成を題材として、プラグイン設定画面の実装、読み上げ機能の実装方法をご紹介します。



実装が必要なクラス


プラグインに設定機能を持たせるには、以下4つのクラスを連携させる必要があります。

Plugin_RSS : IPlugin プラグインクラスです。前回骨格部分を作成しました。
PluginSettings プラグインの設定管理クラスです。
Load()メソッドで設定ファイル(Plugin_RSS.setting)から設定値を読み込みます。
プラグインクラスのメンバとしている他、設定画面管理クラスにもメンバとして持たせる必要があります。
SettingFormData プラグインの設定画面管理クラスです。設定管理クラスをメンバとして持つ他、プロパティシートクラスもメンバに持ちます。
SettingPropertyGrid 設定画面フォームに表示用のプロパティシートクラスです。設定画面の項目を持たせます。

 
また、プラグイン個別の機能として、RSS取得・解析機能を持つRSSReaderクラスも作成します。
 

相関図


以下にクラスの相関を示します。
プラグインクラスのBegin()処理で、設定管理クラスのLoad(Plugin_RSS.setting)メソッドをコールし、
設定ファイル()から設定を読み込みます。

次に、設定画面管理クラスを生成し、設定管理クラスを渡しておきます。
設定画面管理クラスはコンストラクタでプロパティシートクラスを生成・保持するようにしています。
 


  

ファイル構成



ファイル構成は右図の通りです。プロジェクト名はPlugin_RSSとしています、以降、各クラスの実装について解説します。


プラグインクラス Plugin_RSS.cs


前回作成したコードをベースに実装しています。
Begin()メソッド内のプラグイン設定関連処理は前述のとおりです。
その他の処理として、棒読みちゃん本体に表示するRSSボタンを追加しています。
End()メソッドでは設定の保存と、RSSボタンの削除を実施します。
 
RSSボタン押下ハンドラ(_toolStripButton_Click())内では、RSSReaderクラスを利用してRSSを取得し、読み上げタスクに追加しています。
読み上げタスクの追加は、以下のように、読み上げさせたい文章を引数としてAddTalkTask()を呼び出すだけです。

                //(ボタンハンドラ内でRSSReaderクラスを利用してRSS記事を取得済み)
                //lstArticle : 記事のリスト
                //article    : 一つの記事
                //記事リストを読み上げタスク追加
                foreach (Article article in lstArticle)
                {
                    //読み上げ文字列
                    string talk = String.Format("{0}{1}",article.title, article.description);
                    
                    //読み上げタスクに追加
                    Pub.AddTalkTask(talk);
                }

 
 
プラグインクラスのコード全体です。
Plugin_RSS.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms; //ToolStripSeparator, Button用(参照の追加が必要)
using System.Text.RegularExpressions;
using RSSUtility;//RSSリーダークラス
using FNF.BouyomiChanApp;
using FNF.Utility;
using FNF.XmlSerializerSetting;

namespace Plugin_RSS
{
    /// <summary>
    /// RSS読み上げプラグイン
    /// </summary>
    public class Plugin_RSS : IPlugin
    {
        #region 独自メンバー

        //プラグインの設定ファイルパス
        private string _path = Base.CallAsmPath + Base.CallAsmName + ".setting";

        //プラグインの設定管理クラス
        private PluginSettings _setting = null;

        //プラグインの設定画面管理クラス
        private SettingFormData _settingFormData = null;

        //棒読みちゃん本体画面に追加するGUI部品
        //セパレータ
        private ToolStripSeparator _toolStripSeparator = null;
        //ボタン
        private ToolStripButton _toolStripButton = null;

        #endregion

        #region IPlugin メンバー の実装

        /// <summary>
        /// 棒読みちゃん本体の起動時か、
        /// プラグインを有効にしたタイミングで呼ばれる
        /// </summary>
        public void Begin()
        {
            //プラグインの設定管理クラス生成
            this._setting = new PluginSettings();

            //プラグインの設定ファイルから設定値を読み込み
            this._setting.Load(this._path);

            //プラグインの設定画面管理クラス生成
            this._settingFormData = new SettingFormData(_setting);

            //棒読みちゃん本体画面に追加するGUI部品生成
            //セパレータ生成
            this._toolStripSeparator = new ToolStripSeparator();
            //セパレータを本体画面に追加
            Pub.ToolStrip.Items.Add(this._toolStripSeparator);

            //ボタン生成
            //あらかじめソリューションエクスプローラ->Properties->リソース->
            //イメージ->リソースの追加->既存のファイルの追加
            //で、ボタン画像をプロジェクトに追加済み
            this._toolStripButton = new ToolStripButton(Properties.Resources.feed);
            this._toolStripButton.ToolTipText = "RSS読み上げ";
            this._toolStripButton.Click += new EventHandler(_toolStripButton_Click);
            //ボタンを本体画面に追加
            Pub.ToolStrip.Items.Add(this._toolStripButton);

            return;
        }

        /// <summary>
        /// プラグインの備考
        /// </summary>
        public string Caption
        {
            get { return "RSSを読み上げます"; }
        }

        /// <summary>
        /// 棒読みちゃん本体の終了時か、
        /// プラグインを無効にしたタイミングで呼ばれる
        /// </summary>
        public void End()
        {
            //設定値をプラグインの設定フィルに書き込み
            _setting.Save(this._path);

            //棒読みちゃん本体画面に追加したGUI部品削除
            if (this._toolStripSeparator != null)
            {
                Pub.ToolStrip.Items.Remove(this._toolStripSeparator);
                this._toolStripSeparator.Dispose();
                this._toolStripSeparator = null;
            }
            if (this._toolStripButton != null)
            {
                Pub.ToolStrip.Items.Remove(this._toolStripButton);
                this._toolStripButton.Dispose();
                this._toolStripButton = null;
            }

            return;
        }

        /// <summary>
        /// プラグインの名称
        /// </summary>
        public string Name
        {
            get { return "RSS読み上げ"; }
        }

        /// <summary>
        /// プラグインの設定画面管理クラス
        /// </summary>
        public ISettingFormData SettingFormData
        {
            get { return this._settingFormData; }
        }

        /// <summary>
        /// プラグインのバージョン
        /// </summary>
        public string Version
        {
            //公式プラグイン(クリップボード監視、Skype読み上げetc)の
            //バージョン表記に合わせて、
            //"西暦/月/日付版"としておくと統一感がでるでしょう。
            get { return "2012/03/12版"; }
        }
        #endregion

        #region ボタンハンドラ
        /// <summary>
        /// ボタンハンドラ
        /// RSSを取得・解析し、RSS記事の文章を読み上げタスクに追加する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _toolStripButton_Click(object sender, EventArgs e)
        {
            try
            {
                string url = this._setting.URL;

                //URLチェック
                if (url == "")
                {
                    MessageBox.Show("設定画面でURLを入力して下さい");
                    return;
                }
                Regex regex =new Regex(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?");
                Match m = regex.Match(url);
                if (!m.Success)
                {
                    MessageBox.Show("URL文字列が不正です");
                    return;
                }

                //RSSリーダー生成
                RSSReader reader = new RSSReader(url);
                
                //RSS取得
                string xml = reader.Get();
                if (xml == "")
                {
                    MessageBox.Show("RSSを取得できませんでした");
                    return;
                }

                //RSS解析
                //記事リストを取得
                List<Article> lstArticle = reader.Parse(xml);
                if (lstArticle == null)
                {
                    MessageBox.Show("RSSを解析できませんでした");
                    return;
                }

                //記事リストを読み上げタスク追加
                foreach (Article article in lstArticle)
                {
                    //読み上げ文字列
                    string talk = String.Format("{0}{1}",article.title, article.description);
                    
                    //読み上げタスクに追加
                    Pub.AddTalkTask(talk);
                }
            }
            catch (Exception exp)
            {
                PrintException(exp);
            }
            return;
        }
        #endregion

        #region 汎用的なメソッド いろんなアプリで使える機能 別クラスに移動した方がよいかも
        /// <summary>
        /// 例外情報を表示します
        /// </summary>
        /// <param name="exp"></param>
        private void PrintException(Exception exp)
        {
            Console.WriteLine("Message    :n" + exp.Message);
            Console.WriteLine("Type       :n" + exp.GetType().FullName);
            Console.WriteLine("StackTrace :n" + exp.StackTrace.ToString());
        }
        #endregion
    }
}


設定管理クラス PluginSettings.cs


読みあげたいRSSのURLを設定値として持たせています。
メンバをpublicにしておくと、Load()メソッドで自動的に設定ファイル(Plugin_RSS.setting)から読み込まれ(デシリアライズ)、
Save()メソッドで自動的に設定ファイル(Plugin_RSS.setting)に保存(シリアライズ)されます。

using FNF.XmlSerializerSetting;

namespace Plugin_RSS
{
    /// <summary>
    /// プラグインの設定管理クラス
    /// publicなメンバが設定ファイルに保存されます
    /// </summary>
    public class PluginSettings : SettingsBase
    {
        /// <summary>
        /// RSSのURL
        /// </summary>
        public string URL;

        /// <summary>
        /// 以下タイミングで呼ばれる
        /// 親クラス(SettingsBase)のSave()メソッドが呼ばれた時
        /// 設定画面呼び出し時
        /// </summary>
        public override void ReadSettings()
        {
            return;
        }

        /// <summary>
        /// 親クラス(SettingsBase)のLoad()メソッドが呼ばれた時
        /// 設定画面OK終了時
        /// </summary>
        public override void WriteSettings()
        {
            return;
        }
    }
}

 

設定画面管理クラス SettingFormData.cs


ISettingFormDataインタフェースを実装したクラスを作成します。
前回ご紹介したインタフェースの実装機能を利用すると便利です。
Settingメンバでreturnする設定クラスはコンストラクタで受け取るようにしています。
また、プロパティシートクラスをpubulicメンバとして持ち、コンストラクタで生成しています。

using FNF.XmlSerializerSetting;

namespace Plugin_RSS
{
    /// <summary>
    /// プラグインの設定画面管理クラス
    /// </summary>
    class SettingFormData : ISettingFormData
    {
        //プラグインの設定管理クラス
        private PluginSettings _settings = null;

        //プラグインの設定画面(プロパティシート)
        //publicで見せる
        public SettingPropertyGrid _settingPropertyGrid = null;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="settings">プラグインの設定管理クラス</param>
        public SettingFormData(PluginSettings settings)
        {
            //プラグインの設定管理クラスを受け取る
            this._settings = settings;

            //プラグイン設定画面(プロパティシート)を生成する
            this._settingPropertyGrid = new SettingPropertyGrid(this._settings);
        }

        #region ISettingFormData メンバー の実装
        /// <summary>
        /// TreeView内のノードを開いた状態とする 設定なのだと思う。
        /// </summary>
        public bool ExpandAll
        {
            get { return false; }
        }

        /// <summary>
        /// プラグインの設定管理クラスのgetter
        /// </summary>
        public SettingsBase Setting
        {
            get { return this._settings; }
        }

        /// <summary>
        /// 設定画面フォームのタイトル
        /// </summary>
        public string Title
        {
            get { return "RSS読み上げプラグインの設定"; }
        }
        
        #endregion
    }
}

 

プロパティシートクラス SettingPropertyGrid.cs


設定画面に表示する項目として、RSSのURL入力欄を定義しています。

using System.ComponentModel;
using FNF.XmlSerializerSetting;

namespace Plugin_RSS
{
    /// <summary>
    /// プラグインの設定画面に表示するプロパティシート
    /// </summary>
    public class SettingPropertyGrid : ISettingPropertyGrid
    {
        private PluginSettings _settings = null;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="setting"></param>
        public SettingPropertyGrid(PluginSettings setting)
        {
            //プラグイン設定クラスを受け取る
            _settings = setting;
        }

        /// <summary>
        /// シートのタイトル
        /// </summary>
        /// <returns></returns>
        public string GetName() { return "RSS設定"; }

        /// <summary>
        /// 基本設定
        /// </summary>
        [Category("RSS")]
        [DisplayName("01)RSSのURL")]
        [Description("読みあげたいRSSのURLを設定して下さい。")]
        public string URL
        {
            get { return this._settings.URL; }
            set { this._settings.URL = value; }
        }

        /* ISettingPropertyGridでは設定画面での表示項目を指定できます。
        [Category   ("分類")]
        [DisplayName("表示名")]
        [Description("説明文")]
        [DefaultValue(0)]        //デフォルト値:強調表示されないだけ
        [Browsable(false)]       //PropertyGridで表示しない
        [ReadOnly(true)]         //PropertyGridで読み込み専用にする
        string  ファイル選択     →[Editor(typeof(System.Windows.Forms.Design.FolderNameEditor),       typeof(System.Drawing.Design.UITypeEditor))]
        string  フォルダ選択     →[Editor(typeof(System.Windows.Forms.Design.FileNameEditor),         typeof(System.Drawing.Design.UITypeEditor))]
        string  複数行文字列入力 →[Editor(typeof(System.ComponentModel.Design.MultilineStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
        */
    }
}

 

RSSリーダークラス RSSReader.cs


コンストラクタでURLを受け取り、Get()でRSSを取得、Parse()でRSSを解析して記事リストにします。
Parse()メソッドは当ブログのRSS向けに実装しています、必要に応じて書き換えて下さい。

using System;
using System.Collections.Generic;
using System.Net;
using System.Xml;

namespace RSSUtility
{
    /// <summary>
    /// RSSリーダー
    /// </summary>
    class RSSReader
    {
        //RSSのURL
        private string _url = "";

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="url"></param>
        public RSSReader(string url)
        {
            _url = url;
        }

        /// <summary>
        /// RSSを取得します
        /// </summary>
        /// <returns></returns>
        public string Get()
        {
            string xml = "";
            try
            {
                WebClient wc = new WebClient();
                wc.Encoding = System.Text.Encoding.UTF8;
                xml = wc.DownloadString(_url);
                wc.Dispose();
            }
            catch (Exception exp)
            {
                PrintException(exp);
                return "";
            }
            return xml;
        }
        
        /// <summary>
        /// RSSをパースします
        /// http://c-loft.com/blog のRSSでしかテストしていません
        /// パースしたいRSSの構造に応じて、適宜書き換えて下さい。
        /// </summary>
        /// <param name="xml">パース対象XML文字列</param>
        /// <returns>記事リスト または null</returns>
        public List<Article> Parse(string xml)
        {
            List<Article> lstArticle = null;
            try
            {
                //XML解析
                XmlDocument xdoc = new XmlDocument();
                xdoc.LoadXml(xml);
                XmlElement root = xdoc.DocumentElement;
                XmlNodeList nlstItem = root.SelectNodes("channel/item");

                //記事リスト生成
               lstArticle = new List<Article>();

                //記事リスト更新
               foreach (XmlNode item in nlstItem)
               {
                   //記事
                   Article article = new Article();

                   foreach (XmlNode node in item)
                   {
                       //日付
                       if (node.Name == "pubDate")
                       {
                           article.date = node.InnerText;
                       }
                       //タイトル
                       if (node.Name == "title")
                       {
                           article.title = node.InnerText;
                       }
                       //概要
                       if (node.Name == "description")
                       {
                           article.description = node.InnerText;
                       }
                   }

                   //記事リストに格納
                   lstArticle.Add(article);
               }
            }
            catch (Exception exp)
            {
                PrintException(exp);
                return null;
            }

            return lstArticle;
        }

        /// <summary>
        /// 例外情報を表示します
        /// </summary>
        /// <param name="exp"></param>
        private void PrintException(Exception exp)
        {
            Console.WriteLine("Message    :n" + exp.Message);
            Console.WriteLine("Type       :n" + exp.GetType().FullName);
            Console.WriteLine("StackTrace :n" + exp.StackTrace.ToString());
        }
    }
}

なお、1つの記事を表すArticleクラスは以下のように定義しています。

namespace RSSUtility
{
    /// <summary>
    /// 記事クラス
    /// </summary>
    class Article
    {
        public string date;         //日付
        public string title;        //タイトル
        public string description;  //記事内容
    }
}

 

動作確認



棒読みちゃん起動画面です。RSSボタンが追加されています。


 

その他タブにRSS読み上げプラグインが追加されています。
設定画面管理クラスを実装したので、設定ボタンも有効になっています。


 


設定画面です。プロパティシートに実装した設定項目の設定が可能です。


 

RSSボタンを押すとRSSを読み上げます。


 
 
以上で、設定機能と、読み上げ機能を持つプラグインが作成しました。
棒読みちゃんプラグイン作成方法のご紹介は以上です。
 
ところで、今回のようなdllを作成する開発の場合、動作確認方法がめんどくさいですね。
NiconamaCommentViewer(ニコ生コメントビューア)のプラグイン作成法 デバッグ編 でご紹介した方法を使うことで、
ブレークポイントで止めたり、ステップインしてりすることは可能です。
しかしながら、ソースコードを1行書き換えただけでも、ビルドしたdllファイルを、
本体プラグイン格納フォルダに置き直す必要があるのが欠点です。
 
そこで、dll作成プロジェクトでも、通常のプロジェクトと同じように、
デバッグ開始ボタン(or F5)を押すだけで、デバッグできるようにする方法をご紹介する予定です。

5件のコメント

  1. ピンバック:棒読みちゃんプラグインの作成法 01 | 夏研ブログ

  2. Pub.ShowSettingForm()
    使い方教えください!

    • ロフトくん

      >Zさん
      初めまして。
      プラグインの設定画面を開くメソッドですね。
      通常、設定画面は「その他」タブの右下の工具のアイコンから表示させることができます、
      そうではなく、メインの画面(「音声合成」タブ)に独自の設定ボタンを追加したい、ということでしょうか?

      その場合、以下のようにして出来ます。

      /// <summary>
      /// 棒読みちゃん本体の起動時か、
      /// プラグインを有効にしたタイミングで呼ばれる
      /// </summary>
      public void Begin()
      {
      	//:
      	//略(記事掲載ソースコードの通り)
      	
      	//独自の設定ボタンを作成
      	ToolStripButton settingButton = new ToolStripButton(Properties.Resources.feed);
      	settingButton.ToolTipText = "設定画面を開きます";
      	settingButton.Click += settingButton_Click;
      	//ボタンを本体画面に追加
      	Pub.ToolStrip.Items.Add(settingButton);
      
      	return;
      }
      
      /// <summary>
      /// ボタンクリックハンドラ
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      void settingButton_Click(object sender, EventArgs e)
      {
      	//設定画面を開く
      	Pub.ShowSettingForm(this);
      }
      
  3. ピンバック:棒読みちゃん – Site-Builder.wiki

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です