Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2008.12.21;
Скачать: [xml.tar.bz2];

Вниз

Реализация интерфейса-мастера.   Найти похожие ветки 

 
Kolan ©   (2008-02-29 17:02) [0]

Здравствуйте,
 Разрабатываю программу для тач скрина. Интерфейс (все представляют терминал оплаты) состоит из нескольких экранов, которые работают как мастер.

Вопрос как это реализовать?

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

А вот как быть с остальными (кроме показа окон) действиями?

Допустим есть 4 шага. На шаге 2 пользователь вводит данные, а на шаге 4 их надо обработать как-то.

У меня есть идея, но она мне ненравится из за условного выражения.
Чтобы перейти на сл экран пользователь жмет «далее». Соотв при нажатии далее возникает событие «Далее(Sender)», где сендер — это окно где нажали далее. Какое следующее окно паказать написанно в мета данных, а что сделать определяется по Sender.

То есть что-то вроде:
if Sender.StepNumber = 2 then
 <сохранить даные>
if Sender.StepNumber = 4 then
 ShowMessge(<сохраненные данные>)


Мне ненравится такой подход из за if"а&#133

А что вы посоветуете?


 
Игорь Шевченко ©   (2008-03-01 00:05) [1]


> А что вы посоветуете?


Конечный автомат.

type
 TAutomataState = (asStartState, asState1, asasState2, asCompleteState);

И анализировать этот State при действиях


 
Kolan ©   (2008-03-01 10:37) [2]

> И анализировать этот State при действиях

И сделать большой кейс? Ну я тоже самое и предложил, только в моем случае эти стейты хроняться в самих окнах как бы, и передаются по событию&#133

В стейтах плохо то, что трудно модифицировать такой автомат, я так пробовал.

Например бы такой:

case Stae of:
 asStartState:
 begin
   DoSmth;
   State := asState2;
 end;
 asasState2:
 begin
  State := CompleteState;
 end;
 asCompleteState:
 begin
   
 end;


Теперь если надо вставить asState1 между asStartState и asasState2, то надо менять ссылки &#151; неудобно.
Кроме того, нафига состояния если состояние однозначно связано с показываемым экраном, так почему бы не использовать сам экран(то есть форму у меня) в качестве состояния.

Если бы надо было бы только показывать экраны, то я бы сделал, наверно, такой файлик:

У окон есть номера.

[0.0]
Next=0.1
[0.1]
Next=0.2
Back=0.1

То есть некие мета данные&#133 Загружал бы его и при возникновении события &laquo;Back&raquo; в окне &laquo;0.1&raquo; я бы знал, что надо перейти в окно 0.1&#133
Но вот как быть с выполнением действий &#151; хз.

Плюс ко всму окон много и кейс будет здоровый&#133

Мож есть еще идеии?


 
b z   (2008-03-01 12:42) [3]


> Мож есть еще идеии?

Может взять компонент аля wizard?


 
Kolan ©   (2008-03-01 14:07) [4]

> Может взять компонент аля wizard?

А как он работает?

Визард (собссно есть свой) &#151; это страницы (на подобии PageControl) которые лежат на форме, то есть. Мне бы хотелось не создавать ненужные окна.


 
Kolan ©   (2008-03-03 10:09) [5]

Мож есть у кого еще идеи?


 
clickmaker ©   (2008-03-03 13:15) [6]


> Мне ненравится такой подход из за if"а

не вижу логики.
Чем if-то плох?


 
Kolan ©   (2008-03-03 13:35) [7]

Плохо тем, что неудобно добавлять или удалять экраны. Кроме того экранов много и иф будет большой &#151; неудобно. А еще есть как-бы разные ветки, несвязаные друг с другом, и все это будет в одном месте&#133


 
Семеныч   (2008-03-03 14:03) [8]

> Kolan ©   (03.03.08 13:35) [7]

Вместо if можно вызывать некий метод (или процедуру), но не напрямую, а по ссылке, которая хранится в некоей переменной и устанавливается конечным автоматом в зависимости от его текущего состояния.


 
Kolan ©   (2008-03-03 14:19) [9]

> которая хранится в некоей переменной

Лучьше уж стратегию использовать, только как. Хотелось бы более конкретный пример.


 
clickmaker ©   (2008-03-03 14:23) [10]


> Плохо тем, что неудобно добавлять или удалять экраны.

и часто они будут добавляться/удаляться?
все равно же одним добавлением не ограничится? Нужно же еще какой-то код писать, так ведь?


 
KSergey ©   (2008-03-03 14:27) [11]

