Форум » GUI » Новая версия Расширенного релиза библиотеки MiniGUI (часть VI ) (продолжение) » Ответить

Новая версия Расширенного релиза библиотеки MiniGUI (часть VI ) (продолжение)

gfilatov: Начало темы находится здесь, а теперь АНОНС * АНОНС * АНОНС * АНОНС * АНОНС Готовится к опубликованию новая сборка №48, которая выйдет в конце недели. Если у Вас есть интересные наработки для включения в новый релиз, то сейчас самое удобное время для их отправки мне Кратко, что нового: - исправление обнаруженных ошибок и неточностей кода; - новый класс HEADERIMAGE для Grid и Browse; - свойство Address в Hyperlink может теперь открывать папку или файл на диске; - добавлен NOTABSTOP класс для Browse; - поддержка пользовательских компонентов (заимствована из оффициального релиза); - расширения и исправления в библиотеках TsBrowse и PropGrid; - обновлены сборки Харбор и HMGS-IDE; - новые и обновленные старые примеры (как обычно ).

Ответов - 300, стр: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 All

Haz: Andrey пишет: из-за таймера, у меня в проге 3 таймера. Вместо таймера лучше использовать зацикленный поток и из него посылать событие окну , которое вызывает нужную процедуру Еще лучше - обрабатывать отдельным потоком, но при этом не забываем что рабочие области в потоках не видны постоянно нужно передавать через зеро спэйс или открывать свои в потоке ( можно с тем же алиасом ) Переделка быстрая, к примеру этот поток контролирует oBrw:bCahange и вызывает процедуру только когда пользователь оторвал палец от клавиатурных стрелок ( есть любители ездить вверх-вниз по бровсу без остановок , что вызывает бесконечное число ненужных вызовов bChenge ) [pre2] ... __objAddData (oBrw, 'tTs' ) __objAddData (oBrw, 'lChange' ) oBrw:tTs := HB_DateTime() oBrw:lChange := .f. oBrw:bChange := {|| oBrw:tTs := HB_DateTime(), oBrw:lChange := .t. } ... hb_threadDetach( hb_threadStart( HB_THREAD_INHERIT_PUBLIC, @Change() )) ... Func Change() local lMore := .t. local tTs := HB_DateTime() local nTick := 0.041667 / ( 3600 * 4 ) // кждую 1/4 секунды, 0,041667 это разница во времени в 1 час while lMore if oBrw:lChange if HB_DateTime() - oBrw:tTs > nTick oBrw:lChange := .f. _wpost(100, "Form_0") end end HB_ReleaseCPU() end return nil [/pre2]

SergKis: Haz пишет Переделка быстрая, к примеру этот поток контролирует oBrw:bCahange и вызывает процедуру Не забываем, для доступа к одним и тем же Public переменным (особенно при изменении значений) надо делать блокировки доступа. Можно делать: в основном потоке PUBLIC p_oThr := oThrData():New() // блокировка доступов на уровне класса p_oThr:oBrw := oBrw в основном потоке и Thread работаем безопасно с oBrw p_oThr:oBrw:lChange := .f. ...

Andrey: Andrey пишет: Вот такая ошибка: Error BASE/1001 Неизвестная функция: EVENTS Разобрался почему так происходит. Большое СПАСИБО Сергею за его помощь. Нельзя использовать БОЛЬШОЙ ON INIT для окна, плюс в нём делал ОКНО запроса пароля ! А это получается большая вложенность. Разбил на 2-3 части и всё заработало. Пропала ошибка ! Было:[pre2] SET EVENTS FUNCTION TO MyEventsHandler DEFINE WINDOW Form_Main ; MAIN NOSHOW ; ON INIT {|| MyInitForm() } ; Сделал: Set_bEvents("MyEventsHandler") // новый синтаксис ON INIT {|| DoEvents(), _wPost(0) } ; ... WITH OBJECT This.Object :Event( 0, {|ow| // запуск при инициализации окна myInitForm() DO EVENTS _wPost(1, ow) // запуск события 1 Return Nil }) :Event( 1, {|ow| // запуск при инициализации окна myInitForm2(ow) DO EVENTS Return Nil }) ....[/pre2]


SergKis: SergKis пишет в основном потоке и Thread работаем безопасно с oBrw p_oThr:oBrw:lChange := .f. К TsBrowse это не относится, т.к в осн. потоке Tsb работает с oBrw и ему глубоко до лампочки наша переменная p_oThr, по мне, надо все делать в осн. потоке с oBrw, т.е. из потока к тсб нет прямого доступа, только, примерно, такой, на окне имеем :Event(101, {|ow,ky,lval,obr| obr := This.oBrw.Object, obr:lChange := lVal, ky := ow }) :Event(102, {|ow,ky,lval,obr| obr := This.oBrw.Object, obr:Refresh(lval), ky := ow }) в потоке делаем вместо oBrw:lChange := .f. _wSend(101, oBrw, .F.) _wPost(100, oBrw) или если надо refresh с полной прорисовкой, то _wSend(102, oBrw, .T.) oBrw идет для чтения, потому блокировка не нужна, т.к. адрес объекта oBrw не меняется похожим образом поступаем с Label и др. контролами (можно списком), в потоке в p_oThr:cLabel_1, ... заносим значения p_oThr:cLabel_1 := ... p_oThr:cLabel_2 := ... ... p_oThr:cLabel_N := ... _wPost(200, oBrw) на окне есть событие[pre2] :Event(200, {|ow| Local cNm FOR EACH cNm IN {"Label_1","Label_2",...,"Label_N"} cVal := p_oThr:&("c"+cNm) SetProperty(ow:Name, cNm, "Value", cVal) NEXT Return Nil } [/pre2]

Haz: Не забываем, для доступа к одним и тем же Public переменным Само собой с семафорами там где нужно, привел упрощённый пример в котором переменной пользуется сам поток и все

SergKis: gfilatov2002 Сделал изменения у себя в minigui.lib (h_events.prg, h_objects.prg, h_objmisc.prg) - перевел bEvents с блока кода на ф-ю hb_ExecFromArray() - сделал возможность задавать и работать с событиями в мнемонике, а не только в числах, т.е. задаем . :Events({0, "InitWindow"}, {|...| ... }) . :Events({1, "Main-Menu"}, {|...| ... }) в сообщениях исп. символьное имя вместо числового кода (если ставим много событий, то с мнемоникой легче) _wPost("InitWindow") или _wPost(0) _wPost("Main-Menu") или _wPost(1) добавил ф-ии _pPost(...), _pSend(...) для посылки сообщений App.Object параметры аналогичны _wPost(...), _wSend(...) oThis := _oThis() - вернет объект контейнер уст. This среды oThis := _oThis("Form_5") или oThis := _oThis(ow) - поставит среду окна из параметра и вернет объект контейнер уст. This среды oThis := _oThis(oThis) - вернет что было в тек. This, поставит из oThis Переменные объекта контейнера для работы oThis:nFormIndex oThis:cEventType oThis:cType oThis:nIndex oThis:cFormName oThis:cControlName не знаю, надо это или нет ? Пример кода [pre2] WITH OBJECT This.Object // events begin :Event({ 0, "myInit_Window"}, {|ow| // запуск при инициализации окна ... Return Nil }) :Event({ 1, "myMain-Menu"}, {|ow| // главное событие запуска всех отложенных событий ... Return Nil }) посылать сообщения можно так _wPost(0), _wPost(1) или _wPost("myInit_Window"), _wPost("myMain-Menu") Для App.Object (работа таймеров в осн. потоке БЕЗ учета\смены среды This) WITH OBJECT App.Object // объект программы // set events App\Program begin :Event({ 1, "Timer_1" }, {|oa,ky,nn,cnam| // таймер 1 ... :Event({ 2, "Timer_2" }, {|oa,ky,nn,cnam| // таймер 2 ... :Event({10, "Timer_All"}, {|oa,ky,nn,cnam| // таймера все 3-и в одном событии, для примера ... посылаем сообщения так (это SET EVENTS FUNC TO App_OnEvents или Set_bEvents("App_OnEvents")) FUNCTION App_OnEvents( hWnd, nMsg, wParam, lParam ) ... ********************************************************************** CASE WM_TIMER ********************************************************************** cMsg := "Event arrived - WM_TIMER" IF ( i := AScan ( _HMG_aControlIds, wParam ) ) > 0 cn := _HMG_aControlNames[ i ] ky := Val( subs(cn, RAt("_", cn) + 1) ) cMsg += " = " + cn + " ( ky = "+hb_ntos(ky)+" )" // не меняет среду This i := "Timer_"+hb_ntos(ky) // имя события _pPost(i, ky, cn) //_pPost(ky, ky, cn) //(App.Object):PostMsg(ky, ky, cn) // свое событие у timer-а //IF ky == 9 // (App.Object):PostMsg(ky, 0, cn) // 1.свое событие у timer-а //ELSE //_pPost("Timer_All", ky, cn) // (App.Object):PostMsg(10, ky, cn) // 2.одно событие для 3-х timer-ов, //ENDIF ENDIF ... [/pre2]

PSP: SergKis пишет: не знаю, надо это или нет ? Хоть я и не в теме minigui, но могу точно сказать: надо!) Это читается и воспринимается легче. #define тоже не просто так придумали когда-то)

gfilatov2002: SergKis пишет: Сделал изменения у себя SergKis Как я уже ранее писал на форуме, любые Ваши изменения интересны и познавательны, поскольку они основаны на практическом опыте. Но очень важно иметь в библиотеке пример применения этих изменений (например, как Tsb_Basic_3 или Tsb_Basic_4), так как это другой стиль написания кода, который требует понимания взаимосвязи функций и событий.

gfilatov2002: Andrey пишет: Вот такая ошибка: Error BASE/1001 Неизвестная функция: EVENTS Уже поправил код, чтобы такая ошибка не возникала и функция EVENTS() не терялась

SergKis: gfilatov2002 пишет Но очень важно иметь в библиотеке пример применения этих изменений (например, как Tsb_Basic_3 или Tsb_Basic_4), так как это другой стиль написания кода, который требует понимания взаимосвязи функций и событий. Сделаю, надо на подумать время

Andrey: gfilatov2002 пишет: Уже поправил код, чтобы такая ошибка не возникала и функция EVENTS() не терялась Григорий, когда обновление будет ? Добавил в код ещё пару форм и всё труба, опять у заказчика лезет ошибка: [pre2]Error BASE/1001 Неизвестная функция: EVENTS Args: [1] = N 524726 [2] = N 129 [3] = N 0 [4] = N 1632052 --------------------------------- Stack Trace --------------------------------- Called from EVENTS(0) Called from _CREATEWINDOWEX(0) Called from TSBROWSE:CREATE(0) Called from TSBROWSE:NEW(0) Called from _DEFINETBROWSE(0) Called from FORM_MYTABLE(324) in module: Source\Tbrw_table.prg Called from TBRWDOGOVOR(16) in module: Source\Tbrw_1Run.prg Called from EXECTASK(172) in module: Source\ExecTask.prg Called from HB_EXECFROMARRAY(0) Called from (b)METRO_BUTTON(455) in module: Source\main_forms.prg Called from DO_WINDOWEVENTPROCEDURE(82) in module: h_objmisc.prg Called from TWNDDATA:DOEVENT(1233) in module: h_objects.prg Called from DO_ONWNDLAUNCH(256) in module: h_objmisc.prg Called from (b)INIT(126) in module: h_init.prg [/pre2] Разбил свой ON INIT на 4 события, не помогает... Блин заколдованный круг, у меня работает у заказчика нет. У заказчика на сервере 16 Гб оперативки, а прога не может найти функцию в ядре программы.

alkresin: Andrey пишет: прога не может найти функцию в ядре программы. Я не имею представления о внутренностях Minigui, но с подобным сталкивался - в моем HbEdit. Это происходило иногда во время выполнения кода в Idle - состоянии. Пришлось, в конце концов, отказаться от использования Idle и реализовать нужную функциональность другим образом. Так что, похоже, проблема где-то во внутренностях Harbour. Еще один похожий эпизод был с использованием таймера в программе на HwGUI, тогда, правда, это не проявлялось в "забывании" имени функции. Иногда, ровно в 24:00, программа вылетала в функции - обработчике таймера. Проверял сто раз - вылетала программа в самом начале выполнения функции, до всяких действий. Пришлось сделать глупейшую вещь: перед полуночью временно изменял интервал таймера, чтобы он не срабатывал в 24:00. Помогло). Здесь я грешу на Windows. Harbour и HwGUI, полагаю, тут не замешаны.

