1 (изменено: Phoenixxx_Czar, 2022-06-11 22:35:48)

Тема: AHK: Class Timer

Добрейшего всем, давно хотел такой таймер, который вызывается определенное кол-во раз без лишних телодвижений в коде. Решил для этого написать класс и поделиться, дабы получить возможные доработки.

+ class Timer
class Timer
{
	__new(handler, timeout, count := "infinity", callOnInit := false)
	{
		this.Events.stop := this.stop

		this.handler := (IsObject(handler) ? handler : func(handler))
		this.timeout := timeout
		this.count   := count

		if (this.timeout < 0)
		{
			this.count := 1
		}

		funcObj := ObjBindMethod(this.Events, "onTimer")
		this.funcObj := funcObj

		if (callOnInit)
		{
			this.Events.onTimer()
		}

		this.start()
	}

	__delete()
	{
		funcObj := this.funcObj

		setTimer, % funcObj, delete
	}

	stop()
	{
		funcObj := this.funcObj

		if (funcObj)
		{
			setTimer, % funcObj, off
		}
	}

	start()
	{
		funcObj := this.funcObj

		if (funcObj && (this.count == "infinity" || this.count > 0))
		{
			setTimer, % funcObj, % this.timeout
		}
	}

	class Events
	{
		onTimer()
		{
			if (this.count != "infinity")
			{
				this.count -= 1
			}

			this.handler.call(this.count)

			if (this.count == 0)
			{
				this.stop()
			}
		}
	}

	count[]
	{
		get
		{
			return this.Events.count
		}

		set
		{
			this.Events.count := value

			return this.Events.count
		}
	}

	handler[]
	{
		get
		{
			return this.Events.handler
		}

		set
		{
			this.Events.handler := value

			return this.Events.handler
		}
	}
}

Основная цель - чтобы функция вызывалась указанное количество раз через определенное время.
Допустим данный код вызовет функцию 5 раз каждую 1с:

timerHandler := new Timer("test", 1000, 5)

test()
{
	toolTip, % A_TickCount
}

А так мы можем узнать, сколько еще раз вызовет функцию:

timerHandler := new Timer("test", 1000, 5)

test(count)
{
	toolTip, % A_TickCount " | Осталось: " count
}

Так мы можем сразу вызвать функцию при объявлении таймера:

timerHandler := new Timer("test", 1000, 5, true)

test(count)
{
	toolTip, % A_TickCount " | Осталось: " count
}

Так же можно вызывать функцию с указанными параметрами:

timerHandler := new Timer(func("test").bind("параметр 1", "параметр 2"), 1000, 5, true)

test(param1, param2)
{
	toolTip, % A_TickCount " | Param1: " Param1 " | Param2: " Param2 
}

Если не указывать количество, то таймер будет работать как и обычный - бесконечно:

timerHandler := new Timer(func("test").bind("параметр 1", "параметр 2"), 1000)

test(param1, param2)
{
	toolTip, % A_TickCount " | Param1: " Param1 " | Param2: " Param2 
}

Если не указывать количество, то таймер будет работать как и обычный и вызываться при объявлении:

timerHandler := new Timer(func("test").bind("параметр 1", "параметр 2"), 1000,, true)

test(param1, param2)
{
	toolTip, % A_TickCount " | Param1: " Param1 " | Param2: " Param2 
}
Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

2 (изменено: Phoenixxx_Czar, 2022-06-11 12:10:14)

Re: AHK: Class Timer

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

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

3

Re: AHK: Class Timer

По-моему, главная проблема такого дизайна в том, что при удалении инстанса класса таймер будет продолжать вызываться.

tm := new Timer("MyFunc", 1000)
Sleep, 1000
tm := ""