> Kolan ©   (01.03.08 10:37) [2]
> Теперь если надо вставить asState1 между asStartState и
> asasState2, то надо менять ссылки — неудобно.

Вот именно поэтому Игорь и говорит про конечный автомат.
Весь этот "большой case", которого вы так опасаетесь в итоге просто вынесется в однельный управляющий unit, который будет это самый case содержать и создавать требуемые формы при надобности в зависимости от кнопок, нажатых пользователем (анализировать результат ShowModal).
Такой подход позволит:
а) элементарно вставлять новые шаги в середину визарда;
б) легко реализовать кнопку "пошли все на!" на k., шаге;
в) в дополнение к пункту а - построить в одном месте логику "Шаг X из Y" (при условии поддержки в формах)
г) главная засада взардов - иногда возникающая необходимость пропускать некоторые шаги в зависимости от пользовательского ввода. Здесь этот обычно довольно некрасивый и развесистый код реализуется хотя бы в одном точно месте.

ну и главное: если визард тупо-глупо линейный, т.е. только с кнопочками "Back" / "Next" / "Cancel" - то можно и вовсе прописать классы форм в перечислителе - тогда и вовсе никаких Case не понадобится.


 
Kolan ©   (2008-03-03 14:37) [12]

> и часто они будут добавляться/удаляться?
> все равно же одним добавлением не ограничится? Нужно же
> еще какой-то код писать, так ведь?

Опыт показал что может 1-2 добавится/исчезнуть.

Угу, ессно код писать надо, если бы ненадо было, то я, как говорил, использовал бы мета данные и редактировал бы только их.
В том и дело, что код писать надо.


> элементарно вставлять новые шаги в середину визарда;

Сомневаюсь.


> если визард тупо-глупо линейный

Визард восе не тупой.

Для варианта с кэйсом я уже придумал все:

Сделаю общего предка для всех форм визарда. Предок будет содержать классовую функцию, которая вернет код формы(этот код и есть как-бы стейт).
В инициализациях потомки будут регистрировать свои классы.

Тот класс, у кого и будет всеь этот кейс будет по полученному коду и операции делать что-то, а потом брать инфу из мета данных, как в [2], и показывать следующиее окно.

Валидация будет внутри форм.

Оцените, плз&#133


 
Игорь Шевченко ©   (2008-03-03 15:07) [13]


> Оцените, плз…


Keep It Simple Stupid


 
oxffff ©   (2008-03-03 15:50) [14]


> А что вы посоветуете?


А чем тебе двунаправленный список не угодил?
Объект в элементе списка поддерживает интерфейс.

TStepNode=class
function IsDataValid:boolean;Virtual;abstract; вызывается controlleroм для
    {
   
    }

function Back:integer;Virtual;abstract; делегирует вызов контролер .передавая себя в параметре.
function Forward:integer;Virtual;abstract; делегирует вызов контролеру.
передавая себя в параметре.

function SkipSteps(stepCount:integer):Boolean делегирует вызов контролеру который проверяет уж каждого пропущенного IsDataValid. И в случае успеха устанавливает текущий шаг.

Рассуждая так далее можно сделать вложенные шаги и т.д.

Игорь Шевченко ©   (03.03.08 15:07) [13]

Keep It smart and simple.


 
Kolan ©   (2008-03-03 18:23) [15]

> Keep It Simple Stupid

Вроде просто, а вы предлогаете что? Не [1] я читал, как конкретно предлагается реализовать? Где хранить состояния? Как узнавать что следующим показывать?


> А чем тебе двунаправленный список не угодил?
> Объект в элементе списка поддерживает интерфейс.

Так и хотел сделать примерно, только операций много не только &laquo;Назад&raquo; &laquo;Далее&raquo;, поэтому интерфейс несколько другой.
Вопрос что писать в контроллере? При возникновении  Back и Forward?


 
oxffff ©   (2008-03-03 22:18) [16]


> Вопрос что писать в контроллере? При возникновении  Back
> и Forward?


function TStepNode.Back:boolean;
begin
result:=controller.back(self);
end;

procedureTStepController.back(node:TStepNode);
begin
if AllowBack and node.AllowBack  then     // paranoiac AllowBack :)
  begin
  Node.Detach;   // on Node
  setStepNode(previous); // <- где previous может быть не предыдущий,
                                  а тот например который Skipped, либо другое    поведение на твое усмотре
  previous.Attach;
  end;
end;

end;


 
Kolan ©   (2008-03-03 22:58) [17]

