LINUX.ORG.RU

Как полностью разделить логику приложения и его GUI?


0

0

Я хочу позволить пользователю две вещи:

1. возможность полностью (на 100%) изменить скин и поведение GUI (отключить часть диалогов, настроить уведомления, звуки, для каждого окошка/поля ввода задать свой цвет текста, фон, текстуру и т.д.)

2. возможность заменить GUI на свой собственный (ну скорее как плагин наверное), если настроек скинования будет недостаточно

Подскажите, какие проекты используют такой подход и какую документацию лучше всего зачитать про это, а то изобретать велосипед сильно не хочу

* * *

Я так понимаю, что нужно будет задекларировать все вызовы гуя (все сообщения, все кнопки, все формы, поля текста и вообще все, с чем работает пользователь), процесс гуя запускать отдельно, которому сообщения выдавать в некотором промежуточном формате вроде «NEW_USER_MESSAGE someuser Привет!», а оно уже и мелодию для события NEW_USER_MESSAGE проиграет, и окошко/попап с текстом «новое сообщение „Привет!“ от someuser» выдать, или вообще что-то скриптовое запустить. А если настроек будет мало, то морду вообще можно переписать, например, под ncurses, здесь от NEW_USER_MESSAGE может зависеть цвет шрифта (его тоже надо разрешить настраивать). Здесь же решается проблема с локализациями, все текстовое даем юзеру. Проблема в том, что я не знаю до какого уровня абстракций идти: сделать ли многоуровневые команды, типа ALERT TYPE=NOTIFY SOURCE=REMOTEUSER ID=NEW_USER_MESSAGE, сделав минимальную обертку над тулкитом, повторяя его Alert.Info/Warning/Danger, или каждый NEW_USER_MESSAGE изобретать с нуля. А может есть что-то более лучшее?

Как вариант смотри в сторону клиент-серверной реализации.
Например mpd.
Касательно тем - не нужно, есть системная тема тулкита (qt/gtk).

CyberTribe ★★ ()

посмотри как реализован audacious. там можно натягивать шкурки не просто с другим внешним видом, но и с другим расположением элементов. лично мне не видится повода рисовать еще один лисапед.

Deleted ()

в моем велосипеде( пишу свой тулкит ) как раз можно, в коде декларативно задается описание гуи - в любом случае дефолтное должно быть, например так( один из вариантов ес-но, можно писать как в Qt или воспользоваться редактором ):

	LDialog dlg;
	LTextCtrl*	name;
	LCheckButton*	pass;

	VLAYOUT
		BOLD_LABEL( _("Name") + ":" )
		TEXT_CTRL( name ) WIDTH( 250 ) PROXY( ID_OK )
		CHK_BUTTON( pass, _("Save Password") ) RESTORABLE
		STD_BUTTONS( ID_OK | ID_CANCEL )
			FOR( ID_OK ) ENABLE_IF( name )
	END_LAYOUT

	dlg.Show();

в данном случае кроме всего прочего такое описание дает:

  • автоматический подбор размеров, отступов и выравнивания( ес-но при желании все можно указать, в том числе цвет, шрифт и т.п. );
  • кнопка OK будет активна только, если в поле имени введено значение, в макрос ENABLE_IF можно также передать какое свойство виджета использовать для проверки, или вместо виджета - метод/функцию/лямбду;
  • чекбаттон будет автоматом сохранять/восстанавливать свое значение в конфиге;
  • кнопки ОК и Сancel автоматом располагаются в зависимости от ОС;
  • автоматом ставятся ограничения на ресайз диалога( в данном случае можно расширить по длине );
  • нажатие Enter в поле имени с введенным значением - равнозначно нажатию ОК;

на самом деле плюшек еще больше, особенно при написание более сложного гуи, ну да ладно - отвлекся :)

