Отловить внутри компонента нажатие Ctrl

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 07.09.2009 15:34:36

Здравствуйте.

Я разрабатываю компонент для лазаруса (потомок TCustomPanel), и мне нужно, чтоб один из дочерних контролов (TImage) моей панели реагировал на нажатие и отпускание клавиши Ctrl - перерисовывал себя.

При этом хотелось бы сделать автономный и кроссплатформенный компонент. То есть я не хочу использовать события OnKeyDown и OnKeyUp родительской формы, влазить в обработчик событий TApplication и использовать win32 api (GetAsyncKeyState).
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Отловить внутри компонента нажатие Ctrl

Сообщение Vadim » 07.09.2009 17:30:24

VKB писал(а):При этом хотелось бы сделать автономный и кроссплатформенный компонент. То есть я не хочу использовать события OnKeyDown и OnKeyUp родительской формы,

А почему Вы думаете, что это не кроссплатформенное средство?
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 07.09.2009 21:45:17

Vadim писал(а):
VKB писал(а):При этом хотелось бы сделать автономный и кроссплатформенный компонент. То есть я не хочу использовать события OnKeyDown и OnKeyUp родительской формы,

А почему Вы думаете, что это не кроссплатформенное средство?

А почему Вы думаете, что я думаю, что это не кроссплатформенное средство? Говоря о некроссплатформенности я имел ввиду только GetAsyncKeyState.
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Отловить внутри компонента нажатие Ctrl

Сообщение Vadim » 08.09.2009 04:57:07

VKB
Вы это так ловко объединили, что другой мысли у меня возникнуть и не могло. :)
Тогда задам вопрос по другому - а почему Вы не хотите использовать OnKeyDown?
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 08.09.2009 16:09:20

Vadim писал(а):VKB
Вы это так ловко объединили, что другой мысли у меня возникнуть и не могло. :)
Тогда задам вопрос по другому - а почему Вы не хотите использовать OnKeyDown?

Почему я не хочу использовать OnKeyDown родительской формы я объяснил - у меня автономный компонент, и негоже ему залазить не в свою епархию.
А для того, чтоб использовать собственные OnKeyDown и OnKeyUp нужно чтоб контрол имел фокус ввода. Что в общем-то логично. У TPanel и TImage по умолчанию свойство TabStop установлено в False. А свойства OnKeyDown и OnKeyUp неопубликованы. В принципе это можно обойти, присваивая эти свойства в момент инициализации. Но тогда всё равно срабатывать эта штуковина будет только при переходе фокуса ввода на мой компонент. И в этом есть свои неудобства - переключение фокуса ввода обычно выполняется при нажатии на Tab или клике мышкой. Чтобы отлавливались нажатия на клавиатуру мне придётся перехватывать фокус из события OnMouseMove. Пользователю моего компонента будет не совсем удобно, что мой компонент отхватывает себе фокус при проведении поверх него мышки. Тем более, что клавиатурой там делать нечего и в плане общепрограммной логики ему это не нужно ни для чего кроме реагирования на клавишу Ctrl.
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Отловить внутри компонента нажатие Ctrl

Сообщение скалогрыз » 08.09.2009 16:39:24

