- 投稿日:2021-01-17T23:12:36+09:00
Hello Xamarin!
はじめに
こんにちは。初めてQiitaを書きます。ドッキドキです。
よろしくお願いします。2020年に新卒で入社した会社でC#とWPFを使ってぼちぼちやっています。
前々から気になっていたXamarinを使ってみたので自分のアウトプットの場として記事を書こうと思った次第です。
作ったもの
今回はお試しということで、簡単な四則演算アプリを作りました。
数字を入力、演算子を選択して計算ボタンを押すと答えが出力されるだけのものになります。
コード
Xaml
MainPage.xaml<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="test1.MainPage"> <StackLayout Orientation="Vertical" Margin="30,50,0,0"> <StackLayout Orientation="Horizontal"> <Entry x:Name="left" WidthRequest="100" VerticalOptions="Start"/> <Picker x:Name="picker"/> <Entry x:Name="right" WidthRequest="100" VerticalOptions="Start" /> <Label Text="="/> <Label x:Name="answer" WidthRequest="100" /> </StackLayout> <Button x:Name="button" Text="計算" Clicked="OnButtonClicked"/> </StackLayout> </ContentPage>C#
MainPage.Xaml.csusing System; using Xamarin.Forms; namespace test1 { public partial class MainPage : ContentPage { static string[] operators = new[] { "+", "-", "×", "÷" }; public MainPage() { InitializeComponent(); // pickerに演算子をセット picker.ItemsSource = operators; // 初期値は"+" picker.SelectedIndex = 0; } void OnButtonClicked(object sender, EventArgs e) { var leftNumberText = left.Text; var rightNumberText = right.Text; if (double.TryParse(leftNumberText, out var leftNumber) && double.TryParse(rightNumberText, out var rightNumber)){ var answer = Calculate(leftNumber, rightNumber, picker.SelectedItem.ToString()); this.answer.Text = answer.ToString(); } } static double Calculate(double leftNumber, double rightNumber, string ope) { switch (ope) { case "+": return leftNumber + rightNumber; case "-": return leftNumber - rightNumber; case "×": return leftNumber * rightNumber; case "÷": if (rightNumber == 0) { throw new InvalidOperationException(); } else { return leftNumber / rightNumber; } default: throw new InvalidOperationException(); } } } }ただボタンのイベントハンドラで計算してるだけですね。
WPFとのコントロールの違いに少し戸惑いました。お試しなので、コードの内容については(例外処理とか)多めに見てくださいということで。
マシンが非力なせいか、Xamarinだから仕方ないのかエミュレーターの起動が遅くてイライラしますね。
夏のボーナスでたら新しいMac買おうかな。。。
今回はコードビハインドでやりましたが、次はViewModel作ってMVVMで四則演算したいと思います。
- 投稿日:2021-01-17T21:08:29+09:00
C#のコーディングルールをまとめてみました。
udemyの動画を視聴してアウトプットをかねて学んだことをまとめてみました。
駆け出しのエンジニアで、実力はまだまだですか記事を書いてみました今回は「C#のコーディングルール:スタイルコップアナライザーを使ってチーム全員が同じコードを書く方法」という動画をみてアウトプットを行いました。
1、「コーディングにおける名前付のルール」
・命名は英語でつける。
・基本的にはPascal型で書く。Pascalとは、頭文字は大文字で書くという意味。
(二句の英単語ならUserNameと言うように格句を大文字にして書く。)・プロジェクト名は、例えば製品名のようにアイテムの名前を記載。
名称:(ex:ps4)・ソリューション名はアイテム名.用法というように記載。
用法:(ex:ps4.説明書)・二文字の略語は大文字にする。
IE (ex:Internet Explorer)・三文字の略語の場合は先頭だけ大文字にする。
Sql(Structured Query Language)・例外(2つのみ)
・identifier →id
・Okay →Ok2.「名前空間の決め方」
会社名+製品名+プロジェクト名+フォルダ名・ローカルルール(マイクロソフトで規定されていない物)
→チームで統一されてれば良いルール。・動画内で推奨されていたルール。
private変数はアンダーバーをつける。
(ex:_userName)
→thisの代わりにアンダーバーをつけると表示がわかりやすい。。・コントロール名の名付け方
最後にコントロール名をつける。
ボタンの時:SaveButton
テキストボックスの時:ProductNameTextBox・クラス名・ファイル名の付け方。
語尾に種類をつける。
ex)ProductListForm3.「StyleCop.Analyzersの使い方」
StyleCop.Analyzersとは、VS上で使用する
コーディングルールの誤りを警告する拡張ツール。・警告が番号でエラーウィンドウに出る、
(ex:SA--)・不要なルールと感じれば、該当警告の部分を選択し非表示にする、
・class作ったらxmlコメントとアクセス修飾を記載する。
・if文等で中括弧{}を使用した際、もう一度使う際は一行開けて次の中括弧を使用する。
・メソッドの中にコメントは避ける。どうしても描きたい際はスラッシュは4つ書く。。
・「アクセスレベルの順番」
public→internal→protected internal→protected→private
の順番で記載する。以上で学んだことのまとめを終わります。
今回初めてのQiita執筆となりました。
これからも継続的にアウトプットかねてエンジニアとしてより高みを目指していきたいと思います。もしよろしければ励みになりますため、LGTMをお願いします
- 投稿日:2021-01-17T20:50:01+09:00
【Unity】カメラの揺らし方
方法
揺れるという動きを「視点が球内部のランダムな点に移動し続けた後、元の視点に戻る」という風に解釈します。掴みにくい方は以下の動画の立方体を追ってみてください。球内部の点にランダムで移動しているだけですが、立方体に注視することで目が回るような感覚に陥ると思います。
記事用
— TANUKEINA (@FH1b4mzzirXPoiM) January 16, 2021
球内部の点にランダム移動 pic.twitter.com/TODhvRherq実装
実装はシンプルで以下のコードになります。
CameraShake.csusing System.Collections; using UnityEngine; public class CameraShake : MonoBehaviour { public IEnumerator Shake(float duration, float magnitude) { Vector3 originalPosition = transform.position; float elapsed = 0f; while (elapsed < duration) { transform.position = originalPosition + Random.insideUnitSphere * magnitude; elapsed += Time.deltaTime; yield return null; } transform.position = originalPosition; } }球の半径とカメラが揺れる時間を引数に渡して使用します。
transform.position = originalPosition + Random.insideUnitSphere * magnitude;
Random.insideUnitSphere
とは半径1の球体の内部の点をランダムに返すメソッドになります。半径 = 1 * magnitude
とすることで、揺れの大きさを決定しています。elapsed += Time.deltaTime;ランダムに位置を変更する毎の時間を
elapsed
に追加しておき、指定時間を超えるまで処理を続けます。使用方法
以下のように呼び出して使用します。
StartCoroutine(cameraShake.Shake(0.3f, 0.6f));使用用途に合わせて、揺れの大きさや、揺れ時間を変更するとGOOD!
実装動画
記事用
— TANUKEINA (@FH1b4mzzirXPoiM) January 17, 2021
カメラを揺らす pic.twitter.com/skLn7U0KWu終わりに
- 注意点として、
Canvas
内部は揺れません。UIや背景等も揺らしたい場合は、Canvas
から外しましょう。- 一回作成しておくと、使い回しのきく便利なプログラムになると思います。是非参考にしてみてください。
- 投稿日:2021-01-17T17:04:29+09:00
TwitterAPIトレンドをツイート Ver.C#
Program.csusing System; using System.Collections.Generic; using System.Threading; using Newtonsoft.Json; namespace ConsoleApp1 { public class Trend { public string name { get; set; } public string url { get; set; } public object promoted_content { get; set; } public string query { get; set; } public int? tweet_volume { get; set; } } public class Location { public string name { get; set; } public int woeid { get; set; } } public class JsonData { public List<Trend> trends { get; set; } public DateTime as_of { get; set; } public DateTime created_at { get; set; } public List<Location> locations { get; set; } } public class Root { public List<JsonData> json_data { get; set; } } class Program { static void Main(string[] args) { //API key string apiKey = "<APIキー>"; //API secret key string apiSecretKey = "<APIシークレットキー>"; //Access token string accessToken = "<アクセストークン>"; //Access token secret string accessTokenSecret = "<アクセストークンシークレット>"; //認証情報 var tokens = CoreTweet.Tokens.Create(apiKey, apiSecretKey, accessToken, accessTokenSecret); //大阪のトレンド情報取得 var TrendsJson = tokens.Trends.Place(15015370); //Osaka //JSONデータを取得 string JsonData = TrendsJson.Json.ToString(); //うまくデシリアライズできないので、ちょとデータ加工。。 JsonData = "{ json_data: " + JsonData + "}"; //URLデコードする string UrlDec = System.Web.HttpUtility.UrlDecode(JsonData); //JSON文字列をデシリアライズ Root TrendData = JsonConvert.DeserializeObject<Root>(UrlDec); //練習数 int DataCnt = 3; //ツイート tokens.Statuses.Update(status => DataCnt + "単語のフリック練習スタート!?"); //1秒待つ Thread.Sleep(1000); for (int idx = 0; idx < DataCnt; idx++) { //取得トレンド総数 int TrendCnt = TrendData.json_data[0].trends.Count; //ランダム数値生成 Random Rnd = new Random(); int TrendRdm = Rnd.Next(0, TrendCnt); //トレンド文字列取得 string TrendText = TrendData.json_data[0].trends[TrendRdm].query; //#はいらないので置換 TrendText = TrendText.Replace("#",""); //ツイート tokens.Statuses.Update(status => "【" + (idx + 1) + "】" + TrendText); //1秒待つ Thread.Sleep(1000); } //ツイート tokens.Statuses.Update(status => "終了?"); } } }Follow @miki1220jp
↑よろしくお願いしますぜひぜひ▼ 参考リンク!ありがとうございます
https://developer.twitter.com/en/docs/twitter-api/v1/trends/trends-for-location/api-reference/get-trends-place - TwitterAPIトレンドのリファレンス
https://qiita.com/hogeta_/items/8e3224c4960e19b7a33a - WOEID一覧
https://json2csharp.com/json-to-csharp - JSON文字列をC#のクラスにしてくれる
- 投稿日:2021-01-17T13:45:11+09:00
C#にpythonで作った処理を組み込む【pythonnet準備+確認編 2021年1月最新】
概要
pythonで作った処理(主にはtensorflow, pytorch等のディープラーニング系)をGUIから動かして結果を表示したいということがあると思います。
PyQtでもいけるかもなんですが、C#を扱える人の方が周りに多いので組み込むことができると嬉しいと思い調べたところ、
以下の先人の方々が使い方をまとめていただいていますのでそれを元に実際にtensorflowで作ったモデルを使ってアプリを作るところまでの手順を記事したいと思います。
本記事はまずは準備と動作確認編です。
(2021年1月現在、pythonnet自体のソースコードの更新がありますので最新の手順を載せます)
C#初心者なので間違っているところもあるかもしれませんがあしからずご了承ください・・・参考①:.NET (C#)からpythonを呼び出す
参考②:Pythonnetを使って.NetからPythonコードを実行してみました(Hallo World編)
0.前提
開発の環境
visualstudio2019 community Version 16.7.7
NuGet パッケージ マネージャー 5.7.0
Anaconda Navigator 1.9.12
C#の環境
.NET Framework 4.7.2
pythonの環境
Python 3.7.6
tensorflow 2.3.0
numpy 1.18.5
前提知識
・pythonでpip等で環境構築ができ、コーディングできる人
・C#が少しわかる人1.pythonnetの導入
Nugetで導入
python3.7であればNugetを使って簡単に導入できます(2021/01/10現在)
⇒2.呼び出して使うまで飛ばしてください自分でビルドして導入
以下の記事を参考にさせていただきました。
pythonnetの古いソースコードであれば以下の記事を参考にしていただければと思います。参考:.NET (C#)からpythonを呼び出す
参考:Pythonnetを使って.NetからPythonコードを実行してみました(Hallo World編)
2021年1月10日現在の最新のソースコードでのビルド方法をここでは記述します。
1.pythonnetのgithubリポジトリから最新コードをcloneし、pythonnet.slnを開く
2.プロジェクトPython.Runtime
のプロジェクト プロパティを開き、コンパイルシンボルを手入力する。
python3.8を使いたい場合はPYTHON38;WINDOWS
と入力。3.7の場合はPYTHON38を37に変更してください。
ちなみにruntime.cs
の中身を見るとコンパイルシンボルの設定の方法がわかるようになっています。
WINDOWS
を入力していないとdllの中身がpython38となり、実際に使うときにはpython3.8を探しに行くのでうまくdllを読み込めなくなります。
3.ソリューションエクスプローラーのPython.Runtimeで右クリックしてビルドを選択する
うまくビルドが通るとルートフォルダの
src\runtime\bin\Debug\netstandard2.0
に
Python.Runtime.dll
が生成されていると思います。2.C#からpythonnetを呼び出して使ってみる
参考:.NET (C#)からpythonを呼び出す
先人の方のコードの書き方をほぼ真似するだけですが、フォームにラベルを配置してnumpyのバージョン表示、numpyでの簡単な計算をするアプリを作ってみようと思います。1.フォームのソリューションを作る
2.プラットホームの変更
3.参照の追加
Python.Runtimeが追加されていることを確認してください。
4.サンプルコード作成
参考:.NET (C#)からpythonを呼び出す
後は先人の方のコードを参考に書くだけです。Form.csusing System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; using System.Runtime.InteropServices; using Python.Runtime; namespace pythonnet_numpy_version { public partial class Form1 : Form { /// <summary> /// pythonライブラリを共有して使うための変数 /// </summary> public dynamic np; public Form1() { InitializeComponent(); } /// <summary> /// プロセスの環境変数PATHに、指定されたディレクトリを追加する(パスを通す)。 /// </summary> /// <param name="paths">PATHに追加するディレクトリ。</param> public static void AddEnvPath(params string[] paths) { var envPaths = Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator).ToList(); foreach (var path in paths) { if (path.Length > 0 && !envPaths.Contains(path)) { envPaths.Insert(0, path); } } Environment.SetEnvironmentVariable("PATH", string.Join(Path.PathSeparator.ToString(), envPaths), EnvironmentVariableTarget.Process); } // フォームが呼び出されるときに処理させる private void Form1_Load(object sender, EventArgs e) { // *-------------------------------------------------------* // * python環境の設定 // *-------------------------------------------------------* // python環境にパスを通す // TODO: 環境に合わせてパスを直すこと var PYTHON_HOME = Environment.ExpandEnvironmentVariables(@@"%userprofile%\Anaconda3\envs\tensorflow-2-3-0"); // pythonnetが、python本体のDLLおよび依存DLLを見つけられるようにする AddEnvPath( PYTHON_HOME, Path.Combine(PYTHON_HOME, @"Library\bin") ); // python環境に、PYTHON_HOME(標準pythonライブラリの場所)を設定 PythonEngine.PythonHome = PYTHON_HOME; // pythonの処理をする=numpyの定義とバージョンをラベルに表示させる using (Py.GIL()) { // numpyのインポート np = Py.Import("numpy"); // numpyのバージョンを変数に格納 dynamic np_version = np.__version__; // string型にして文字列と連結させラベルに表示 labelNumpyVersion.Text = "numpyバージョン:" + np_version.ToString(); } } // ボタンをクリックするとnumpyのメソッドを使って簡単な計算をしてラベルに表示 private void buttonCalc_Click(object sender, EventArgs e) { // 簡単な計算をして変数に格納 dynamic result = np.cos(np.pi / 3); // ToStringしなくても文字列にくっつけると勝手に型を変えてくれる labelResult.Text = "np.cos(np.pi / 3)=" + result; } } }基本は参考にしている記事の通りに書くだけですが、1つポイントとしてフォームアプリの場合は色々なコントロールでnumpyを使いたいのでインポートしたときに格納するメンバー変数を定義しておく必要があります。
まとめると以下の通りになります。(使い方によってはアクセス修飾子の適切な設定はあるかもです・・・とりあえずpublicにしときました)メンバー変数の定義
hogehoge.cspublic partial class Form1 : Form { /// <summary> /// pythonライブラリを共有して使うための変数 /// </summary> public dynamic np;numpyの定義
hogehoge.cs// pythonの処理をする=numpyの定義とバージョンをラベルに表示させる using (Py.GIL()) { // numpyのインポート np = Py.Import("numpy");numpyの使用
hogehoge.cs// 簡単な計算をして変数に格納 dynamic result = np.cos(np.pi / 3);3.終わりに
pythonからプログラミングを始めた俄かでC#を勉強してまだ数ヶ月でpythonnetをビルドするところで半日くらいかかってしまい心が折れそうでしたが何とか形できてホッとしています。
次はtensorflowとopencvを使ってC#側で画像を読み込んでpythonでセグメンテーションのモデルで推論した結果をC#のpictureboxに表示するアプリを作る記事を書こうと思います。
以上です。
不明点、おかしい点ありましたらコメントよろしくお願いします。
- 投稿日:2021-01-17T01:34:20+09:00
【C#】調査用の実行時間計測クラス
背景
レスポンスチューニングをする際に、
まず調査として各処理にどれくらいの実行時間がかかっているのか知りたいという場面がある。
その際に、ソースコードに簡単に仕込めて実行時間を計測できる仕組みがあればと思い作ってみました。作ったものとその動作
とりあえず作ってみたものがこれ
コード
public class TimeMeasure { //Property private Dictionary<string, TimeRecordItem> TimeRecordDic { get; set; } private string ClassName { get; set; } //Public Method public void Record(string key = null, string suffix = "") { if (key == null) key = new System.Diagnostics.StackFrame(1).GetMethod().Name; if (!string.IsNullOrEmpty(suffix)) key = string.Join("_", key, suffix); if (!TimeRecordDic.ContainsKey(key)) TimeRecordDic.Add(key, new TimeRecordItem(key, DateTime.Now)); else if (TimeRecordDic[key].IsEnd) TimeRecordDic[key].AddTimeSets(DateTime.Now); else TimeRecordDic[key].TimeSets.Last().SetEndTime(DateTime.Now); } public void OutputDebugWriteLine() { foreach(var content in GetOutputContents()) { System.Diagnostics.Debug.WriteLine(content); } } public void OutputConsoleWriteLine() { foreach(var content in GetOutputContents()) { System.Console.WriteLine(content); } } //Private Method private IEnumerable<string> GetOutputContents() { var TimeSets = new List<TimeSet>(); foreach (var item in TimeRecordDic.Values) { TimeSets.AddRange(item.TimeSets); } foreach (var r in TimeSets.OrderBy(rr => rr.StartTime)) { yield return string.Join(",", ClassName, r.Key, r.StartTime.ToString(), r.EndTime.ToString(), r.ExecutionTime.ToString()); } } //Constructor public TimeMeasure() { TimeRecordDic = new Dictionary<string, TimeRecordItem>(); ClassName = new System.Diagnostics.StackFrame(1).GetMethod().ReflectedType.FullName; } //Private Class private class TimeRecordItem { //Property public string Key { get; private set; } public List<TimeSet> TimeSets { get; private set; } public bool IsEnd { get { return TimeSets.Last().EndTime != null; } } //Public Method public void AddTimeSets(DateTime time) { TimeSets.Add(new TimeSet(Key, time)); } //Constructor public TimeRecordItem(string key, DateTime time) { Key = key; TimeSets = new List<TimeSet> { new TimeSet(Key, time) }; } } private class TimeSet { //Property public string Key { get; private set; } public DateTime StartTime { get; private set; } public DateTime? EndTime { get; private set; } public decimal? ExecutionTime { get { if (EndTime == null) return null; var diff = EndTime.Value - StartTime; return (decimal)diff.TotalMilliseconds / 1000; } } //Public Method public void SetEndTime(DateTime time) { EndTime = time; } //Constructor public TimeSet(string key, DateTime time) { Key = key; StartTime = time; } } }動作
以下のようなサンプルクラスで動作確認
public class Sample { private TimeMeasure _timeMeasure = new TimeMeasure(); public void Execute() { _timeMeasure.Record(); TestMethod1(); TestMethod2(); TestMethod1(); _timeMeasure.Record(); _timeMeasure.OutputConsoleWriteLine(); } private void TestMethod1() { _timeMeasure.Record(); System.Threading.Thread.Sleep(1000); _timeMeasure.Record(); } private void TestMethod2() { _timeMeasure.Record(); _timeMeasure.Record(suffix: "処理1"); System.Threading.Thread.Sleep(1000); _timeMeasure.Record(suffix: "処理1"); System.Threading.Thread.Sleep(1000); _timeMeasure.Record(); } }実行結果
ConsoleAppForDebug.Program+Sample,Execute,2021/01/17 1:14:04,2021/01/17 1:14:08,4.0148715 ConsoleAppForDebug.Program+Sample,TestMethod1,2021/01/17 1:14:04,2021/01/17 1:14:05,1.0005907 ConsoleAppForDebug.Program+Sample,TestMethod2,2021/01/17 1:14:05,2021/01/17 1:14:07,2.0018408 ConsoleAppForDebug.Program+Sample,TestMethod2_処理1,2021/01/17 1:14:05,2021/01/17 1:14:06,1.0007147 ConsoleAppForDebug.Program+Sample,TestMethod1,2021/01/17 1:14:07,2021/01/17 1:14:08,1.000481どのクラスのどのメソッドで実行にどれくらいかかったかがちゃんと計測できてそう
説明
いくつかピックアップして説明します
簡単に仕込みたい!
調査なので時間をかけずに簡単に仕込めるようにしたかったです。
同じコードの貼り付けのみで仕込んでいけることを目指しました。public TimeMeasure() { TimeRecordDic = new Dictionary<string, TimeRecordItem>(); ClassName = new System.Diagnostics.StackFrame(1).GetMethod().ReflectedType.FullName; }コンストラクタでStackFrameを用いて呼び出し元のクラスを取得します。
public void Record(string key = null, string suffix = "") { if (key == null) key = new System.Diagnostics.StackFrame(1).GetMethod().Name; if (!string.IsNullOrEmpty(suffix)) key = string.Join("_", key, suffix); if (!TimeRecordDic.ContainsKey(key)) TimeRecordDic.Add(key, new TimeRecordItem(key, DateTime.Now)); else if (TimeRecordDic[key].IsEnd) TimeRecordDic[key].AddTimeSets(DateTime.Now); else TimeRecordDic[key].TimeSets.Last().SetEndTime(DateTime.Now); }測定の開始・終了用のメソッドでもStackFrameを用いて呼び出し元のメソッド名を取得するようにしてます。
加えて、開始か終了の判断をTimeSetクラスで行うようにすることでRecord()を呼び出すだけでいいようにしてます。
結果サンプルコードのように_timeMeasure.Record();を計測したい部分に張り付けていくだけで仕込むことができます。メソッド内の特定の処理を計測したい!
メソッド内の特定の部分の処理時間を計測したい場合もあるので、
Recordメソッドではsuffix引数を用意してます。
suffixが渡された場合は、呼び出し元メソッド名と渡された文字列を"_"で結合してkeyとして使用します。
※サンプルコードのTestMethod2の部分private void TestMethod2() { _timeMeasure.Record(); _timeMeasure.Record(suffix: "処理1"); System.Threading.Thread.Sleep(1000); _timeMeasure.Record(suffix: "処理1"); System.Threading.Thread.Sleep(1000); _timeMeasure.Record(); }ConsoleAppForDebug.Program+Sample,TestMethod2,2021/01/17 1:14:05,2021/01/17 1:14:07,2.0018408 ConsoleAppForDebug.Program+Sample,TestMethod2_処理1,2021/01/17 1:14:05,2021/01/17 1:14:06,1.0007147