вобщем по сути - диалогу, или даже отдельному виджету, можно сказать «SaveLayout» и «LoadLayout» для сохранения расположения дочерних виджетов + сайзеров, с учетом - скрыт виджет или нет, т.е. одной командой можно диалог переделать полностью, также можно добавить новые элементы:

BUTTON( ID_OPEN, _("My Button") ) ACTION( "open" ) // вызов пользовательской команды, которая должна быть обработана в классе диалога
BUTTON( _("Next Button") ) ACTION( ID_LIST, DO_CLEAR ) // у каждого виджета есть свой набор предопределенных команд, в макросе можно обратится к ним либо через указатель, либо через идентификатор, плюс дописать, что мы хотим, а также указать параметр для команды
BUTTON( btn, _("Third Button") ) ACTION( [](LWidget* p) { p.Label = _("Test"); }, btn )
BUTTON( _("Dummy Button") ) ACTION( THIS, DO_CHANGE_LABEL, _("Test") )

вариантов много на самом деле - кому как больше нравится, в тексте, что отдаст SaveLayout ес-но все описано не в таком виде и не все можно сохранить( ес-но придется использовать числовые или строковые идентификаторы виджетов и/или команд - если хочется пользоваться скинами ), но думаю смысл понятен.

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

lester ★★★★ ()

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

Я бы, наоборот, задекларировал все вызовы и коллбэки основной программы.

Miguel ★★★★★ ()
Ответ на: комментарий от lester

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp

  • автоматический подбор размеров, отступов и выравнивания( ес-но при желании все можно указать, в том числе цвет, шрифт и т.п. );
  • кнопка OK будет активна только, если в поле имени введено значение, в макрос ENABLE_IF можно также передать какое свойство виджета использовать для проверки, или вместо виджета - метод/функцию/лямбду;
  • чекбаттон будет автоматом сохранять/восстанавливать свое значение в конфиге;
  • кнопки ОК и Сancel автоматом располагаются в зависимости от ОС;
  • автоматом ставятся ограничения на ресайз диалога( в данном случае можно расширить по длине );
  • нажатие Enter в поле имени с введенным значением - равнозначно нажатию ОК;

Омг, ты сделал некое подобие Magritte на макросах.

yoghurt ★★★★★ ()
Ответ на: комментарий от yoghurt

это просто функции так «обозваны», чтоб сделать декларацию более читабельной, так что ничего страшного - гуй как он есть, бессмысленный и беспощадный ;)

lester ★★★★ ()

> Омг, ты сделал некое подобие Magritte на макросах.

надо будет почитать - спасибо за ссылку, но практически 100% у меня быстрее будет работать, как я уже говорил - на n810 все работает отлично

lester ★★★★ ()
Ответ на: комментарий от lester

Быстрее то по-любому быстрее, по ссылке всё в рантайме строится по модели, да ещё и на Смолтоке :)

Но там всё более абстрактно - описывается собственно не сам интерфейс, а представляемый объект: какие у него есть поля, какого (предположительно) они типа и как до них достучаться. Всякие валидаторы и контроллеры описываются там же.

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

Собственно так можно для одной и той же модели генерить формочки для разных веб-фреймворков, (теоретически) описания и привязки для ORM.

Если бы такая же штука была у плюсов, было бы круто %)

yoghurt ★★★★★ ()

клиент-серверная модель. логика отдельно, гуи какой прикрутишь такой и будет. Типа как mpd.

azure ★★ ()
Ответ на: комментарий от yoghurt

> Если бы такая же штука была у плюсов, было бы круто %)

я почитаю про Magritte - может хоть что-то полезное получится «содрать» без особых хаков :)

Собственно так можно для одной и той же модели генерить формочки для разных веб-фреймворков, (теоретически) описания и привязки для ORM.