Код: Выделить всё
TWinControl = class(TControl)
...
protected
    procedure ControlKeyDown(var Key: Word; Shift: TShiftState); virtual;
    procedure ControlKeyUp(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyDown(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyDownBeforeInterface(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyDownAfterInterface(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyPress(var Key: char); virtual;
    procedure KeyUp(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyUpBeforeInterface(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyUpAfterInterface(var Key: Word; Shift: TShiftState); virtual;
...


можно override-ить любой метод, по-вкусу.
настроятельно рекомендуется вызывать inherited внутри каждого из переопределённых методов.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 08.09.2009 16:59:29

скалогрыз писал(а):
Код: Выделить всё
TWinControl = class(TControl)
...
protected
    procedure ControlKeyDown(var Key: Word; Shift: TShiftState); virtual;
    procedure ControlKeyUp(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyDown(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyDownBeforeInterface(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyDownAfterInterface(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyPress(var Key: char); virtual;
    procedure KeyUp(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyUpBeforeInterface(var Key: Word; Shift: TShiftState); virtual;
    procedure KeyUpAfterInterface(var Key: Word; Shift: TShiftState); virtual;
...


можно override-ить любой метод, по-вкусу.
настроятельно рекомендуется вызывать inherited внутри каждого из переопределённых методов.

Вы смотрели код тех процедур, что предлагаете?
Например
Код: Выделить всё
procedure TWinControl.KeyDown(var Key: Word; shift : TShiftState);
begin
  if Assigned(FOnKeyDown) then FOnKeyDown(Self, Key, Shift);
end;
То есть если уж у меня установлено свойство OnKeyDown, то и штатная процедура, унаследованная от TWinControl его вызовет. Но только если фокус ввода будет принадлежать моему контролу. А если нет, то и она (и переопределённая) не получит управления (и это правильно).
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Отловить внутри компонента нажатие Ctrl

Сообщение скалогрыз » 08.09.2009 17:30:58

и? это не то что нужно?

Негоже контролу или компоненту изменять чей-либо OnKeyDown/OnKeyUp (тем более свой), если этот OnKeyDown/OnKeyUp, может быть изменён пользователем.
Кстати это касается ВСЕХ событий а не только OnKeyDown/OnKeyUp

Если нужно, по-особому, обработать нажатие клавиши для этого специально сделали защищённые виртуальные методы!

Защищённые - чтобы пользователь не мог их некорректно вызывать.
Виртуальные - чтобы наследники от TWinControl-а могли изменять поведение по-умолчанию (т.е. ничегонеделанье)
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 08.09.2009 23:46:32

скалогрыз писал(а):и? это не то что нужно?
Мне нужно, чтоб мой контрол реагировал на нажатие и отпускание клавиши Ctrl даже если фокус ввода находится не у него. Более того, лучше, чтоб ему вообще фокус ввода не попадал никогда - он ему не нужен.
скалогрыз писал(а):Негоже контролу или компоненту изменять чей-либо OnKeyDown/OnKeyUp (тем более свой), если этот OnKeyDown/OnKeyUp, может быть изменён пользователем.
В целом я согласен, но не согласен с акцентами. Как раз свой OnKeyDown и OnKeyUp компонент более вправе изменять, если по логике работы пользователю компонента эти свойства будут не нужны. Так как мой компонент является потомком TPanel и я всё равно создаю для него новый класс, то действительно лучше переписать методы KeyUp и KeyDown. Это правильное замечание, но оно не имеет никакой пользы для решения той проблемы, с которой я столкнулся, потому что итог будет такой же. А скажем внутрь своей панели я вставляю TImage и мне не надо от этого TImage ничего, кроме перехвата событий OnMouseDown и OnMouseMove. Так зачем мне создавать потомка TImage только для того, чтоб переопределить там методы, если я могу просто присвоить свойствам стандартного TImage адреса процедур с тем же эффектом?
скалогрыз писал(а):Кстати это касается ВСЕХ событий а не только OnKeyDown/OnKeyUp

Если нужно, по-особому, обработать нажатие клавиши для этого специально сделали защищённые виртуальные методы!

Защищённые - чтобы пользователь не мог их некорректно вызывать.
Виртуальные - чтобы наследники от TWinControl-а могли изменять поведение по-умолчанию (т.е. ничегонеделанье)

Я объяснил, почему мне этого не достаточно. Мне было бы вполне достаточно изменить свойства родительской формы, если бы я просто вставлял в свою форму TPanel, TImage и т.д., но я хочу сделать компонент, который будет вставляться потом в любую форму и не хочу заранее сужать пользователю моего компонента возможности по обработке его формой KeyDown и KeyUp. Ведь разработчик родительской формы не ожидает, что один из вставленных контролов покушается на обработку его формой этих событий!

Добавлено спустя 36 минут 12 секунд:
Похоже то, что мне нужно, в Delphi называется TApplicationEvents. А в Лазарусе я такого не нашёл.
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Отловить внутри компонента нажатие Ctrl

Сообщение скалогрыз » 09.09.2009 00:29:13

Мне нужно, чтоб мой контрол реагировал на нажатие и отпускание клавиши Ctrl даже если фокус ввода находится не у него. Более того, лучше, чтоб ему вообще фокус ввода не попадал никогда - он ему не нужен.

А каким макаром ты узнаешь о нажатии кнопки вообще?
по таймеру будешь вызывать GetKeyboardState (и её налоги для других систем).

У формы есть свойство: KeyPreview. Установив эго в True, методы формы OnKeyDown/Press/Up всегда вызываются вперёд обработчика контрола, находящегося в фокусе. Так что: всегда есть шанс, что нажатие ctrl твой контрол не отловит.

Ещё вариант, это вешать системные обработчики на системные объекты (в обход LCL) и получать нажатие клавиш, до того как об этом узанала форма или кто-то ещё... По-сути это хак, но если его красиво исполнить, то всё получится.

как итог: регистрировать нужно Application.AddOnUserInputHandler. Если этого недостаточно, то клянчить у разработчиков дополнительные методы, и строго-на-строго избегать хаков! а главное НИКОГДА не трогать публичные (и опубликованные) OnXXX события. Потому что они не для взаимодействия компонентов, а для программиста, использующего эти компоненты.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 09.09.2009 11:14:10

скалогрыз писал(а):А каким макаром ты узнаешь о нажатии кнопки вообще?
по таймеру будешь вызывать GetKeyboardState (и её налоги для других систем).
Именно такая мысль и была, когда я писал о некроссплатформенности GetAsyncKeyState
скалогрыз писал(а):У формы есть свойство: KeyPreview. Установив эго в True, методы формы OnKeyDown/Press/Up всегда вызываются вперёд обработчика контрола, находящегося в фокусе. Так что: всегда есть шанс, что нажатие ctrl твой контрол не отловит.
Ну понятно, что пользователь контрола может его замордовать так, что тот не сможет работать. Если ему нужно, чтоб контрол нормально работал - будет пропускать нажатие и отпускание Ctrl.
скалогрыз писал(а):Ещё вариант, это вешать системные обработчики на системные объекты (в обход LCL) и получать нажатие клавиш, до того как об этом узанала форма или кто-то ещё... По-сути это хак, но если его красиво исполнить, то всё получится.
Ты имеешь ввиду что-то типа SetWindowsHookEx? Во-первых оно не кроссплатформенное, а во-вторых для него нужны полномочия администратора. Это уж совсем неизящно и черезмерно для той простой задачи, котораую мне нужно решить.
скалогрыз писал(а):как итог: регистрировать нужно Application.AddOnUserInputHandler.
О, вот это похоже то, что надо. Буду пробовать, спасибо.
скалогрыз писал(а): Если этого недостаточно, то клянчить у разработчиков дополнительные методы, и строго-на-строго избегать хаков! а главное НИКОГДА не трогать публичные (и опубликованные) OnXXX события. Потому что они не для взаимодействия компонентов, а для программиста, использующего эти компоненты.
То есть если мне скажем нужно вставить в свой компонент несколько картинок, которые должны по разному реагировать на клики мышкой, то я должен для каждого из них создавать свой класс и перекрывать MouseDown? Полагаю, что это черезчур ортодоксальный взгляд на ООП. Я ограничиваю доступ пользователя к этим свойствам внутренних контролов моего компонента. Считаю, что этого вполне достаточно. По принципу инкапсуляции пользователь компонента не должен лезть внутрь.
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

Re: Отловить внутри компонента нажатие Ctrl

Сообщение скалогрыз » 09.09.2009 11:42:45

VKB писал(а):Именно такая мысль и была, когда я писал о некроссплатформенности GetAsyncKeyState
...
Ты имеешь ввиду что-то типа SetWindowsHookEx? Во-первых оно не кроссплатформенное, а во-вторых для него нужны полномочия администратора. Это уж совсем неизящно и черезмерно для той простой задачи, котораую мне нужно решить.

Осбой разницы между GetAsyncKeyState и Hook-ами нет! Для обоих прийдётся писать платформенные реализации, с общей кроссплатформенной обёрткой. Аналоги хуков и GetAsyncKeyState, есть и в линуксе и маке...

VKB писал(а):То есть если мне скажем нужно вставить в свой компонент несколько картинок, которые должны по разному реагировать на клики мышкой, то я должен для каждого из них создавать свой класс и перекрывать MouseDown? Полагаю, что это черезчур ортодоксальный взгляд на ООП. Я ограничиваю доступ пользователя к этим свойствам внутренних контролов моего компонента. Считаю, что этого вполне достаточно. По принципу инкапсуляции пользователь компонента не должен лезть внутрь.

Всё правильно, у контролов для "внутреннего использования", события можно менять и использовать как угодно, без дополнительных спец-классов.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Отловить внутри компонента нажатие Ctrl

Сообщение Inferno » 09.09.2009 12:14:45

зачем такие сложности:
Код: Выделить всё
1.У формы KeyPressed:=true;
2.на FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState  );
begin
  if(Shift=[ssCtrl]) then  ......


и не важно где фокус, главное что бы форма была активной
Аватара пользователя
Inferno
новенький
 
Сообщения: 78
Зарегистрирован: 20.03.2009 14:40:20
Откуда: Тюмень

Re: Отловить внутри компонента нажатие Ctrl

Сообщение скалогрыз » 09.09.2009 12:52:29

Inferno писал(а):зачем такие сложности:
Код: Выделить всё
1.У формы KeyPressed:=true;
2.на FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState  );
begin
  if(Shift=[ssCtrl]) then  ......


и не важно где фокус, главное что бы форма была активной


1-х) тогда ответственность за работу компонента ложится на программиста. Т.е. ему нужно будет писать этот код каждый раз! А ведь хочется: положил компонент на форму - и всё работает.

2-х) Да. такой код работает, только если форма активна.

3-х) Соглашусь, что задуманный компонент - ну уж слишком какой-то заумный. Имхо, ни к чему это всё )) И хочет получить информацию, которую знать не нужно!
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Отловить внутри компонента нажатие Ctrl

Сообщение VKB » 09.09.2009 14:10:55

скалогрыз писал(а):Осбой разницы между GetAsyncKeyState и Hook-ами нет! Для обоих прийдётся писать платформенные реализации, с общей кроссплатформенной обёрткой. Аналоги хуков и GetAsyncKeyState, есть и в линуксе и маке...
Ну как бы разница-то всё-таки есть. Для того, чтоб вставить Хук должны быть нужны полномочия суперпользователя, но зато не нужно использовать таймер. Что же касается того, что есть аналоги для линукса и мака, то я даже верю. Это будет мой запасной вариант
Всё правильно, у контролов для "внутреннего использования", события можно менять и использовать как угодно, без дополнительных спец-классов.
А я ни на что большее и не покушаюсь :-).
3-х) Соглашусь, что задуманный компонент - ну уж слишком какой-то заумный. Имхо, ни к чему это всё )) И хочет получить информацию, которую знать не нужно!
А предположим, что мой компонент имеет одно основное состояние, где действует своя логика по обработке мышки, но ему иногда нужно показывать некую дополнительную информацию, слегка видоизменяя свой внешний вид. Это существенно более редкая ситуация, которую нужно включать обычно на очень короткое время. И удобно навесить переключение в это состояние по нажатию на Ctrl. Потому что предполагается, что пользователь в основном будет только недолго смотреть на это видоизменённое состояние. Но так же я хочу ему дать возможность кое что подправить в этом видоизменённом состоянии (с этим как раз меньше всего проблем - при нажатии на мышку передаётся состояние кнопки Ctrl).

Добавлено спустя 7 часов 15 минут 37 секунд:
скалогрыз писал(а):как итог: регистрировать нужно Application.AddOnUserInputHandler.
Попробовал AddOnKeyDownHandler - то что, нужно, но странно, что нет такого же для KeyUp. Так что всё равно придётся использовать AddOnUserInputHandler. Всем спасибо за обсуждение, особенно скалогрызу!

Добавлено спустя 56 минут 2 секунды:
Мда. Всё-таки чуть-чуть они не доделали. Почему в UserInputEvent передают только поле Msg, а не всё сообщение? Я отлавливаю, что отжата какая-то кнопка, причём какая именно вызывающей процедуре известно, а мне придётся вызывать GetKeyState (и его аналогов для не Windows)...
VKB
новенький
 
Сообщения: 33
Зарегистрирован: 07.09.2009 13:57:35

След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 20

Рейтинг@Mail.ru