> procedureTStepController.back(node:TStepNode);

Так это для одного &laquo;нода&raquo; а теперь представь что  их 25 и у всех по некст(бэк не беру тут проще) надо делать разные действия.
Так вот как ты это напишешь? Вот сама суть того, что я хз как сделать, чтобы попроще было&#133


 
oxffff ©   (2008-03-03 23:11) [18]


> Kolan ©   (03.03.08 22:58) [17]


У конктроллера есть связный список переходов на одном уровне.
Его уведомляет элемент о Back переходе, вызовом

procedureTStepController.back(node:TStepNode);

У контролер зафиксировано предудущее состояние (либо оно вычисляется). Он совершает на него переход.

Динамическая структура, без статической привязки.

Что тебе не нравится?


 
Kolan ©   (2008-03-03 23:31) [19]

Да переход фигня, кароме него работу еще надо сделать.

На 2 шаге запомнить в переменную на 4 что-то с ней сделать, на 7 запустить поток и туда её засунуть&#133

Вот как это делать ты прелагаешь &#151; непонятно.


 
oxffff ©   (2008-03-04 08:38) [20]


> Вот как это делать ты прелагаешь — непонятно.


function TStepNode.Back:boolean;
begin
BeforeMoveBack;
result:=controller.back(self);
AfterMoveBack(result);
end;

procedureTStepController.back(node:TStepNode);
begin
if AllowBack and node.AllowBack  then     // paranoiac AllowBack :)
 begin
 Node.Detach;   // on Node
 setStepNode(previous); // <- где previous может быть не предыдущий,
                                 а тот например который Skipped, либо другое    поведение на твое усмотре
 previous.Attach;
 end;
end;

Что еще не понятно?


 
Kolan ©   (2008-03-04 08:40) [21]

То есть ты предлагаешь логику работы из контроллера распылить по всем окнам, так?


 
Kolan ©   (2008-03-04 08:42) [22]

И вообще непонятно как в этом случае ты на шаге 2 что-то запомнишь, а на шаге 4 что-то с этим сделаешь, если формы ничего не знают про друг друга&#133


 
KSergey ©   (2008-03-04 08:46) [23]

как обычно - сценарий меняется по ходу пьессы.
Это уже не интересно становится. Неужели нельзя все сразу было рассказать?

По поводу "переменных на разных шагах" - ну так это для пользователя эти шаги - разные. Но что мешает все состояние хранить в одном месте, в общей куче от всех "шагов"? По сути ведь есть некий объект (здесь обьек - условность, не в смысле ООП), управление которым исключительно для удобства (не более!) разнесено на несколько "шагов" вместо одного огромного экрана.
По-моему, именно так стоит на это все смотреть. Т.е. экраны шагов - друг про друга пусть не знают, однако меняют состояние одного общего объекта (опять же не буквально в терминах ООП). Может и не экраны меняют его состояние, а из них просто информация вычитывается. Тут смотреть надо как связей меньше.


 
Kolan ©   (2008-03-04 09:02) [24]

> как обычно &#151; сценарий меняется по ходу пьессы.

&laquo;Допустим есть 4 шага. На шаге 2 пользователь вводит данные, а на шаге 4 их надо обработать как-то.&raquo; © [0] Kolan


> Может и не экраны меняют его состояние, а из них просто
> информация вычитывается.

Вот тут я и прошу конкретный пример, бо всплывают проблеммы&#133 Все написанное я понимаю.


 
oxffff ©   (2008-03-04 09:02) [25]


> Kolan ©   (04.03.08 08:42) [22]
> И вообще непонятно как в этом случае ты на шаге 2 что-то
> запомнишь, а на шаге 4 что-то с этим сделаешь, если формы
> ничего не знают про друг друга…


А что мешает тебе определить шаги в одном модуле например и использовать shared переменные.
Либо при создании их ссылаться друг на друга.
Либо и т.д.

Если шаги взаимодействуют они это делают согласно контракта.
Какой контракт это выбор за тобой.


 
oxffff ©   (2008-03-04 09:07) [26]


> моему, именно так стоит на это все смотреть. Т.е. экраны
> шагов - друг про друга пусть не знают, однако меняют состояние
> одного общего объекта (опять же не буквально в терминах
> ООП). Может и не экраны меняют его состояние, а из них просто
> информация вычитывается. Тут смотреть надо как связей меньше.
>


Это и есть контракт. Но это все разновидность [25].


 
Kolan ©   (2008-03-04 09:10) [27]

> Либо и т.д.

