Учимся работать с таймером в DFL2

При работе в Entice Designer рано или поздно приходится убедиться, в том, насколько мало компонентов дизайна содержит эта небольшая среда визуального проектирования графического интерфейса, и чем раньше приходит в голову эта мысль, тем быстрее закрадываются в голову нелегкие сомнения о правильности выбора.

Все мы знаем, что Entice проектировалась с учетом минимализма и весьма скромных требований, однако, это не означает, что DFL2 (и Entice, в частности) обделен функциональностью, как может показаться тем, кто работал раньше с другими средами разработки, напротив — в Entice достаточно разных полезностей, а некоторые компоненты просто не вынесены на панель дизайнера из редкого их использования.

Те, кто работали с продвинутыми RAD-средами (Rapid Application Development — быстрая разработка приложений), наверняка видели огромное множество компонентов на панелях таких визуальных дизайнеров, но немногие знают, что на такие панели вынесены не только компоненты, которые имеют графическое представление, но и такие, которые не имеют экранного представления, однако, имеют некоторое поведение.

Наверное, сказанное поставило в тупик: как это так, компонент есть на панели, но при этом графическое представление как элемент интерфейса не имеет?

На самом деле, во многих графических тулкитах, есть так называемые невизуальные компоненты, которые при работе программы не отображаются на форме, но наличие явного отображения на панели компонентов имеет ряд весьма существенных плюсов: удобство размещения компонента на форме (с учетом того, что код для него сгенерируется автоматически) и совершенно тривиальные операции по настройке размещаемого элемента через панель свойств. К невизуальным компонентам относятся весьма и весьма полезные компоненты, такие как диалог выбора файла, диалог выбора папки и многие другие.

Одним из таких компонентов является таймер — компонент, который способен отсчитывать интервалы времени (в миллисекундах) и запускать некоторые события по их истечению. Таймер привлекателен тем, что имеет минимум свойств и только одно событие, которое называется tick (соответственно, точно также называется и свойство, которое отвечает за привязку собственных событий к таймеру) и которое срабатывает при истечении заданного временного интервала.

Безусловно, таймер — один из самых ходовых и полезных компонентов, однако, есть и ложка дегтя — программисту, работающему в Entice Designer придется вручную добавлять и настраивать таймер…

Для примера воспользуемся таймером по его прямому назначению (т.е. будем использовать его для отсчета времени) и создадим для демонстрации возможностей компонента простую программку, которая будет отсчитывать время, этакий секундомер с секундами и минутами, а также кнопкой сброса.

Для начала создадим в Entice Designer простую форму, на которой разместим три надписи и две кнопки: надпись «00», надпись «:», надпись «00», кнопку «Пуск» и кнопку «Стоп». После создания формы с компонентами прикрепим к кнопкам события: onStartButton — событие, которое сработает при нажатии кнопки «Пуск» и onClearButton — событие, которое сработает при нажатии кнопки «Стоп».

Наш импровизированный секундомер будет работать следующим образом: при нажатии кнопки «Пуск» будет активирован таймер (т.е свойство enabled будет равно true), если таймер еще не был запущен (в противном случае, не произойдет ничего, ибо таймер уже запущен), и будет дезактивирована кнопка «Сброс» (при этом кнопка «Пуск» превратится в кнопку «Stop», которая будет выключать таймер). Таймер будет отсчитывать интервал в половину секунды — этот промежуток времени мы будем использовать как триггер для включения-выключения надписи «:», что создаст эффект будто двоеточие секундомера мигает.

Так как двоеточие будет мигать с частотой в 500 миллисекунд, то время с момента первого появления надписи «:» до второго ее отображения равно 1 секунде, и именно, это поможет отсчитывать секундные интервалы — нужно будет просто посмотреть, активно ли двоеточие в настоящие момент, и если оно отображается на экране (т.е. если надпись имеет свойство visible установленное в true), то значит, секунда уже истекла.

Для отчета времени потребуется ряд переменных, на уровне доступном для всего класса формы, и эти переменные будут хранить минуты и секунды соответственно (переменные m и s). Эти переменные мы будем использовать в самом событии таймера: если прошла одна секунда, то необходимо увеличить переменную s на единицу, если прошла минута — то переменную m.

Однако, если число секунд или минут меньше десяти, то для отображения в том формате, который используется в часах, необходимо перед числом минут или секунд добавить 0, для чего проще число перевести в строку и слева добавить символ «0». Помимо этого, секундомер должен считать время циклически, что означает, сброс в ноль переменных m и s, если их значения превысят 59 (число секунд в минутах и число минут в часе) — и такой сброс должен происходить в самом событии таймера (кроме того, в случае обнуления этих переменных, надписи ответственные за минуты и секунды должны выглядеть вот так «00», что мы несомненно учитываем в том же событии).

Код таймера:

/*
	Generated by Entice Designer
	Entice Designer written by Christopher E. Miller
	www.dprogramming.com/entice.php
*/

import dfl.all;


class MyForm: dfl.form.Form
{
	// Do not modify or move this block of variables.
	//~Entice Designer variables begin here.
	dfl.button.Button button1;
	dfl.button.Button button2;
	dfl.label.Label label1;
	dfl.label.Label label2;
	dfl.label.Label label3;
	dfl.timer.Timer timer1;
	//~Entice Designer variables end here.
	