у меня можно пользоваться сторонними тулкитами - т.е. есть отдельная прослойка, чтоб вместо «себя» пользоваться gtk, Qt, wxWidgets и тем же FLTK, зачем я это сделал - хз, конечно, больше из спортивного интереса( да и сложного ничего не было - только много рутинной работы ), но зато приложение может использовать родной для окружения тулкит без всяких проблем - достаточно просто докинуть «расширение» и указать его при инициализации, т.е. можно даже выбирать с чем запуститься, чтоб не выглядеть, например, в КДЕ белой вороной

lester ★★★★ ()
Ответ на: комментарий от yoghurt

кстати в чем как раз смысл это всего - я параллельно пишу редактор GUI под свой тулкит, который позволит удобно связывать между собой виджеты и будет автоматом генерить код + ( сейчас полетят какашки ) предоставит функциональность на уровне Delphi - т.е. кинул например грид, в свойствах ткнул, что источник данных - текущий элемент комбобокса, которому сказали показывать таблицы из БД, снизу кинул кнопки - ткнул в свойствах, что брать объект из того же комбобокса, получил список действий - указал нужное действие + возможный параметр( опять же можно привязать к другому виджету ), и все - можно радостно запускать и жать кнопки, рассказывая всем, что я кулхацкер и пешу на цэпэпэ :)

П.С. кстати аналог файндера у меня пишется в несколько строк - «кидается» виджет DataBrowser( колонки как в файндере ), а ему в качестве источника данных уже отдается либо «файловая система», либо БД, либо архив и т.п., либо даже все вместе

lester ★★★★ ()
Ответ на: комментарий от lester

опять же ес-но, если удалить/добавить/переименовать таблицу в данном примере - все само обновляется, т.е. можно сделать кнопку, которая удалит показываемую таблицу и не писать ни строчки кода

П.С. ладно - что-то я заспамил, надо завести блог или лучше вики :)

lester ★★★★ ()
Ответ на: комментарий от Hjorn

> Чего только люди не изобретают, чтобы Tcl/Tk не использовать :)

так нет толком наглядных примеров, которые доказывают его надобность, из тех двух приложений, что я видел - оба были страшные как ядерная зима, и функциональность у них была почти нулевая

lester ★★★★ ()
Ответ на: комментарий от name_no

> а что ж ты тогда на работе пишешь?

разные нудные и неинтересные вещи, вроде движка отчетов, экспорта в PDF, драйвер к ODBC и т.д. и т.п

lester ★★★★ ()
Ответ на: пишу свой тулкит от mv

кстати можешь не верить, но я кода на MFC так и не видел ни разу; вроде wxWidgets оттуда много взяло - с ним я работал много, а вот именно с MFC сталкиваться не приходилось

lester ★★★★ ()
Ответ на: комментарий от lester

> из тех двух приложений, что я видел - оба были страшные как ядерная зима и функциональность у них была почти нулевая

Ага, и поэтому не надо пытаться его использовать или тем паче расширять, а лучше написать собственный Tk и к нему подобие Tcl на плюсовых макросах. Логично, да :) Впрочем в рамках хобби это нормально.

Hjorn ()
Ответ на: комментарий от Hjorn

> а лучше написать собственный Tk и к нему подобие Tcl на плюсовых макросах. Логично, да :)

лучше сначала разобраться почему оно такое непопулярное

lester ★★★★ ()
Ответ на: комментарий от lester

так нет толком наглядных примеров, которые доказывают его надобность

у него не такая уж большая документация, вообще говоря. посмотреть на bind и gridplus, во всяком случае, не помешает

jtootf ★★★★★ ()
Ответ на: комментарий от lester

лучше сначала разобраться почему оно такое непопулярное

в 98 году Sun бросила Tcl ради Java - и где теперь Sun? :)

jtootf ★★★★★ ()
Ответ на: комментарий от jtootf

> и где теперь Sun? :)

там же где Tcl? :) я прочитаю конечно про Tk, но копировать смысла нет - раз люди не хотят им пользоваться, они тем более не будут пользоваться набором костылей на С++ для его «эмуляции»

