Форум » Документация » Harbour для начинающих » Ответить

Harbour для начинающих

alkresin: Просто сообщаю, что я кардинально обновил одноименную страницу у себя на сайте: http://www.kresin.ru/hrbfaq.html, существенно пополнил (и продолжаю понемногу пополнять) раздел "Отличия от Клиппера".

Ответов - 157, стр: 1 2 3 4 5 6 7 8 All

Andrey: Здравствуйте. Очень мало документации по переменным времени. Можете сделать отдельный раздел у себя в справке ? Чтобы было в одном месте.[pre2] tDtm := HB_DATETIME() ? HB_TSTOSTR( tDtm ) // 2021-07-19 00:28:52.946 "C" ? HB_TTOC( tDtm ) // 19.07.2021 00:28:52.946 "C" ? HB_TTOD( tDtm ) // 19.07.2021 "D" ? HB_TTON( tDtm ) // 2459415.02 "N" ? HB_TTOS( tDtm ) // 20210719002852946 "C" cDtm := HB_TSTOSTR( tDtm ) ? HB_STRTOTS( cDtm ) // 2021-07-19 00:28:52.946 "T" ? HB_CToT(cDtm) // 00:28:52.946 "T" cTm1 := cTm2 := "" ? tDtm := HB_DATETIME() // 2021-07-19 00:28:52.949 "T" ? tUtc := hb_TSToUTC( tDtm ) // 2021-07-18 21:28:52.949 "T" HB_TTOD( tDtm, @cTm1, "hh:mm:ss.fff" ) HB_TTOD( tUtc, @cTm2, "hh:mm:ss" ) ? cTm1 // 00:28:52.949 "C" ? cTm2 // 21:28:52 "C" [/pre2]

Haz: Andrey пишет: Очень мало документации по переменным времени. Можете сделать отдельный раздел у себя в справке ? Чтобы было в одном месте тогда тут дублировать весь хелп по харбуру придется. Зачем ? достаточно где есть https://github.com/Petewg/harbour-core/wiki/hb_T Более того в ChangeLog.txt по Harbour эти функции прекрасно документированы [pre2] * harbour/source/rtl/dateshb.c + added new .prg functions to mange date and timestamp values: HB_DATETIME() -> <tTimeStamp> HB_CTOD( <cDate> [, <cDateFormat> ] ) -> <dDate> HB_DTOC( <dDate> [, <cDateFormat> ] ) -> <cDate> HB_NTOT( <nValue> ) -> <tTimeStamp> HB_TTON( <tTimeStamp> ) -> <nValue> HB_TTOC( <tTimeStamp>, [ <cDateFormat> ] [, <cTimeFormat> ] ) -> <cTimeStamp> HB_CTOT( <cTimeStamp>, [ <cDateFormat> ] [, <cTimeFormat> ] ) -> <tTimeStamp> HB_TTOS( <tTimeStamp> ) -> <cYYYYMMDDHHMMSSFFF> HB_STOT( <cDateTime> ) -> <tTimeStamp> <cDateTime> should be in one of the above form: - "YYYYMMDDHHMMSSFFF" - "YYYYMMDDHHMMSSFF" - "YYYYMMDDHHMMSSF" - "YYYYMMDDHHMMSS" - "YYYYMMDDHHMM" - "YYYYMMDDHH" - "YYYYMMDD" - "HHMMSSFFF" - "HHMMSSF" - "HHMMSS" - "HHMM" - "HH" Important is number of digits. [/pre2]

Andrey: Haz пишет: Более того в ChangeLog.txt по Harbour эти функции прекрасно документированы Блин, а я искал по всему инету, вместо того чтобы первоисточник посмотреть... И поиск правильно нужно делать, я искал "HARBOUR hh:mm:ss"


Andrey: На сайте у вас есть: [pre2]3.13.6 Управление запуском процессов nResult := hb_ProcessRun( cCommand, [cStdIn], [@cStdOut], [@cStdErr], [lDetach] ) handle := hb_ProcessOpen( cCommand, [cStdIn], [@cStdOut], [@cStdErr], [lDetach] ) nResult := hb_ProcessValue( handle, [lWait] ) nResult := hb_ProcessClose( handle, lGentle ) [/pre2] А можно более подробнее дать параметры ? Я использую то что здесь на форуме давали: [pre2] hPID := hb_processOpen( cRun + " " + cParam,,,,.F., @hProcess) [/pre2] Может и неправильно... nResult := hb_ProcessClose( handle, lGentle ) - возвращает логическую переменную ! Не понял почему ?

