棒読みちゃんは、テキストを音声で読み上げてくれるソフトです。プラグインによる機能拡張に対応しており、クリップボードや、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)を押すだけで、デバッグできるようにする方法をご紹介する予定です。
ピンバック:棒読みちゃんプラグインの作成法 01 | 夏研ブログ
Pub.ShowSettingForm()
使い方教えください!
>Zさん
初めまして。
プラグインの設定画面を開くメソッドですね。
通常、設定画面は「その他」タブの右下の工具のアイコンから表示させることができます、
そうではなく、メインの画面(「音声合成」タブ)に独自の設定ボタンを追加したい、ということでしょうか?
その場合、以下のようにして出来ます。
本当にありがとうございました!!
感動!とても勉強になりました。ヾ(*´∪`*)
ピンバック:棒読みちゃん – Site-Builder.wiki