lester ★★★★ ()
Ответ на: комментарий от lester

> раз люди не хотят им пользоваться, они тем более не будут пользоваться набором костылей на С++ для его «эмуляции»

Вот это совершенно неправильный вывод. Людей в основном отпугивает олдскульный внешний вид виджетов (хотя сейчас для ttk есть вполне нормальные темы) и пресловутая производительность тикля, а вовсе не архитектура. Писать GUI на связке Tcl/Snit/Tk - одно удовольствие.

Hjorn ()
Ответ на: комментарий от Hjorn

> Писать GUI на связке Tcl/Snit/Tk - одно удовольствие.

можно посмотреть скриншот чего-то написанного лично вами? ( я не придираюсь - просто интересно )

lester ★★★★ ()
Ответ на: комментарий от lester

копировать смысла нет

ну это само собой

люди не хотят им пользоваться

а ещё люди не хотят пользоваться линуксом и, хм, коммон лиспом ;)

они тем более не будут пользоваться набором костылей на С++ для его «эмуляции»

я пробовал писать на C++/Tk, удовольствие ниже среднего - эффективней даже использование C++/Tcl с Tk в качестве расширения. не годится C++ (в текущей версии стандарта, по крайней мере) для полноценного внедрения Tk в качестве eDSL

jtootf ★★★★★ ()
Ответ на: комментарий от jtootf

> и, хм, коммон лиспом ;)

и я их понимаю :)

удовольствие ниже среднего


ну вот и я о чем - отсюда и родилась идея сделать аналог делфи, только лучше - ведь накидывать гуй на самом деле вполне ес-но и логично, зачем тратить время программиста на рутину? гуй должен легко создаваться( чуть ли не только мышкой ), легко выставляться все связи, списки/деревья и т.п. должны сами автоматом обновляться и т.д.

lester ★★★★ ()
Ответ на: комментарий от lester

смотрится очень жалко

там очень странный список. грамотно написанное на Tcl/Tk приложение смотрится где-то так:

http://sk1project.org/modules.php?name=Products&product=sk1&op=screenshots#screenshots