alkresin: Описаний нигде нет, разобраться можно только по исходникам и примерам применения. handle := hb_ProcessOpen( cCommand, [cStdIn], [@cStdOut], [@cStdErr], [lDetach] ) Здесь cCommand - сама команда, cStdIn - то, что для этой команды вы бы ввели с клавиатуры, cStdOut и cStdErr - в эти переменные записывается вывод запускаемой программы, если он направлен в стандартные потоки stdout, stderr, lDetach - чтобы запустить процесс и не ждать его завершения, "отвязать" от своей программы, @hProcess - сюда записывается идентификатор процесса. hb_ProcessClose() возвращает результат - удалось ли нормально завершить процесс.

Andrey: alkresin пишет: hb_ProcessClose() возвращает результат - удалось ли нормально завершить процесс. Это я понял, просто наверное nResult нужно поменять на lResult ! А то получается, что ожидаешь число (N), и пишешь сразу в коде HB_NtoS(nResult) и получаешь пустоту....

alkresin: А... Ну да, надо будет исправить

Andrey: Ещё вопрос по вашей документации. Можно ли разместить отдельный пример работы с потоком с одной базой данных ? Не знаю как сделать у себя. Базу открываю монопольно, запускаются несколько потоков с РАЗНЫМИ RECNO(), нужно записать в базу по этим RECNO() что-то и потом завершить поток.

alkresin: Не получится. В документации написано, что workarea - локальные ресурсы потока, т.е., они не наследуются потоком. Для проверки написал вот это: Function Main LOCAL hThread IF hb_mtvm() ? "Start" ELSE ? "No threads support..." QUIT ENDIF select a use life exclusive hThread := hb_threadStart( @thFunc() ) hb_threadDetach( hThread ) Inkey(1) ? "End" return nil Function thFunc select a dbgoto( 3 ) ? field->EVENT return nil и оно не работает, потому что поток "не видит" открытую родителем базу. Да, вообще говоря, такой режим работы не очень эффективен. Если бы это работало, то надо было бы расставлять мьютексы: hb_mutexLock( mutex1 ) dbgoto( nRec ) field->MYFIELD := ... hb_mutexUnLock( mutex1 ) чтобы между dbgoto() и изменением записи другой поток не передвинул указатель записи в другое место. Так что вряд ли это получится быстрее, чем в одном потоке.

Andrey: Спасибо, понял ! А если открывать базу тогда в многопользовательском режиме ? Или вообще заново открывать базу в этом потоке, а потом закрывать. Будет тогда работать ? Мне не быстрота нужна, а независимость потока для записи в базу. Т.е. что то сделали в потоке и записали в базу. А в это время юзер может передвигаться по базе в основном потоке.

alkresin: Да, базу надо открывать в потоке в shared режиме, будет работать.

PSP: Andrey пишет: что то сделали в потоке и записали в базу. А в это время юзер может передвигаться по базе в основном потоке Нужно как-то отслеживать изменения. Иначе юзер может их и не увидеть.

Andrey: PSP пишет: Нужно как-то отслеживать изменения. Иначе юзер может их и не увидеть. Ну можно сделать Public nPubUpdateBase := 0 и из потока присваивать 1 при нужных условиях. А в главном потоке следить за этой переменной и делать Refresh базы если nPubUpdateBase == 1 Только как это реализовывать я не знаю ... Нужно как то блокировать переменную M->nPubUpdateBase ?