Andrey: Новая версия МиниГуи 23.09 Опять засада в работающем коде. В старой версии 23.04.4 ни разу такого не было !!! У юзера за сегодня ПЯТЫЙ раз (это только 13:00) за сегодня падает программа вот с такой ошибкой: [pre2]DbInfo: Alias - USER2LOG, Recno - 138035/138035 Time from start: 0 days 0 hours 0 mins 59 secs Error BASE/1003 Переменная не существует: EVENT --------------------------------- Stack Trace --------------------------------- Called from USER2LOGWRITE(103) in module: Source\users2log.prg Called from USERLOGGETBOXCARD(4702) in module: Source\Tbrw_fCard.prg Called from MYWRTGETUSERLOG(2742) in module: Source\Tbrw_fCard.prg Called from (b)MYCARDFIELDGETBOX(2611) in module: Source\Tbrw_fCard.prg Called from _DOCONTROLEVENTPROCEDURE(1993) in module: h_windows.prg Called from EVENTS(2146) in module: h_events.prg Called from DOMESSAGELOOP(0) [/pre2] Вот мой код: [pre2] nSel := SELECT("User2Log") IF nSel == 0 cMsg := ";;БАЗА ЖУРНАЛА-действий закрыта !;" WaitWindowError( cMsg ) ELSE SELECT User2Log APPEND BLANK // добавить запись dDT := User2Log->DT_MODIFY IF User2Log->( RLock() ) User2Log->EVENT := dDT // 3 TimeStamp 8 0 - строка 103 User2Log->NEVENT := nEvtn // 6 Numeric 6 0[/pre2] Да, имел неосторожность назвать поле EVENT И что теперь делать ? Переименовывать ?

SergKis: Попробуй (alias уже установлен ссылку можно не делать) 1. FieldPut(FieldPos('EVENT'), dDT) 2. REPLACE EVENT with dDT или REPL EVENT with dDT // тут может FIELD-><имя> добавлять надо 3. hRec := { "EVENT" => dDT, "NEVENT" => nEvtn } lRet := HMG_HashToRec( hRec ) RLock\UnLock внутри есть, можешь проверить lRet -> .T. - Ok! PS Можешь читать запись в Hash. hRec := HMG_RecToHash( cFieldList, cNames ) (данные получает по макро, лучше бы через FieldGet(FieldPos(...))) Можешь писать запись из Hash. HMG_HashToRec( hRec, cFieldList ) Можешь получить запись в контейнере oRec := oHmgData() ; oRec:Set(HMG_RecToHash(), .T.) или oRec := oHmgData():Set(HMG_RecToHash(), .T.) и работать ? oRec:Event, oRec:NEvent oRec:Event := dDT oRec:NEVENT := nEvtn lRet := HMG_HashToRec(oRec:CloneHash()) если названия полей не совпадут с методами

gfilatov2002: Подготовил 1-е обновление для сборки 23.09 с последними исправлениями и дополнениями, которое будет опубликовано на следующей неделе Что исправлено: * Updated: Adaptation FiveWin Class TSBrowse 9.0 in HMG: - fixed an incorrect call of the Events() function when changing of the main event listener with the SET EVENTS FUNCTION TO <name> command. The bug was reported by Verchenko Andrey Благодарю Андрея за настойчивость и Сергея - за точное определение причины этой ошибки

SergKis: gfilatov2002 Немного модифицируйте ф-ю в Excel (я не работаю с ole совсем, потому сделать ... ), добавить вывод строк заголовков и подножия, т.е. что то такое[pre2] *-----------------------------------------------------------------------------* FUNCTION HMG_DbfToExcel( cFieldList, aHeader, bFor, bWhile, nNext, nRec, lRest ) *-----------------------------------------------------------------------------* LOCAL nRecNo := RecNo() LOCAL bLine, aFooter, aTitle, o LOCAL oExcel, oBook, oSheet, oRange LOCAL nCols LOCAL nRow := 1 IF HB_ISOBJECT( aHeader ) .and. aHeader:ClassName $ "THMGDATA,TKEYDATA,TTHRDATA" o := aHeader aHeader := o:aHeader aFooter := o:aFooter aTitle := o:aTitle ELSEIF HB_ISARRAY( aHeader ) o := ASize( AClone( aHeader ), 3 ) aHeader := o[1] aFooter := o[2] aTitle := o[3] ENDIF ... далее добавить строки из aTitle фонт bold, если заданы (колонки объединить) в конце таблицы строки из aFooter фонт bold, если заданы (как колонки aHeader) [/pre2]

SergKis: PS Ф-я HMG_HashToRec( hRec, cFieldList ) не учитывает наличие защищенных полей и что ключ может быть не символьный, а значение к ключу NIL, т.е. из ф-ии oRecPut(oRec, aFieldList) обрывок[pre2] FOR EACH aFld IN oRec:GetAll() cFld := aFld[1] IF !HB_ISCHAR( cFld ) .or. aFld[2] == NIL LOOP ENDIF IF ( nPos := FieldPos( cFld ) ) > 0 IF FieldType( nPos ) $ "+^=" LOOP // write protection ENDIF FieldPut( nPos, aFld[2] ) nCnt++ ENDIF NEXT [/pre2]

Andrey: Что-то непонятная ошибка начала показываться: Какая картинка, строка вызова - ничего нет. Гадание на кофейной гуще... Да я что-то забыл указать, а где искать то ?

Andrey: Нашёл в исходнике h_tbrowse.prg эту ошибку: [pre2] IF uBitMap != NIL .AND. ValType( uBitMap ) != "L" DEFAULT lNoLines := .T. cHeading := iif( ValType( uBitMap ) == "B", Eval( uBitMap ), uBitMap ) cHeading := iif( ValType( cHeading ) == "O", Eval( ::bBitMapH, cHeading ), cHeading ) IF Empty( cHeading ) MsgStop( "Image is not found!" + CRLF + "cHeading=???????" + CRLF + ProcNL(), "Error" ) RETURN NIL ENDIF nLHeight := SBmpHeight( cHeading )[/pre2] Добавить так можно ? Ещё бы указать какой картинки нет. Только я не знаю как ?