но вообще в серьёзных проектах на Tcl на внешний вид, как правило, ложат болт :(

jtootf ★★★★★ ()
Ответ на: комментарий от lester

идея сделать аналог делфи, только лучше

но почему как расширение C++, а не самостоятельный язык (компилирующийся в тот же C++ или C)? вот этого не понимаю

jtootf ★★★★★ ()
Ответ на: комментарий от jtootf

> но почему как расширение C++, а не самостоятельный язык (компилирующийся в тот же C++ или C)? вот этого не понимаю

во-первых я не осилю полноценный язык( разве что именно «компиляцию» в С++, но именно это мне не нравится в Qt ), во-вторых - он тут вряд ли нужен, т.к. опять же все будет создаваться в редакторе, а программист будет работать с интерфейсами, которых вполне хватает, чтоб он смог сам расширить гуй, например, добавить свой контейнер, который будет показываться в том же дереве или колонках; кстати интерфейс исходных данных для них, а также для списков, комбобоксов и т.д. один и тот же - и достаточно простой

lester ★★★★ ()
Ответ на: комментарий от lester

можно посмотреть скриншот

Нет, скриншотов не будет, а то опять начнутся стенания про уродливый мотиф. Простой пример кода из домашней шняги могу привести, но он не показателен конечно, надо самому трогать и исследовать.

constructor {model} {
    variable _model
    set _model $model
    
    # Define widgets
    ttk::frame $win.frmFields
    ttk::frame $win.frmButtons
    ttk::frame $win.frmCountry
    ttk::frame $win.frmYear
    ttk::frame $win.frmLogoFname
    ttk::labelframe $win.frmLogoPreview   -text "Logo preview"
    ttk::labelframe $win.frmCountryEditor -text "New country"
    foreach f {Name Country Year LogoFname} {
        ttk::label $win.lbl$f -text [dict get $Fields $f caption]
    }
    ttk::label $win.imgLogo
    ttk::entry $win.entName -textvariable [myvar fldName]
    ttk::entry $win.entYear \
         -width 8 -textvariable [myvar fldYear]
    ttk::entry $win.entLogoFname \
        -state readonly -textvariable [myvar fldLogoFname]
    ttk::combobox $win.cmbCountry \
        -state readonly -values [$_model fetchCountries]
        
    ttk::button $win.btnAddCountry \
        -width 3  -text "n"  -takefocus 0 -command [myproc loadCountryEditor]
    ttk::button $win.btnLoadLogo \
        -width 3  -text "o" -command [myproc loadLogo]
    ttk::button $win.btnApply \
        -text "Save" -state disabled -command [mymethod saveBand]
    ttk::button $win.btnCancel \
        -text "Cancel" -command [mymethod cancel]
        
    # Set initial layout
    pack $win.entYear -in $win.frmYear -side left
    pack $win.cmbCountry -in $win.frmCountry \
        -side left -padx {0 6} -fill x -expand true
    pack $win.btnAddCountry -in $win.frmCountry -side right
    pack $win.entLogoFname  -in $win.frmLogoFname \
        -side left -padx {0 6} -fill x -expand true
    pack $win.btnLoadLogo -in $win.frmLogoFname -side right
        
    set fieldsTable {
      lblName       entName
      lblCountry    frmCountry
      lblYear       frmYear
      lblLogoFname  frmLogoFname
    }
    foreach {caption field} $fieldsTable {
        grid $win.$caption $win.$field -in $win.frmFields
        grid $win.$caption -sticky e -padx 3 -pady 3
        grid $win.$field   -sticky ew
    }
    pack $win.imgLogo -in $win.frmLogoPreview
    pack $win.btnApply -in $win.frmButtons -side left
    pack $win.btnCancel -in $win.frmButtons -side right
    pack $win.frmFields $win.frmButtons -padx 6 -pady 6 -fill both

    setFocusRing {
      $win.entName $win.cmbCountry $win.entYear
      $win.btnLoadLogo $win.btnApply $win.btnCancel
    }

    # Bind events
    bind $win.entName <FocusOut> [mymethod validate name]
    bind $win.entName <Leave>    [mymethod validate name]
    bind $win.entOriginYear <FocusOut> [mymethod validate originYear]
    bind $win.entOriginYear <Leave>    [mymethod validate originYear]
}
Hjorn ()
Ответ на: комментарий от Hjorn

подсветки синтаксиса не хватает для нормальной читабельности, но это проблема движка ЛОР, конечно, и честно говоря не вижу особой разницы между декларативной записью макросами как в моем примере, и этим кодом, разве что у меня layout сделан в виде «блока»

lester ★★★★ ()
Ответ на: комментарий от lester

Дык, я о том и говорю - разницы особой нет. Только tcl/tk - готовое и зрелое решение, и там есть много всего вкусного, что в маленьком примере просто не покажешь. Тут у меня код конечно не самый декларативный, прямо скажем :) Но при желании уровень декларативности можно поднять, благо динамический тикль много чего позволяет. Вообще, для меня главное, что нужно от GUI - не заморачиваться с ним, и тут tcl/tk решает. Проще пока ничего нет.

Hjorn ()
Ответ на: комментарий от jtootf

> я пробовал писать на C++/Tk, удовольствие ниже среднего

По-моему Tk имеет смысл только в паре с Tcl, ну может быть с Ruby ещё. Иначе всё довольно уныло получается.

Hjorn ()
Ответ на: комментарий от Hjorn

> Только tcl/tk - готовое и зрелое решение

не спорю, у моего велосипеда возможно еще все впереди

что в маленьком примере просто не покажешь

Вообще, для меня главное, что нужно от GUI - не заморачиваться с ним



опять же согласен

lester ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.