今までコマンドプロンプトでは描画処理をやったことがなかったので、試しに作ってみました。両方同じ動きをしますが、まずはソースを2種類載せます。
TimerCallbackを使用したバージョン.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace ConsoleApplication88
{
class Program
{
static void Main(string[] args)
{
// ウィンドウサイズを適切に設定
Console.WindowHeight = 10;
Console.WindowWidth = 24;
// 時計描画
var timer = new Timer(new TimerCallback(DrawClock), null, 0, 1000);
while (true)
{
Console.Write("終了しますか?[Y/N] : ");
// 終了待ち
if ("Y" == Console.ReadLine().ToUpper())
{
timer.Dispose();
break;
}
else
{
Console.CursorTop = 0;
Console.Clear();
}
}
}
///
/// コンソールのウィンドウプロセスを取得
///
///
static Process GetConsoleProcess()
{
foreach (Process p in Process.GetProcesses())
if (p.MainWindowTitle == Console.Title)
return p;
return null;
}
///
/// 時計描画
///
static void DrawClock(object state)
{
// コンソールのウィンドウハンドルから描画クラス取得
var g = Graphics.FromHwnd(GetConsoleProcess().MainWindowHandle);
// チラつき防止のためのバッファ領域
var bmp = new Bitmap(150, 150);
var gBuffer = Graphics.FromImage(bmp);
// 座標系の原点を変える
gBuffer.TranslateTransform(110 / 2, 110 / 2, MatrixOrder.Append);
var center = new Point(0, 0);
var time = DateTime.Now;
var r = Math.Min(110, 110) / 2;
// 時計の部品を描画する
DrawClockBase(gBuffer, time);
DrawSecondLine(gBuffer, time, r, center);
DrawMinuteLine(gBuffer, time, r, center);
DrawHour(gBuffer, time, r, center);
// 実描画
g.DrawImage(bmp, new Point(40, 30));
}
///
/// 秒針描画
///
///
///
///
///
private static void DrawSecondLine(Graphics gBuffer, DateTime time, int r, Point center)
{
using (var bluePen = new Pen(Color.AliceBlue, 1))
{
var secAng = 2.0f * Math.PI * time.Second / 60.0f;
var secHandLength = Convert.ToInt32(0.9f * r);
var secHand = new Point(Convert.ToInt32(secHandLength * Math.Sin(secAng)), Convert.ToInt32(-secHandLength * Math.Cos(secAng)));
gBuffer.DrawLine(bluePen, center, secHand);
}
}
///
/// 分針描画
///
///
///
///
///
private static void DrawMinuteLine(Graphics gBuffer, DateTime time, int r, Point center)
{
using (var greenPen = new Pen(Color.LightGreen, 2))
{
var minAng = 2.0f * Math.PI * (time.Minute + time.Second / 60.0f) / 60.0f;
var minHandLength = Convert.ToInt32(0.7f * r);
var minHand = new Point(Convert.ToInt32(minHandLength * Math.Sin(minAng)), Convert.ToInt32(-minHandLength * Math.Cos(minAng)));
gBuffer.DrawLine(greenPen, center, minHand);
}
}
///
/// 時針描画
///
///
///
///
///
private static void DrawHour(Graphics gBuffer, DateTime time, int r, Point center)
{
using (var redPen = new Pen(Color.LightPink, 2))
{
var hourAng = 2.0f * Math.PI * (time.Hour + time.Minute / 60.0f) / 12.0f;
var hourHandLength = Convert.ToInt32(0.5f * r);
var hourHand = new Point(Convert.ToInt32(hourHandLength * Math.Sin(hourAng)), Convert.ToInt32(-hourHandLength * Math.Cos(hourAng)));
gBuffer.DrawLine(redPen, center, hourHand);
}
}
///
/// 時計の針以外を描画
///
///
static void DrawClockBase(Graphics gBuffer, DateTime time)
{
// 円
using (var circlePen = new Pen(Color.AliceBlue, 2))
{
gBuffer.Clear(Color.Black);
gBuffer.FillEllipse(Brushes.AliceBlue, -2.5f, -2.5f, 5f, 5f);
gBuffer.DrawEllipse(circlePen, -110 / 2, -110 / 2, 110, 110);
}
// フォント指定
var msGothicFont = new Font("MS ゴシック", 15, FontStyle.Bold, GraphicsUnit.Pixel);
// 目盛り
using (var scalePen = new Pen(Color.Aqua, 1))
{
Enumerable.Range(1, 60).ToList().ForEach(i =>
{
var k = i * 6 * (Math.PI / 180);
// 見易いように5目盛りごとに長さを変える
var length = 0 == i % 5 ? 35 : 40;
var x = Convert.ToInt32(Math.Sin(k) * length);
var y = -Convert.ToInt32(Math.Cos(k) * length);
var x1 = Convert.ToInt32(Math.Sin(k) * 45);
var y1 = -Convert.ToInt32(Math.Cos(k) * 45);
gBuffer.DrawLine(scalePen, x, y, x1, y1);
// 5目盛りごとに数字を表示
if (0 == i % 5)
{
var timeText = (i / 5).ToString();
var fontSize = gBuffer.MeasureString(timeText, msGothicFont);
gBuffer.DrawString(timeText, msGothicFont, Brushes.White, new PointF(x1 - fontSize.Width / 2, y1 - fontSize.Height / 2));
}
});
}
var textSize = gBuffer.MeasureString("HH:mm:ss", msGothicFont);
gBuffer.DrawString(time.ToString("HH:mm:ss"), msGothicFont, Brushes.White, new PointF(-textSize.Width / 2, 65));
}
}
}
Thread.Abortによる強制終了を行うバージョン.cs
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Threading;
namespace ConsoleApplication92
{
class Program
{
private const int MAX_SIZE = 110;
private const int DRAW_START = 40;
private const int DIGITAL_TIME_TOP = 65;
private const int FONT_SIZE = 15;
private const int LONG_SCALE = 40;
private const int SHORT_SCALE = 35;
private const int SCALE_LENGTH = 45;
private const int CIRCLE_WIDTH = 3;
private const int WINDOW_HEIGHT = 10;
private const int WINDOW_WIDTH = 24;
private const string FONT_NAME = "MS ゴシック";
private const string TIME_FORMAT = "HH:mm:ss";
///
/// メイン
///
///
static void Main(string[] args)
{
// ウィンドウサイズを適切に設定
Console.WindowHeight = WINDOW_HEIGHT;
Console.WindowWidth = WINDOW_WIDTH;
// 時計描画スレッド
var thread = new Thread(DrawClock);
thread.Start();
// 入力待ちメインスレッド
while (true)
{
Console.Write("終了しますか?[Y/N] : ");
// 終了待ち
if ("Y" == Console.ReadLine().ToUpper())
{
ThreadStop(thread);
thread.Join();
break;
}
else
{
Console.CursorTop = 0;
Console.Clear();
}
}
}
///
/// スレッド中止
///
///
static void ThreadStop(Thread thread)
{
try
{
thread.Abort();
}
catch (ThreadAbortException)
{
// あえてスルーする
}
}
///
/// 時計描画
///
static void DrawClock()
{
// コンソールのウィンドウハンドルから描画クラス取得
var process = Process.GetProcesses().ToList().Where(p => p.MainWindowTitle == Console.Title).SingleOrDefault();
var g = Graphics.FromHwnd(process.MainWindowHandle);
// チラつき防止のためのバッファ領域
var msGothicFont = new Font(FONT_NAME, FONT_SIZE, FontStyle.Bold, GraphicsUnit.Pixel);
var fontSize = g.MeasureString(string.Empty, msGothicFont);
var bmp = new Bitmap(MAX_SIZE + CIRCLE_WIDTH, MAX_SIZE + CIRCLE_WIDTH + DIGITAL_TIME_TOP + Convert.ToInt32(fontSize.Height));
var gBuffer = Graphics.FromImage(bmp);
// 座標系の原点を変える
gBuffer.TranslateTransform((MAX_SIZE + CIRCLE_WIDTH) / 2, (MAX_SIZE + CIRCLE_WIDTH) / 2, MatrixOrder.Append);
var center = new Point(0, 0);
var r = MAX_SIZE / 2;
while (true)
{
var time = DateTime.Now;
// 時計の部品を描画する
DrawClockBase(gBuffer, time);
DrawSecondLine(gBuffer, time, r, center);
DrawMinuteLine(gBuffer, time, r, center);
DrawHourLine(gBuffer, time, r, center);
// 実描画
g.DrawImage(bmp, new Point(DRAW_START, DRAW_START));
// 一秒置き
Thread.Sleep(1000);
}
}
///
/// 秒針描画
///
///
///
///
///
private static void DrawSecondLine(Graphics gBuffer, DateTime time, int r, Point center)
{
using (var bluePen = new Pen(Color.AliceBlue, 1))
{
var secAng = 2.0f * Math.PI * time.Second / 60.0f;
var secHandLength = Convert.ToInt32(0.9f * r);
var secHand = new Point(Convert.ToInt32(secHandLength * Math.Sin(secAng)), Convert.ToInt32(-secHandLength * Math.Cos(secAng)));
gBuffer.DrawLine(bluePen, center, secHand);
}
}
///
/// 分針描画
///
///
///
///
///
private static void DrawMinuteLine(Graphics gBuffer, DateTime time, int r, Point center)
{
using (var greenPen = new Pen(Color.LightGreen, 2))
{
var minAng = 2.0f * Math.PI * (time.Minute + time.Second / 60.0f) / 60.0f;
var minHandLength = Convert.ToInt32(0.7f * r);
var minHand = new Point(Convert.ToInt32(minHandLength * Math.Sin(minAng)), Convert.ToInt32(-minHandLength * Math.Cos(minAng)));
gBuffer.DrawLine(greenPen, center, minHand);
}
}
///
/// 時針描画
///
///
///
///
///
private static void DrawHourLine(Graphics gBuffer, DateTime time, int r, Point center)
{
using (var redPen = new Pen(Color.LightPink, 2))
{
var hourAng = 2.0f * Math.PI * (time.Hour + time.Minute / 60.0f) / 12.0f;
var hourHandLength = Convert.ToInt32(0.5f * r);
var hourHand = new Point(Convert.ToInt32(hourHandLength * Math.Sin(hourAng)), Convert.ToInt32(-hourHandLength * Math.Cos(hourAng)));
gBuffer.DrawLine(redPen, center, hourHand);
}
}
///
/// 時計の針以外を描画
///
///
static void DrawClockBase(Graphics gBuffer, DateTime time)
{
// 外円
DrawCircle(gBuffer);
// 目盛り
DrawScale(gBuffer);
// デジタル時計
DrawDigitalTime(gBuffer, time);
}
///
/// 外円描画
///
///
static void DrawCircle(Graphics gBuffer)
{
using (var circlePen = new Pen(Color.AliceBlue, CIRCLE_WIDTH))
{
gBuffer.Clear(Color.Black);
gBuffer.FillEllipse(Brushes.AliceBlue, -2.5f, -2.5f, 5f, 5f);
gBuffer.DrawEllipse(circlePen, -MAX_SIZE / 2, -MAX_SIZE / 2, MAX_SIZE, MAX_SIZE);
}
}
///
/// 目盛り描画
///
///
static void DrawScale(Graphics gBuffer)
{
var msGothicFont = new Font(FONT_NAME, FONT_SIZE, FontStyle.Bold, GraphicsUnit.Pixel);
using (var scalePen = new Pen(Color.Aqua, 1))
{
Enumerable.Range(1, 60).ToList().ForEach(i =>
{
var angle = i * 6 * (Math.PI / 180);
// 見易いように5目盛りごとに長さを変える
var length = 0 == i % 5 ? SHORT_SCALE : LONG_SCALE;
var x = Convert.ToInt32(Math.Sin(angle) * length);
var y = -Convert.ToInt32(Math.Cos(angle) * length);
var x1 = Convert.ToInt32(Math.Sin(angle) * SCALE_LENGTH);
var y1 = -Convert.ToInt32(Math.Cos(angle) * SCALE_LENGTH);
gBuffer.DrawLine(scalePen, x, y, x1, y1);
// 5目盛りごとに数字を表示
if (0 == i % 5)
{
var timeText = (i / 5).ToString();
var fontSize = gBuffer.MeasureString(timeText, msGothicFont);
gBuffer.DrawString(timeText, msGothicFont, Brushes.White, new PointF(x1 - fontSize.Width / 2, y1 - fontSize.Height / 2));
}
});
}
}
///
/// デジタル時計描画
///
///
///
static void DrawDigitalTime(Graphics gBuffer, DateTime time)
{
var msGothicFont = new Font(FONT_NAME, FONT_SIZE, FontStyle.Bold, GraphicsUnit.Pixel);
var textSize = gBuffer.MeasureString(TIME_FORMAT, msGothicFont);
gBuffer.DrawString(time.ToString(TIME_FORMAT), msGothicFont, Brushes.White, new PointF(-textSize.Width / 2, DIGITAL_TIME_TOP));
}
}
}
基本構造はどちらも同じです。違うのはスレッドの扱いのみ。
Thread.Abort版の方が無駄が少ないように思うので、もう少しいじってみようと思います。
現時点ではここまで。