Страницы

Поиск по вопросам

Показаны сообщения с ярлыком timer. Показать все сообщения
Показаны сообщения с ярлыком timer. Показать все сообщения

воскресенье, 9 февраля 2020 г.

WPF Отследить сколько секунд мышь остается на Label

#c_sharp #wpf #многопоточность #timer


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


Ответы

Ответ 1



Всё просто. При заходе мыши в лейбл, делаем 2 задачи: первая закончится через 3 секунды, вторая закончится когда мышь покинет лейбл. Ждем конца любой из задач. После этого проверяем - если закончилась первая, то добавляем элемент, если вторая - то ничего не делаем. Все это запускается асинхронно, никаких таймеров не надо. Пример кода: class Wnd : Window { public Wnd() { var lbl = new Label() { Content = "I AM LABEL" }; var sp = new StackPanel() {Orientation = Orientation.Vertical}; sp.Children.Add(lbl); lbl.MouseEnter += async (sender, args) => { var tcs = new TaskCompletionSource(); MouseEventHandler leave = null; leave = (sender2, args2) => { lbl.MouseLeave-=leave; tcs.SetResult(0); }; lbl.MouseLeave+=leave; var delayTask = Task.Delay(3000); await Task.WhenAny(tcs.Task, delayTask); if (delayTask.IsCompleted) sp.Children.Add(new Label() {Content="3 sec passed!"}); }; this.Content = sp; } } В действии:

Ответ 2



