Помощь - Поиск - Пользователи - Календарь
Полная версия: Builder 6.0
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ада и другие языки
Fanat
Хочу сделать следуюшее...чтобы по определенным клавишам двигалася напиример Button1,
а по другим Button2...написал примерно так


void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg,
bool &Handled)
{
// Memo1->Lines->Add(Msg.wParam);
const nStep = 1;
switch(Msg.wParam)
{
case 40: Button1->Top += nStep;
break;
case 38: Button1->Top -= nStep;
break;
case 37: Button1->Left -= nStep;
break;
case 39: Button1->Left += nStep;
break;

case 83: Button2->Top += nStep;
break;
case 87: Button2->Top -= nStep;
break;
case 65: Button2->Left -= nStep;
break;
case 68: Button2->Left += nStep;
break;

}



Но теперь пока одна кнопка движеться вторая не будет...как реализовать независимое движение?..
может как то через много поточность?..можно для каждой кнопки писать свой поток и в нём ждать нажатия определённой кнопки и его уже орабатывать...но как ето сделать?..
volvo
Цитата
можно для каждой кнопки писать свой поток и в нём ждать нажатия определённой кнопки и его уже орабатывать...
Угу... Чтобы наконец убедиться, что весь GUI должен всегда работать в одном потоке, иначе проблем не оберешься... Вообще-то можно сделать и так:

typedef struct {
int x, y;
bool b;
} RMove;

RMove moving[2];

void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg, bool &Handled)
{
switch(Msg.message) {
case WM_KEYDOWN:
// Ну, ты же понимаешь, что все это можно записать и намного короче,
// добавив одну-единственную функцию, правда? Это - просто пример...
switch(Msg.wParam) {
case 40:
{
moving[0].x = 0; moving[0].y = 1;
Handled = moving[0].b = true; break;
}
case 38:
{
moving[0].x = 0; moving[0].y = -1;
Handled = moving[0].b = true; break;
}
case 37:
{
moving[0].x = -1; moving[0].y = 0;
Handled = moving[0].b = true; break;
}
case 39:
{
moving[0].x = 1; moving[0].y = 0;
Handled = moving[0].b = true; break;
}

case 83:
{
moving[1].x = 0; moving[1].y = 1;
Handled = moving[1].b = true; break;
}
case 87:
{
moving[1].x = 0; moving[1].y = -1;
Handled = moving[1].b = true; break;
}
case 65:
{
moving[1].x = -1; moving[1].y = 0;
Handled = moving[1].b = true; break;
}
case 68:
{
moving[1].x = 1; moving[1].y = 0;
Handled = moving[1].b = true; break;
}
}

break;
case WM_KEYUP:
switch(Msg.wParam) {
case 40: case 38:
case 37: case 39:
{
Handled = true;
moving[0].b = false; break;
}
case 83: case 87:
case 65: case 68:
{
Handled = true;
moving[1].b = false; break;
}
}

break;
}
}


void MoveButton(TButton *button, RMove r) {
const int nStep = 1;
button->Left += nStep * r.x;
button->Top += nStep * r.y;
}


// Таймер установлен на 0,1 секунды...
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if(moving[0].b) {
MoveButton(Button1, moving[0]);
}
if(moving[1].b) {
MoveButton(Button2, moving[1]);
}
}
Fanat
Да...так работает..=)

Хотелось бы всё таки узнать как сделать через потоки...
мне непонятно к в потоке узнать что нажата клавиша?..

А зачем Handled ( - признак обработки события) выставлять в true?..

Цитата

// Ну, ты же понимаешь, что все это можно записать и намного короче,
// добавив одну-единственную функцию, правда? Это - просто пример...


Я как то об этом не задумывался даже... blink.gif Я так понимаю эта функция просто внутри себя будет проверять клавиши, да и кнопку которую двигать ей тоже отправлять надо...и она уже будет двигать ету кнопку...
volvo
Цитата
Хотелось бы всё таки узнать как сделать через потоки...
Ты действительно этого хочешь? Тогда посмотри присоединенный файл, там описание того, как это можно сделать. Один из способов, скажем так...

Нажмите для просмотра прикрепленного файла

Цитата
А зачем Handled ( - признак обработки события) выставлять в true?..
Затем, что если ты этого не сделаешь, и у тебя на форме будет, например, Edit, и фокус будет на нем, то этот Edit будет заполняться символами, которые ты вводишь (нажимая клавишу). А если ничего из текстовых контролов не будет - то программа будет пищать. А установкой Handled = true ты говоришь обработчику ApplicationMessage, что это событие уже обработано, дальше по цепочке его передавать не надо...
Fanat
Цитата(volvo @ 1.02.2008 1:29) *

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

Нажмите для просмотра прикрепленного файла

Затем, что если ты этого не сделаешь, и у тебя на форме будет, например, Edit, и фокус будет на нем, то этот Edit будет заполняться символами, которые ты вводишь (нажимая клавишу). А если ничего из текстовых контролов не будет - то программа будет пищать. А установкой Handled = true ты говоришь обработчику ApplicationMessage, что это событие уже обработано, дальше по цепочке его передавать не надо...


Да...интересно..я вроде во всём разобрался...

И я так понимаю Sleep(100); чтобы мы успели нажать какую либо кнопку...но при етом слишком скорость маленькая...а при Sleep(10) у меня в конце концов всё таки зависало =(...
А вообще при отключения Sleep кнопки почему то движуться с разными скоростями и зависает быстро..=(
Не мог бы ты привести пример ещё какого либо способа...

(не обязательно на счёт данной задачи...я могу разобраться и сам попрбовать сделать..=)...в общем как тебе удобней...)
volvo
Цитата
а при Sleep(10) у меня в конце концов всё таки зависало =(...
Поменял в Execute() условие выхода на
  ...
}
while(!Terminated);

(как я мог написать там Suspended - не понимаю wacko.gif ), гонял программу минут 20 со Sleep(5) и nStep = 2: скорости движения одинаковые, зависаний никаких не обнаружено. Я надеюсь, ты в OnDestroy формы прописал завершение потоков?
Fanat
Цитата(volvo @ 1.02.2008 12:24) *

Поменял в Execute() условие выхода на
  ...
}
while(!Terminated);

(как я мог написать там Suspended - не понимаю wacko.gif ), гонял программу минут 20 со Sleep(5) и nStep = 2: скорости движения одинаковые, зависаний никаких не обнаружено. Я надеюсь, ты в OnDestroy формы прописал завершение потоков?


Да..так лучше...
Теперь написал...они же и так сами должны завершаться при завершении приложения?..
volvo
Должны... Но их должна завершить система, а это требует дополнительного времени, будет подвисать при выходе. А если ты напишешь
for(int i = num_threads - 1; i >= 0; i--) {
thr[i] -> Terminate();
}
, то дело пойдет быстрее...
Fanat
Цитата(volvo @ 1.02.2008 12:47) *

Должны... Но их должна завершить система, а это требует дополнительного времени, будет подвисать при выходе. А если ты напишешь
for(int i = num_threads - 1; i >= 0; i--) {
thr[i] -> Terminate();
}
, то дело пойдет быстрее...


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

volvo
Цитата
А как отловить нажатие двух кнопок сразу?..хочу добавить движение по диагоналям...
Ну, например, вот так:
const int dirLt = 0;
const int dirRg = 1;
const int dirUp = 2;
const int dirDn = 3;

// Делаем множество направлений
typedef Set<int, dirLt, dirDn> TDir;

class TMyThread : public TThread
{
private:
TButton *btn;
TDir FState;
protected:
void __fastcall Execute();
public:
__property TDir GetState = {read = FState, nodefault};
__fastcall bool SetState(TDir& state) {
FState = state;
if(state.Empty())
{
Suspend();
}
else
{
Resume();
}
return true;
}

__fastcall TMyThread(TButton *button, bool CreateSuspended):
TThread(CreateSuspended)
{
btn = button;
}
};

const int num_threads = 2;
TMyThread *thr[num_threads];


void __fastcall TMyThread::Execute()
{
const int nStep = 2;

do {

int dx = 0, dy = 0;
for(int dir = dirLt; dir <= dirDn; dir++) {
// если текущее направление присутствует во множестве
if(FState.Contains(dir)) {
// то высчитываем, куда двигаться...
switch(dir) {
case dirLt: dx = -nStep; break;
case dirRg: dx = +nStep; break;
case dirUp: dy = -nStep; break;
case dirDn: dy = +nStep; break;
}

}
}

btn->Left += dx; btn->Top += dy;
Sleep(5);
}
while(!Terminated);
}

// ...

int key_pressed(unsigned int key, int& dir)
{
unsigned codes[num_threads][4] = {
{37, 39, 38, 40}, {65, 68, 87, 83}
};

for(int i = 0; i < num_threads; i++) {
for(int j = 0; j < 4; j++) {
if(codes[i][j] == key) {
dir = j; return i;
}
}
}
return -1;
}


void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg, bool &Handled)
{
int thread_n, dir;

switch(Msg.message) {
case WM_KEYDOWN:
if((thread_n = key_pressed(Msg.wParam, dir)) >= 0) {
Handled = thr[thread_n]->SetState(
thr[thread_n]->GetState << dir // добавляем направление во множество
);
}
break;
case WM_KEYUP:
if((thread_n = key_pressed(Msg.wParam, dir)) >= 0) {
Handled = thr[thread_n]->SetState(
thr[thread_n]->GetState >> dir // "изымаем" направление ...
);
}
break;
}
}
"... И всего делов..." (С) "И.В. меняет профессию"
smile.gif
Fanat
А что значит строчка

__property TDir GetState = {read = FState, nodefault};


Это мы свойства добавляем?..синтаксис не понятен..=(
И:

thr[thread_n]->GetState << dir


<< это что значит?..

volvo
Цитата
Это мы свойства добавляем?
Угу... Именно свойства. А что непонятно? Описываешь свойство (имя и тип), и указываешь что для чтения обращение к свойству аналогично обращению к FState. Поскольку нет write=, то это свойство только для чтения. И не является свойством "по умолчанию".

Цитата
<< это что значит?..
для типа Set перегружены операторы << (через него реализовано добавление элемента в множество) и >> (извлечение элемента из множества, если он там присутствует)
Fanat
Цитата(volvo @ 2.02.2008 4:22) *

Угу... Именно свойства. А что непонятно? Описываешь свойство (имя и тип), и указываешь что для чтения обращение к свойству аналогично обращению к FState. Поскольку нет write=, то это свойство только для чтения. И не является свойством "по умолчанию".


Вроде разобрался...а почему используется свойство?...можно так

TDir GetState() {return FState;};


Через свойство лучше чем то?..

Цитата

для типа Set перегружены операторы << (через него реализовано добавление элемента в множество) и >> (извлечение элемента из множества, если он там присутствует)

Понял...

volvo
Цитата
можно так
TDir GetState() {return FState;};

Можно. Только тогда придется делать:
				Handled = thr[thread_n]->SetState(
thr[thread_n]->GetState() >> dir // <--- вызов функции GetState
);

, правда? А не боишься, что у тебя будет постоянно создаваться новая копия множества, и ты потом получишь тормоза при выходе из программы? А при работе с __property ты работаешь с одной и той же переменной...
Fanat
Цитата(volvo @ 2.02.2008 13:41) *

Можно. Только тогда придется делать:
				Handled = thr[thread_n]->SetState(
thr[thread_n]->GetState() >> dir // <--- вызов функции GetState
);

, правда?

Ну это понятно...

Цитата

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


Я так понимаю что они создаються при возвращении значения...но разве эта копия будет существовать до самого выхода из программы?..они не будут удаляться при выходе за '}' ?..
Согласен, что твой вариант работать все равно будет лучше... good.gif
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.