SergKis: SergKis пишет Сделаю, надо на подумать время Пример тут https://TransFiles.ru/cin6x Что сделано [pre2] Прилагаемый пример сделан на основе APP_OOPREPORT\demo.prg, на кнопку Print окни выборки-отчета Report сделал вызов AlertInfo(), вместо MsgInfo(), что бы показать как работает фокус на этих окнах, т.к. окно AlertInfo() это hmg, а окно MsgInfo() - нет. Правил файлы: - h_controlmisc2.prg ~~~~~~~~~~~~~~~~~~~~ ... добавил, пропущенные свойства в DEFINE TBROWSE ON GOTFOCUS oParam:bOnGotFocus ; ON CHANGE oParam:bOnChange ; ON LOSTFOCUS oParam:bOnLostFocus ; ON DBLCLICK oParam:bOnDblClick ; ... - h_events.prg ~~~~~~~~~~~~~~ ... добавил код STATIC s_bEvents, s_lEvents := .F. *-----------------------------------------------------------------------------* FUNCTION Set_bEvents ( bBlock ) *-----------------------------------------------------------------------------* IF HB_ISCHAR( bBlock ) .and. "(" $ bBlock bBlock := hb_ULeft( bBlock, hb_UAt( "(", bBlock ) - 1 ) ENDIF s_lEvents := !Empty( bBlock ) RETURN ( s_bEvents := bBlock ) *-----------------------------------------------------------------------------* FUNCTION Events ( hWnd, nMsg, wParam, lParam ) *-----------------------------------------------------------------------------* ... IF s_lEvents IF HB_ISBLOCK( s_bEvents ) // could be a static function IF !Empty( Eval( s_bEvents, hWnd, nMsg, wParam, lParam ) ) RETURN 1 ENDIF ELSEIF HB_ISCHAR( s_bEvents ) // Can't be a static function IF !Empty( hb_ExecFromArray(s_bEvents, {hWnd, nMsg, wParam, lParam}) ) RETURN 1 ENDIF ENDIF ENDIF ... Задавать свой обработчик можно так 1. Set_bEvents("App_OnEvents") // NO static function 2. Set_bEvents( {|...| App_OnEvents(...) } ) // static function Во втором случае обработчик может быть STATIC FUNCTION, по мне, предпочтительнее, т.е. STATIC FUNCTION App_OnEvents( hWnd, nMsg, wParam, lParam ) ... - h_objects.prg ~~~~~~~~~~~~~~~ ... добавлена возможность задавать не только числом, но символьной мнемоникой: WITH OBJECT App.Object // set events App\Program begin :Event({ 1, "Timer_1"}, {|oa,ky,nn,cnam| // таймер 1 ... :Event({ 2, "Timer_2"}, {|oa,ky,nn,cnam| // таймер 2 ... :Event({ 1, 'отчет First'}, {|ow,ky| Report(ow, ky) } ) :Event({ 2, 'отчет Last' }, {|ow,ky| Report(ow, ky) } ) ... активация событий посылкой сообщения так // App.Object _pPost(1, nParam, xParam) _pPost("Timer_1", nParam, xParam) _pPost(2, nParam, xParam) _pPost("Timer_2", nParam, xParam) // This.Object :UserKeys( VK_1, {|ob| _wPost('отчет First' , ob:cParentWnd) } ) или :UserKeys( VK_1, {|ob| _wPost(1, ob:cParentWnd) } ) :UserKeys( VK_2, {|ob| _wPost(2, ob:cParentWnd) } ) или :UserKeys( VK_2, {|ob| _wPost('отчет Last' , ob:cParentWnd) } ) ON KEY SHIFT+1 ACTION _wPost('отчет First' ) или ON KEY SHIFT+1 ACTION _wPost(1) ON KEY SHIFT+2 ACTION _wPost(2) или ON KEY SHIFT+2 ACTION _wPost('отчет Last' ) ... В прилагаемом примере показано это - h_objmisc.prg ~~~~~~~~~~~~~~~ ... поправлены ф-ии и добавлены новые _pPost(nEvent, nParam, xParam) - активация событий для App.Object _pSend(nEvent, nParam, xParam) - активация событий для App.Object _oThis() - снимок текущей среды This в oHmgData(), формирование переменных аналогов _HMG_This... ( o := oTis ): o:FormIndex o:EventType o:Type o:Index o:FormName o:ControlName o:FocusedForm o:FocusedControl o:ControlCargo o:ControlHandle o:ControlParentHandle o:ControlParentName o:FormCargo o:FormHandle o:FormParentHandle o:FormObject если o:FocusedForm и o:FocusedControl имеют значение (фокус на hmg), а o:FormName не задано или задано, но др. именем, то все переменные объекта oThis приводятся к значениям от o:FocusedForm, только среда окна, не контрола. Преременные _HMG_This... по умолчанию не меняются, но их можно принудительно привести в соответствие переменным объекта oThis. Для этого надо в функция получения использовать параметр lSets может принимать значения: 0 - вернуть все как есть, без приведения к переменной o:FocusedForm .F. - привести только внутренние переменные oThis к значениям от переменной o:FocusedForm .T. - привести все переменные, включая _HMG_This..., к значениям от переменной o:FocusedForm В прилагаемом примере показано использование _oThis(), параметры FUNCTION _oThis( oThis, lSets ) // Snapshot of current data This or Sets ... - h_tbrowse.prg ~~~~~~~~~~~~~~~ ... METHOD LostFocus( hCtlFocus ) CLASS TSBrowse ... IF ! Empty( ::bLostFocus ) Eval( ::bLostFocus, hCtlFocus, Self ) //!!! правка ENDIF // ^^^^ ... В методе ::GotFocus( hCtlLost ) и ::LostFocus( hCtlFocus ) сторическое не соответсвие параметров IF ::bGotFocus != NIL Eval( ::bGotFocus, Self, hCtlLost ) ENDIF // ^^^^ [/pre2]



полная версия страницы