public partial class MainWindow { private CancellationTokenSource _cts = new CancellationTokenSource(); public MainWindow() { InitializeComponent(); lbl.MouseLeave += (s, arg) => { _cts?.Cancel(); }; lbl.MouseEnter += async (s, arg) => { _cts = new CancellationTokenSource(); try { await Task.Delay(TimeSpan.FromSeconds(3), _cts.Token); //Your action here Trace.WriteLine("3 secs passed"); } catch (TaskCanceledException) { //Ignore: leave before 3 sec } }; } }

вторник, 24 декабря 2019 г.

Зависают таймеры при удержании кнопок свернуть, развернуть, закрыть

#delphi #timer


Зависают таймеры при удержании кнопок свернуть, развернуть, закрыть.
Т.е. можно остановить таймер, нажав и удерживая кнопку.  

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   n := n +1;
   Caption := IntToStr(n);
end;


Как с этим можно бороться?
    


Ответы

Ответ 1



Ну, как вариант: сделать свой таймер через отдельный поток, тогда сообщения WM_TIMER будут накапливаться и при отпускании кнопки всё накопленное вывалится разом. Вообще такие таймеры я когда-то даже видел готовые в виде компонент, но простейший пример будет выглядеть так: unit tmMain; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private StopTimer: PBoolean; procedure WMTimerProc(var Message: TWMTimer); message WM_TIMER; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} type PTimerData = ^TTimerData; TTimerData = record Owner: THandle; Interval: Cardinal; Terminated: Boolean; end; function TimerProc(Param: PTimerData): DWORD; stdcall; begin while not Param.Terminated do begin Sleep(Param.Interval); PostMessage(Param.Owner, WM_TIMER, 0, 0); end; Dispose(Param); end; function StartTimer(Interval: Cardinal; Wnd: THandle): PBoolean; var TimerData: PTimerData; ThreadID: Cardinal; begin if Interval < 10 then raise Exception.Create('Timer interval is too small'); New(TimerData); TimerData.Owner := Wnd; TimerData.Interval := Interval; TimerData.Terminated := False; Result := @TimerData^.Terminated; if CreateThread(nil, 0, @TimerProc, TimerData, 0, ThreadID) = 0 then RaiseLastOSError; end; { TForm1 } procedure TForm1.Button1Click(Sender: TObject); begin StopTimer := StartTimer(500, Handle); end; procedure TForm1.Button2Click(Sender: TObject); begin StopTimer^ := True; end; procedure TForm1.WMTimerProc(var Message: TWMTimer); begin Caption := IntToStr(GetTickCount); end; end. P.S: if Interval < 10 then из-за того что разрешающая способность Sleep() где-то 8-15 мс.

Ответ 2



Delphi у меня под рукой нет, поэтому опишу на примере платформы .NET. Любопытно. Попробовал в C# WinForms с применением System.Windows.Forms.Timer - аналогичное поведение. Дело тут вот в чём: событие тика таймера происходит в гуевом потоке. Событие нажатия на эти кнопки - тоже в потоке GUI. Кнопку нажали - произошёл вход в обработчик события - происходит ожидание отжатия - всё встало. Взял взамен System.Threading.Timer. Событие тика этого типа таймера происходит не в том потоке, в котором работает форма, а в отдельном потоке, взятом из пула. Однако, к компонентам формы можно обращаться только из того потока, в котором они созданы. Поэтому для обновления, в частности, заголовка формы нужно использовать Control.Invoke. При этом происходит передача управления в поток GUI - снова всё зависает. В C# удалось побороть это используя System.Threading.Timer и Control.BeginInvoke. Этот метод вызывается асинхронно и хотя прорисовка формы при зажатии кнопки по-прежнему не происходит, но инкремент переменной n продолжается. И когда кнопку отпускаем, выводится увеличенное значение.

Ответ 3



Добавить в описании класса формы: procedure WmNcLButtonDown(var Msg:TMessage); message WM_NCLBUTTONDOWN; procedure WmNcRButtonDown(var Msg:TMessage); message WM_NCRBUTTONDOWN; с такой реализацией: procedure TForm1.WmNcLButtonDown(var Msg: TMessage); begin Msg.Result := 0; case Msg.WParam of HTCLOSE: PostMessage(Handle, WM_SYSCOMMAND, SC_CLOSE, 0); HTMINBUTTON: PostMessage(Handle, WM_SYSCOMMAND, SC_MINIMIZE, 0); HTMAXBUTTON: if ( WindowState = wsMaximized ) then PostMessage(Handle, WM_SYSCOMMAND, SC_RESTORE, 0) else PostMessage(Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); else Msg.Result := DefWindowProc(Handle, Msg.Msg, Msg.WParam, Msg.LParam); end; end; procedure TForm1.WmNcRButtonDown(var Msg: TMessage); begin Msg.Result := 0; case Msg.WParam of HTMINBUTTON, HTCAPTION, HTSYSMENU: PostMessage(Handle, WM_CONTEXTMENU, Msg.WParam, Msg.LParam); else Msg.Result := DefWindowProc(Handle, Msg.Msg, Msg.WParam, Msg.LParam); end; end; Теперь удерживать кнопку не получится - действие будет сразу по нажатию.

Ответ 4



На английском для шарпа(но по сути для всего) ответ тут По русский Происходит это потому что пока нажата хотя бы одна из этих кнопок главный поток блокируется. Соответственно сообщения таймера не придет. Проверяется очень легко через переопределение WndProc с OutoutDebugString. Если при это все равно очень нужен таймер, то нужно делать его на потоке и при этом не забывать, что гуй залочен.

четверг, 5 декабря 2019 г.

Вызов метода в конкретное время по графику (by schedule)

#c_sharp #schedule #timer


Как вызывать метод в конкретное время каждый день или же одноразово?
    


Ответы

Ответ 1



Проблема вызова метода по расписанию не решается в общем виде. Дело в том, что в нужное время ваша программа может вовсе не работать! Поэтому, для начала, требуется придумать способ запуска программы по этому самому расписанию. Тут возможны три варианта: Планировщик Windows. Иногда кажется, что про него забыли - но он со временем становится только лучше. И если на том же XP администратор часто выключал планировщик - то в последних версиях винды он обычно все же оставлен включенным, потому что на него завязано много полезных программ и служб. Подробнее про создание задачи в планировщике windows можно увидеть тут: Creating Scheduled Tasks SQL Server Agent. Если вы используете SQL Server (а его использует довольно много проектов) - то и его агент будет где-то рядом. Процедура создания задачи описана на MSDN (там используются хранимые процедуры - то есть вам понадобится подключение к БД и права администратора сервера). В обоих случаях, вам будет нужен какой-то способ вызвать не программу целиком, а конкретный метод. Для этого можно: Выбирать метод для вызова исходя из аргументов командной строки. Тут может помочь пакет CommandLineParser из nuget. Использовать powershell. Поскольку powershell основан на CLR, из него можно обращаться к любым методам на C#: Add-Type -Path путь\к\сборке.dll [Ваш.Класс]::СтатическийМетод(Параметры) SQL Server Agent может вызвать встроенный powershell-скрипт, а планировщику windows будет нужен отдельный файл, лежащий на диске. Также, вероятно, вам придется обработать событие AppDomain.AssemblyResolve чтобы загрузить зависимые сборки. Можно написать свою службу, которая будет запущена все время и выполнять задачи. В этом случае вам может пригодиться библиотека Quartz.NET. Или же можно "накатать" свой велосипед (там основа - просто Thread.Sleep или Task.Delay в бесконечном цикле). В любом случае, основная проблема тут будет - хранение списка задач в каком-то постоянном хранилище (БД) или восстановление этого списка при запуске службы. Способ с хранением в БД больше подойдет для задач, создаваемых пользователем, а способ с восстановлением при запуске - для задач, определяемых настройками приложения.

пятница, 12 июля 2019 г.

Таймер на PHP как реализовать?

Я хочу создать функцию, которая будет запрещать флудить, то есть, чтобы можно было писать сообщение раз в 5 секунд, к примеру. Хочу понять сам принцип, а не попросить вас решить мою конкретную проблему.
Например функция отправки сообщения выглядит так:
function sendMessage($User, $Message) { if( validate_spam() < 5000 ) { /* Если в функция возвращает меньше число чем 5 секунд */ return json_encode(["error"=>"Прекратите флудить"]); } else { validate_spam(0) // Это, типа, что-то вроде запуска таймера } }
То как должна выглядеть function validate_spam($param) ?


Ответ

Принцип такой:
Вы должны проверять когда было добавлено последнее сообщение Нужно расчитать разницу в секундах между посленим добавленым сообщением и сейчас. Это можно примерно так делать: time() - $row['last_time_message'] если результат будет например 6 секунд - всё ок, добавляем сообщение в БД с текущей меткой времени, есдт результат например 4 секунды - флуд

воскресенье, 14 апреля 2019 г.

WPF Отследить сколько секунд мышь остается на Label

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


Ответ

Всё просто. При заходе мыши в лейбл, делаем 2 задачи: первая закончится через 3 секунды, вторая закончится когда мышь покинет лейбл.
Ждем конца любой из задач. После этого проверяем - если закончилась первая, то добавляем элемент, если вторая - то ничего не делаем.
Все это запускается асинхронно, никаких таймеров не надо.
Пример кода:
class Wnd : Window { public Wnd() { var lbl = new Label() { Content = "I AM LABEL" }; var sp = new StackPanel() {Orientation = Orientation.Vertical}; sp.Children.Add(lbl);
lbl.MouseEnter += async (sender, args) => { var tcs = new TaskCompletionSource();
MouseEventHandler leave = null; leave = (sender2, args2) => { lbl.MouseLeave-=leave; tcs.SetResult(0); };
lbl.MouseLeave+=leave;
var delayTask = Task.Delay(3000); await Task.WhenAny(tcs.Task, delayTask);
if (delayTask.IsCompleted) sp.Children.Add(new Label() {Content="3 sec passed!"}); };
this.Content = sp; } }
В действии:

вторник, 9 октября 2018 г.

Вызов метода в конкретное время по графику (by schedule)

Как вызывать метод в конкретное время каждый день или же одноразово?


Ответ

Проблема вызова метода по расписанию не решается в общем виде. Дело в том, что в нужное время ваша программа может вовсе не работать!
Поэтому, для начала, требуется придумать способ запуска программы по этому самому расписанию.
Тут возможны три варианта:
Планировщик Windows. Иногда кажется, что про него забыли - но он со временем становится только лучше. И если на том же XP администратор часто выключал планировщик - то в последних версиях винды он обычно все же оставлен включенным, потому что на него завязано много полезных программ и служб.
Подробнее про создание задачи в планировщике windows можно увидеть тут: Creating Scheduled Tasks SQL Server Agent. Если вы используете SQL Server (а его использует довольно много проектов) - то и его агент будет где-то рядом. Процедура создания задачи описана на MSDN (там используются хранимые процедуры - то есть вам понадобится подключение к БД и права администратора сервера).

В обоих случаях, вам будет нужен какой-то способ вызвать не программу целиком, а конкретный метод. Для этого можно:
Выбирать метод для вызова исходя из аргументов командной строки. Тут может помочь пакет CommandLineParser из nuget. Использовать powershell. Поскольку powershell основан на CLR, из него можно обращаться к любым методам на C#:
Add-Type -Path путь\к\сборке.dll [Ваш.Класс]::СтатическийМетод(Параметры)
SQL Server Agent может вызвать встроенный powershell-скрипт, а планировщику windows будет нужен отдельный файл, лежащий на диске.
Также, вероятно, вам придется обработать событие AppDomain.AssemblyResolve чтобы загрузить зависимые сборки.

Можно написать свою службу, которая будет запущена все время и выполнять задачи. В этом случае вам может пригодиться библиотека Quartz.NET. Или же можно "накатать" свой велосипед (там основа - просто Thread.Sleep или Task.Delay в бесконечном цикле).
В любом случае, основная проблема тут будет - хранение списка задач в каком-то постоянном хранилище (БД) или восстановление этого списка при запуске службы.
Способ с хранением в БД больше подойдет для задач, создаваемых пользователем, а способ с восстановлением при запуске - для задач, определяемых настройками приложения.