	int m, ms, s;
	
	this()
	{
		initializeMyForm();
		
		//@  Other MyForm initialization code here.
		m = 0;
		ms = 0;
		s = 0;
		
		label1.text = "00";
		label2.visible = true;
		label3.text = "00";
		
	}
	
	
	private void initializeMyForm()
	{
		// Do not manually modify this function.
		//~Entice Designer 0.8.5.02 code begins here.
		//~DFL Form
		formBorderStyle = dfl.all.FormBorderStyle.FIXED_SINGLE;
		text = "My Form";
		clientSize = dfl.all.Size(282, 188);
		//~DFL dfl.button.Button=button1
		button1 = new dfl.button.Button();
		button1.name = "button1";
		button1.text = "Пуск";
		button1.bounds = dfl.all.Rect(8, 136, 120, 32);
		button1.parent = this;
		button1.click ~= &this.onStartButton;
		//~DFL dfl.button.Button=button2
		button2 = new dfl.button.Button();
		button2.name = "button2";
		button2.text = "Сброс";
		button2.bounds = dfl.all.Rect(136, 136, 128, 32);
		button2.parent = this;
		button2.click ~= &this.onClearButton;
		//~DFL dfl.label.Label=label1
		label1 = new dfl.label.Label();
		label1.name = "label1";
		label1.font = new dfl.all.Font("Microsoft Sans Serif", 48f, dfl.all.FontStyle.REGULAR);
		label1.text = "00";
		label1.textAlign = dfl.all.ContentAlignment.MIDDLE_CENTER;
		label1.bounds = dfl.all.Rect(16, 16, 104, 112);
		label1.parent = this;
		//~DFL dfl.label.Label=label3
		label3 = new dfl.label.Label();
		label3.name = "label3";
		label3.font = new dfl.all.Font("Microsoft Sans Serif", 48f, dfl.all.FontStyle.REGULAR);
		label3.text = "00";
		label3.textAlign = dfl.all.ContentAlignment.MIDDLE_CENTER;
		label3.bounds = dfl.all.Rect(160, 16, 104, 112);
		label3.parent = this;
		//~DFL dfl.label.Label=label2
		label2 = new dfl.label.Label();
		label2.name = "label2";
		label2.font = new dfl.all.Font("Microsoft Sans Serif", 48f, dfl.all.FontStyle.REGULAR);
		label2.text = ":";
		label2.textAlign = dfl.all.ContentAlignment.MIDDLE_CENTER;
		label2.bounds = dfl.all.Rect(120, 8, 40, 120);
		label2.parent = this;
		//~Entice Designer 0.8.5.02 code ends here.
		
		timer1 = new Timer();
		timer1.interval = 500;
		timer1.tick ~= &this.onTimerTick;
	}
	
	private void onStartButton(Object sender, EventArgs ea)
	{
		if (timer1.enabled)
		{
			timer1.enabled = false;
			button1.text = "Start";
			button2.enabled = true;
		} 
		else
		{
			timer1.enabled = true;
			button1.text = "Stop";
			button2.enabled = false;
		}
	}
	
	
	private void onClearButton(Object sender, EventArgs ea)
	{
		m = 0;
		ms = 0;
		s = 0;
		
		label1.text = "00";
		label3.text = "00";
	}
	
	private void onTimerTick(Object sender, EventArgs ea)
	{
		import std.conv : to;
		if (label2.visible)
		{
			if (s < 59)
			{
				s++;
				if (s < 10)
				{
					label3.text = "0" ~ to!string(s);
				}
				else
				{
					label3.text = to!string(s);
				}
			}
			else
			{
				if (m < 59)
				{
					m++;
					if (s < 10)
				    {
					    label1.text = "0" ~ to!string(m);
				    }
				    else
				    {
					    label1.text = to!string(m);
					}
					s = 0;
					label3.text = "00";
				}
				else
				{
					m = 0;
					label1.text = "0";
				}
			}
			label2.visible = false;
		}
		else
		{
			label2.visible = true;
		}
	}
}


int main()
{
	int result = 0;
	
	try
	{
		Application.enableVisualStyles();
		Application.run(new MyForm);
	}
	catch(DflThrowable o)
	{
		msgBox(o.toString(), "Fatal Error", MsgBoxButtons.OK, MsgBoxIcon.ERROR);
		
		result = 1;
	}
	
	return result;
}

И таймер в работе:

h50y3hilqqi_0_op05lcv9swk4_0_o

И напоследок, небольшое напутствие: старайтесь всегда делать очистку используемых ресурсов, перегружая процедуру onClosing (в частности, в этой процедуре можно отключить таймер вне зависимости от того, в каком состоянии находился до этого таймер). Если этого не сделать, то если таймер не был остановлен нажатием на кнопку, программа сгенерирует исключение о нехватке памяти, которое будет выглядеть примерно так:

zl0mltz3jg_0_o

Чтобы так не получалось, достаточно добавить вот такой код:

protected override void onClosing(EventArgs ea)
{
    super.OnClosing(ea);
    timer1.enabled = false;
}

Добавить комментарий