MyFunc() {
   SoundBeep
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

4 (изменено: Phoenixxx_Czar, 2022-06-11 15:05:38)

Re: AHK: Class Timer

Я уже добавил метод __delete, который остановит таймер. Хотя.. Оно почему-то не работает.
Хотя у меня в целом не срабатывает метод __delete.

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

5

Re: AHK: Class Timer

Этот метод срабатывает, когда удаляется последняя ссылка на инстанс, но если внутри класса используется ObjBindMethod(this, ...), то возникает лишняя ссылка внутри класса, и __Delete() не запускается при удалении внешнего инстанса.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: AHK: Class Timer

И как лучше поступить?

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

7

Re: AHK: Class Timer

Можно вложенный класс использовать для функции таймера.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8 (изменено: Phoenixxx_Czar, 2022-06-11 20:07:23)

Re: AHK: Class Timer

Можете привести пример? Я никогда не использовал вложенные классы и почему-то думал, что они как отдельные классы и в целом не понимаю, зачем так делать.

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

9

Re: AHK: Class Timer

Вложенные классы мало чем отличаются от обычных, можно и обычный использовать. Но тогда получится, что функционал будет разнесён на два отдельных класса, причём второй нужен только для использования из первого. Так что просто логичнее упаковать один в другой.

MyTimer := new Timer("Counter")
MyTimer.Set(100)
Sleep, 1000
MyTimer := ""
Sleep, 1000
Return

Counter() {
   static i := 0
   ToolTip % ++i
}

class Timer
{
   __New(callBack) {
      this.Events.callBack := callBack
      this.timer := ObjBindMethod(this.Events, "OnTimer")
   }
   Set(value) {
      timer := this.timer
      SetTimer, % timer, % value
   }
   Stop() {
      timer := this.timer
      SetTimer, % timer, Off
   }
   class Events {
      OnTimer() {
         callBack := this.callBack
         %callBack%()
      }
   }
   __Delete() {
      timer := this.timer
      SetTimer, % timer, Delete
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10

Re: AHK: Class Timer

То есть.. Вложенный класс будет виден только внутри первого?

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

11

Re: AHK: Class Timer

Вроде того. Извне к нему можно обратиться только через внешний (или через его инстанс).

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: Class Timer

А как я могу из вложенного класса обратиться к методу первого класса?

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

13

Re: AHK: Class Timer

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14

Re: AHK: Class Timer

Я решил это так:

class Timer
{
	__new(handler, timeout, count := "infinity", callOnInit := false)
	{
		this.Events.stop := this.stop

		this.handler := (IsObject(handler) ? handler : func(handler))
		this.timeout := timeout
		this.count   := count

		if (this.timeout < 0)
		{
			this.count := 1
		}

		funcObj := ObjBindMethod(this.Events, "onTimer")
		this.funcObj := funcObj

		if (callOnInit)
		{
			this.Events.onTimer()
		}

		this.start()
	}

	__delete()
	{
		funcObj := this.funcObj

		setTimer, % funcObj, delete
	}

	stop()
	{
		funcObj := this.funcObj

		if (funcObj)
		{
			setTimer, % funcObj, off
		}
	}

	start()
	{
		funcObj := this.funcObj

		if (funcObj && (this.count == "infinity" || this.count > 0))
		{
			setTimer, % funcObj, % this.timeout
		}
	}

	class Events
	{
		onTimer()
		{
			if (this.count != "infinity")
			{
				this.count -= 1
			}

			this.handler.call(this.count)

			if (this.count == 0)
			{
				this.stop()
			}
		}
	}

	count[]
	{
		get
		{
			return this.Events.count
		}

		set
		{
			this.Events.count := value

			return this.Events.count
		}
	}

	handler[]
	{
		get
		{
			return this.Events.handler
		}

		set
		{
			this.Events.handler := value

			return this.Events.handler
		}
	}
}
Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

15

Re: AHK: Class Timer

Проще так:


			if (this.count == 0)
			{
				SetTimer,, Off ; или Delete
			}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

16

Re: AHK: Class Timer

Я все еще думаю, стоит ли вызывать функцию с добавлением количества. Ибо это может все сломать.

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

17

Re: AHK: Class Timer

Думаю, не так всё сложно:

#Persistent
MyTimer := new Timer("Counter")
MyTimer.Set("500", 3)

Counter() {
   static i := 0
   ToolTip % ++i
}

class Timer
{
   __New(callBack) {
      this.Events.callBack := callBack
      this.timer := ObjBindMethod(this.Events, "OnTimer")
   }
   Set(value, count := "") {
      this.Events.count := count
      timer := this.timer
      SetTimer, % timer, % value
   }
   Stop() {
      this.Events.count := ""
      timer := this.timer
      SetTimer, % timer, Off
   }
   class Events {
      OnTimer() {
         callBack := this.callBack
         %callBack%()
         if (this.count && !--this.count)
            SetTimer,, Delete
      }
   }
   __Delete() {
      this.Events.count := ""
      timer := this.timer
      SetTimer, % timer, Delete
   }
}

Как видите, вложенному классу не приходится вызывать методы из внешнего.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder