Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Ада и другие языки _ Builder 6.0

Автор: Fanat 31.01.2008 21:41

Хочу сделать следуюшее...чтобы по определенным клавишам двигалася напиример 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 31.01.2008 23:32

Цитата
можно для каждой кнопки писать свой поток и в нём ждать нажатия определённой кнопки и его уже орабатывать...
Угу... Чтобы наконец убедиться, что весь 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 1.02.2008 3:16

Да...так работает..=)

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

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

Цитата

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


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

Автор: volvo 1.02.2008 5:29

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

Прикрепленный файл  fanat.txt ( 2.08 килобайт ) Кол-во скачиваний: 486


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

Автор: Fanat 1.02.2008 15:53

Цитата(volvo @ 1.02.2008 1:29) *

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

Прикрепленный файл  fanat.txt ( 2.08 килобайт ) Кол-во скачиваний: 486


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


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

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

(не обязательно на счёт данной задачи...я могу разобраться и сам попрбовать сделать..=)...в общем как тебе удобней...)

Автор: volvo 1.02.2008 16:24

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

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

Автор: Fanat 1.02.2008 16:34

Цитата(volvo @ 1.02.2008 12:24) *

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

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


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

Автор: volvo 1.02.2008 16:47

Должны... Но их должна завершить система, а это требует дополнительного времени, будет подвисать при выходе. А если ты напишешь

for(int i = num_threads - 1; i >= 0; i--) {
thr[i] -> Terminate();
}
, то дело пойдет быстрее...

Автор: Fanat 1.02.2008 17:10

Цитата(volvo @ 1.02.2008 12:47) *

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


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


Автор: volvo 1.02.2008 18:45

Цитата
А как отловить нажатие двух кнопок сразу?..хочу добавить движение по диагоналям...
Ну, например, вот так:
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 2.02.2008 7:01

А что значит строчка


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


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

thr[thread_n]->GetState << dir


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


Автор: volvo 2.02.2008 8:22

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

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

Автор: Fanat 2.02.2008 17:27

Цитата(volvo @ 2.02.2008 4:22) *

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


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

TDir GetState() {return FState;};


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

Цитата

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

Понял...


Автор: volvo 2.02.2008 17:41

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

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

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

Автор: Fanat 2.02.2008 18:01

Цитата(volvo @ 2.02.2008 13:41) *

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

, правда?

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

Цитата

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


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