Haz: Andrey пишет: Ну можно сделать Public nPubUpdateBase := 0 и из потока присваивать 1 при нужных условиях. Поток используется чтобы не прерывать работу в основном процессе. Базы открываются в каждом потоке в режиме совместного доступа и логика точно такая же как и при работе с базой нескольких пользователей. Естественно в рамках работы в одной программе у нас есть вся информация о действиях "второго" пользователя и можно организовать хитрые рефреши если изменились записи из активного окна бровса, но на практике не заморачиваемся и все как при работе многопользовательской среде.. Если открывать базу в монопольном режиме, то eе нужно перемещать в zero space и забирать оттуда потоком при необходимости ( не забывая возвращать ) . Пока не забрали - алиас в потоке не увидим. У Александра все очень доступно написано о принципах работы с базой в многопоточке, никакие примеры не нужны. При монопольном доступе: Открыл базу и сразу в переместил в зеро. [pre2] #xcommand UNLOCK WORKAREA [] => hb_dbDetach( ) #xcommand LOCK WORKAREA => hb_dbRequest( , .T.,, .T. ) [/pre2] При необходимости забрал из зеро, поработал и вернул. Вот и вся хитрость. При многопользовательском: Все тоже как будто работает ещё другой пользователь. Можно в разных потоках открывать с одним и тем же алиасом, все равно он будет локальный и виден только в потоке ( как и все прочие установки RDD ) Ранее в xharbour потоком наследовались не только паблик переменные но и рабочие области, это приводило к тому, что приходилось вешать мютексы и запоминать установки RDD в рабочей области для их корректного восстановления. Позже и считаю правильным, наследование рабочей области убрали.

Andrey: В МиниГуи есть глобальные переменные: [pre2] App.Cargo := oHmgData() (App.Cargo):nTimerSec := 5 (App.Cargo):aClose := {} (App.Cargo):cFileDbf := GetStartUpFolder() + "\test.dbf" (App.Cargo):cAlias := "MyTest" [/pre2] И ещё можно делать на окно контейнер с нужными переменными: [pre2] DEFINE WINDOW wMain ; ..... This.Cargo := oHmgData() // for the window we create an object without variables (conditionally empty) This.Cargo:lRefresh := .F. nRecno := (cAls)->Lastrec() This.Cargo:oBrw := oBrw // положить объект TBROWSE на форму This.Cargo:aBrw := ARRAY(nRecno) AFILL( This.Cargo:aBrw, 0 )[/pre2] Если я запускаю поток: [pre2] hb_threadDetach( hb_threadStart( HB_THREAD_INHERIT_MEMVARS, @RunExternal(), aFile, oWnd ) ) [/pre2] То эти переменные доступны в потоке. Вопрос такой, если нужно записать в потоке допустим в [pre2] oWnd:Cargo:aBrw[25] := hPid или This.Cargo:lRefresh := .T. или AADD( (App.Cargo):aClose , {хендл окна, hPid, } )[/pre2] НУЖНО ли делать блокировку этих переменных ? Или лучше использовать обычные PUBLIC переменные ? Или делать через класс TThrData (доступ в потоках к переменным идет с блокировками) из МиниГуи ?

SergKis: Andrey пишет НУЖНО ли делать блокировку этих переменных ? Делай так (только используй h_objects.prg, в нем THmgData и TThrData max совмещены) [pre2] FUNCTION oThrData() RETURN TThrData():New() тогда App.Cargo := oThrData() (App.Cargo):nTimerSec := 5 (App.Cargo):aClose := {} (App.Cargo):cFileDbf := GetStartUpFolder() + "\test.dbf" (App.Cargo):cAlias := "MyTest" ... DEFINE WINDOW wMain ; ..... This.Cargo := oThrData() // for the window we create an object without variables (conditionally empty) This.Cargo:lRefresh := .F. nRecno := (cAls)->Lastrec() This.Cargo:oBrw := oBrw // положить объект TBROWSE на форму This.Cargo:aBrw := ARRAY(nRecno) AFILL( This.Cargo:aBrw, 0 ) [/pre2] тогда и к этим переменным App.Cargo, This.Cargo доступ будет с блокировками в потоках, не путать с oBrw := This.Cargo:oBrw, тут обычный доступ. Можно делать свою переменную для потоков PUBLIC oThread := oThrData() и работать в потоках через нее. Управлять окнами, тсб можно через сообщения окну (по уст. событиям)

alkresin: Обновил Harbour для начинающих - часть III - добавил раздел 3.13 Интернационализация (hbi18n)



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