Либо, либо, вариантов масса&#133 а конкретного примера тен.

Ладно шас выложу свой вариант, будите ругать :).


 
Kolan ©   (2008-03-04 10:38) [28]

Вроде получилось неплохо:

Есделал такого предка:

 TCustomTouchForm = class(TForm)
 private
   FWindowCode: string;
   FControllerInterface: IControllerInterface;
   FOtherInterfacesObjetc: TObject;
 protected
   function GetControllerInterface: IControllerInterface;
 public
   {С помощью этой операции можно узнать поддерживает или нет форма код.
    Таких кодов может быть несколько.}
   class function SupportsWindowCode(AWindowCode: string): Boolean; virtual; abstract;

   {При создании формы ей указывают конкретный её код.
    Если поддерживать форма может несколько кодов, то создается с конкретным.
    А с каким именно знает тот, кто её создает.}
   constructor Create(AWindowCode: string;
     AControllerInterface: IControllerInterface;
     AOtherInterfacesObjetc: TObject); reintroduce;
   property WindowCode: string read FWindowCode write FWindowCode;
 end;

 TCustomTouchFormClass = class of TCustomTouchForm;


Контроллер такой:

procedure TSystemController.Act(ASenderWindowCode,
 APerformedActionName: string);
begin
 if ASenderWindowCode = "Start" then
 begin
   ShowWindow("0.0");
 end;                

 if ASenderWindowCode = "0.0" then
 begin
   if APerformedActionName = "Settings" then
     ShowWindow("7.0");
 end;

 if ASenderWindowCode = "7.0" then
 begin
   if APerformedActionName = "Back" then
     ShowWindow("0.0");    
 end;
 
end;


Вот показ формы:
procedure TSystemController.ShowWindow(AWindowCode: string);
var
 TouchForm: TCustomTouchForm;
begin
 TouchForm := GetWindowsFactory.GetWindow(AWindowCode, Self, nil);
 if Assigned(TouchForm) then
 begin
   TouchForm.Show;
   TouchForm.FormStyle := fsStayOnTop;
 end
 else
   raise EFormNotFoundException.Create(AWindowCode);
end;


Каждая форма регистрирует совй класс в фабрике:
initialization
 GetWindowsFactory.AttachTouchFormClass(TSettingsForm);


Фабрика создает окна так:

function TWindowsFactory.GetWindow(AWindowCode: string;
 AControllerInterface: IControllerInterface;
 AOtherInterfacesObject: TObject): TCustomTouchForm;
begin
 Result := nil;
 {Найи форму в кэше.}
 Result := FFormsList.FindForm(AWindowCode);
 if Assigned(Result) then
 begin
   FFormsList.DeleteAllFormsAfterGiven(Result);
 end
 else
 begin
   {Если форма не найдена, то создать её.}
   Result := CreateWindow(AWindowCode, AControllerInterface, AOtherInterfacesObject);
   if Assigned(Result) then
     FFormsList.Add(Result);
 end;
end;


А еще на каждой форме лежит экше лист в OnExecute которого написано:
procedure TSettingsForm.MainActionListExecute(Action: TBasicAction;
 var Handled: Boolean);
begin
 GetControllerInterface.Act(WindowCode, Action.Name);
end;


Как бы сделать так, чтобы у предка всех форм это экшен лист сразу бы лежал на форме?

Хоть и кейс но все в одном месте вроде&#133


 
Kolan ©   (2008-03-04 10:45) [29]

К когду контроллера прилагается State Chart&#133


 
KSergey ©   (2008-03-04 11:30) [30]

Зачем люди переносят заморочки, требуемые в С++ в дельфийский код? В дельфи можно и без всякие рукописных прибабахнутых фабрик объекты по указанному классу создавать, все для этого в компиляторе уже есть. Зачем отсебятину придумывать? Да и все сознаддые формы в Screen.Forms уже есть. Хотя, конечно, иногда бывает проще свой список поддерживать, только для требуемых форм, но тоже вопрос еще.

А использовать по тексту строковые константы - ваще капец. Описка в букве - и превед отладчег. Можно ведь и просто константами все описать, причем - перчислением, как и было сказано. Они - integer, их проверять быстрее у процессора выходит, да и описки - компилятором все отловятся.

В общем на мой вкус - много лишних сущностей. Но это наверняка субъективно.


 
Kolan ©   (2008-03-04 11:56) [31]

> Зачем люди переносят заморочки, требуемые в С++

Я его с трудм читаю&#133

> указанному классу создавать

А кто укажет его?


> А использовать по тексту строковые константы &#151; ваще капец.

Ладно, а где их хранить, приведи пример&#133


 
KSergey ©   (2008-03-04 12:17) [32]

> Kolan ©   (04.03.08 11:56) [31]
> > указанному классу создавать
> А кто укажет его?

Тот же контроллер, но сразу нужный класс формы шага. И все. Никаких рукописных фабрик классов не надо.

> Kolan ©   (04.03.08 11:56) [31]
> Ладно, а где их хранить, приведи пример…

А чем [1] не нравится?


 
Kolan ©   (2008-03-04 12:37) [33]

> А чем [1] не нравится?

Я не поняимаю как конкретно рпелагается это решнение использовать, как инстанцировать и убивать формы&#133


 
KSergey ©   (2008-03-04 12:44) [34]

> Kolan ©   (04.03.08 12:37) [33]
>  как инстанцировать и убивать формы…

http://www.delphikingdom.com/asp/viewitem.asp?catalogid=342
раздел "Лень, как источник вдохновения."


 
Kolan ©   (2008-03-04 12:50) [35]

> [34] KSergey ©   (04.03.08 12:44)

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

&laquo;Используй перечисление&raquo; &#151; это что, то что их как-то можно использовать я понимаю и так? Тут же вопрос именно как все организовать&#133


 
Kolan ©   (2008-03-04 12:54) [36]

А делать такую хрень:
ListClass : TListClass  = (TEdit , TButton , TCheckBox , TLabel ) ;

Длинну которой придется кажды раз руками менять, и в которую еще надо будет вручную добавлять компоненты я нехочу. Я сделал тоже самое, но более автоматизировано.

Каждая форма регистрирует совй класс в фабрике:
initialization
GetWindowsFactory.AttachTouchFormClass(TSettingsForm);


А вот создание:
function TWindowsFactory.CreateWindow(AWindowCode: string;
 AControllerInterface: IControllerInterface;
 AOtherInterfacesObjetc: TObject): TCustomTouchForm;
var
 FormClass: TCustomTouchFormClass;
begin
 Result := nil;
 FormClass := FFormsClasses.FindFormClass(AWindowCode);
 if FormClass <> nil then
   Result := FormClass.Create(AWindowCode, AControllerInterface, AOtherInterfacesObjetc);
end;


 
oxffff ©   (2008-03-04 14:05) [37]


> Kolan ©   (04.03.08 12:54) [36]
> А делать такую хрень:
> ListClass : TListClass  = (TEdit , TButton , TCheckBox ,
>  TLabel ) ;


А у тебя тоже все статично. Захочешь добавить будешь править свой

procedure TSystemController.Act(ASenderWindowCode,
APerformedActionName: string);
begin
if ASenderWindowCode = "Start" then
begin
  ShowWindow("0.0");
end;                

if ASenderWindowCode = "0.0" then
begin
  if APerformedActionName = "Settings" then
    ShowWindow("7.0");
end;

if ASenderWindowCode = "7.0" then
begin
  if APerformedActionName = "Back" then
    ShowWindow("0.0");    
end;

end;


 
Kolan ©   (2008-03-04 14:17) [38]

> А у тебя тоже все статично. Захочешь добавить будешь править
> свой

Ну а как без этого, хоть ListClass не править. Этим мне и ненравится кейс&#133 О чем собссно и вся ветка с самого начала&#133


 
oxffff ©   (2008-03-04 14:48) [39]


> Kolan ©   (04.03.08 14:17) [38]


Перечитай сначала. :)


 
Kolan ©   (2008-03-04 14:53) [40]

> А у тебя тоже все статично.

Ну а как без этого, хоть ListClass не править.


> Захочешь добавить будешь править свой

Этим мне и ненравится кейс… О чем собссно и вся ветка с самого начала…

Имел это ввиду&#133



Страницы: 1 2 3 вся ветка

Форум: "Основная";
Текущий архив: 2008.12.21;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.049 c
15-1224052782
AlekseyB
2008-10-15 10:39
2008.12.21
Вирус autorun.inf


15-1224121618
МемТест
2008-10-16 05:46
2008.12.21
Как можно использовать GPU для рассчётов?


2-1226305803
timekiller
2008-11-10 11:30
2008.12.21
XML с дочерьми ..


6-1196884543
Norm Iridium
2007-12-05 22:55
2008.12.21
Delphi как получить HTML-код страницы? А через прокси?


15-1224036802
Slider007
2008-10-15 06:13
2008.12.21
С днем рождения ! 15 октября 2008 среда





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский