Форум » Clipper » All In One » Ответить

All In One

SergeyKorotun: В связи с написанием программы по разным частям кода возникают трудности. Чтобы не задавать вопросы в разных темах и не забыть в какой именно, буду спрашивать в этой. Я возможно этим нарушу правила форума, вопросы будут затрагивать разнообразные функции, но пусть оправданием мне будет то, что все они относятся к одной программе. Вопрос №1 В get поле вводится какое то значение и если оно существует в справочнике, переходим в следующее get поле, если же не существует, то выдается предупреждение об этом. Сам оператор не имеет право добавлять это значение в справочник, но, убедившись в том, что он ввел без ошибок, имеет право оставить это значение в get поле и перейти в следующее. Чтобы случайно не проскочило ошибочно введенное значение, выход из get поля должен осуществляться по нестандартной клавише( т.е. K_ENTER, K_DOWN не подходят). Например подошло бы Ctrl+Enter Реализовать самому не удалось Вопрос № 2. В dbedit отобразить базу так, чтобы видеть максимальное число последних записей и чтобы курсор находился на последней записи use base new go bottom base->dbedit() вижу только одну последнюю запись use base new go bottom skip -17 base->dbedit() вижу все последние записи, но курсор находится на первой видимой use base new go bottom skip -17 skip 17 base->dbedit() вижу только одну последнюю запись Вопрос № 3. В dbedit даты отображаются в формате "дд.мм.гг", причем гг первые две цифры из гггг. Как отобразить в формате "дд.мм.гггг", не используя массив из picture для dbedit, чтобы не переделывать программу при изменении структуры базы. Вопрос № 4. в функции из valid изменяются значения других get переменных. Изменения становятся видимыми только тогда, когда курсор попадает в измененное соответствующее get поле, а хочется чтобы обновились сразу после выхода из поля, в котором они изменились. Обновляю так keyboard (replicate(chr(K_DOWN),17)+replicate(chr(K_UP),16)) Можно как то попроще? Вопрос № 5 При входе в get ... read в чужой программе на фоксе все поля пустые. В своей программе get переменные инициализирую командой space(n). Но после выхода из read и сохранения значений в полях базы с типом numeric выдает ошибку несоответствия типа данных. Обхожу так if !Empty() replace namebase->namefield with val(alltrim(namegetfield)) endif Как то попроще можно? Вопрос № 6 Есть строка из символов, которых не должно быть в веденном значении в get поле. Существует ли какая то функция, возвращающая true или false в случае наличия символов из строки 1 в строке 2? Или только посимвольно проверять?

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

petr707: use base new go bottom skip -17 keyboard CHR(K_CTRL_PGDN) // base->dbedit()

petr707: Поиск по форуму "Обновить GET" http://clipper.b.qip.ru/?1-4-0-00000383-000-0-0-1226573702

SergeyKorotun: petr707 пишет: Поиск по форуму "Обновить GET" http://clipper.b.qip.ru/?1-4-0-00000383-000-0-0-1226573702 Как то так @ 0,0 GET a VALID eval({|| c:=a+b,AEVAL( GetList, {|g| g:display()}),.t.}) вставил в прогу на клиппер 5.01, обновляет поля, хотя функции display() в NG for clipper 5.01 не нашел.


Dima: SergeyKorotun пишет: хотя функции display() в NG for clipper 5.01 не нашел. Смотри класс GET

SergeyKorotun: в стартовое сообщения добавил вопрос № 5

AlexMyr: SergeyKorotun пишет: Вопрос № 2. В dbedit отобразить базу так, чтобы видеть максимальное число последних записей и чтобы курсор находился на последней записи index ... descend dbedit()

AlexMyr: SergeyKorotun пишет: Вопрос № 3. В dbedit даты отображаются в формате "дд.мм.гг", причем гг первые две цифры из гггг. Как отобразить в формате "дд.мм.гггг", не используя массив из picture для dbedit, чтобы не переделывать программу при изменении структуры базы. set century on

SergeyKorotun: AlexMyr пишет: index ... descend dbedit() Возможно я неправильно употребил слово "последних". Я просто хотел чтобы окно dbedit было заполнено полностью записями, а какими (отсотированными в каком то порядке или действительно введенные в базу последними) именно не имеет значения. Подошел ответ petr707: use base new go bottom skip -17 keyboard CHR(K_CTRL_PGDN) // base->dbedit() AlexMyr пишет: set century on а вот это то что надо

PSP: SergeyKorotun пишет: В своей программе get переменные инициализирую командой space(n). Но после выхода из read и сохранения значений в полях базы с типом numeric выдает ошибку несоответствия типа данных. Обхожу так if !Empty() replace namebase->namefield with val(alltrim(namegetfield)) endif Как то попроще можно? А не надо переменной, значение которой в будущем будет записываться в числовое поле, присваивать строковое значение. Почему бы вместо Var := Space( n ) не использовать Var := 0. Get-система разберется сама, что ей подсовывают. Можно и PICTURE в GET указать для числового поля.

petr707: вместо Space(n) лучше x=BLANK(x,.t.) или x=MyBlank(x) Function MyBlank(x) Local ret,tx:=valtype(x) do case case tx="N" ret:=0 case tx="C" ret:=space(len(x)) case tx="D" ret:=ctod('') case tx="L" ret:=.f. otherwise ret:=NIL endcase return ret

PSP: petr707, а если значение x и так NIL? NIL и останется. В GET редактирования не будет.

petr707: В контексте задачи: x -значение поля записи БД , разве нет? varGet = blank(fieldget)

SergeyKorotun: PSP пишет: А не надо переменной, значение которой в будущем будет записываться в числовое поле, присваивать строковое значение. Почему бы вместо Var := Space( n ) не использовать Var := 0. Get-система разберется сама, что ей подсовывают. Можно и PICTURE в GET указать для числового поля. Не очень хорошо смотрится например номер телефона или почтовый индекс =0

SergeyKorotun: petr707 пишет: вместо Space(n) лучше x=BLANK(x,.t.) или x=MyBlank(x) Function MyBlank(x) Local ret,tx:=valtype(x) do case case tx="N" ret:=0 case tx="C" ret:=space(len(x)) case tx="D" ret:=ctod('') case tx="L" ret:=.f. otherwise ret:=NIL endcase return ret тип значений мне известен и ничто не мешает мне инициализировать get переменную значением нужного типа напрямую(a:=space(10); n:=0 ). Но... Смотри ответ на предыдущее сообщение

PSP: SergeyKorotun пишет: номер телефона или почтовый индекс =0 В базе эти поля числовые? Оригинально... :)

SergeyKorotun: petr707 пишет: В контексте задачи: x -значение поля записи БД , разве нет? varGet = blank(fieldget) а вот это запомню. Пригодится. Сейчас у поля телефон тип numeric, понятно что со временем сменят на character и в этом случае программу править не придется. А blank - это функция пользователя? В NG такой не нашел

petr707: blank() - из Clipper Tools не хотите цеплять Tools? берите MyBlank() до кучи - ниже - шаблон Picture для get Function get_picture(cfield) Local nf:=fieldnum(UPPER(cfield)),tip,ret:="",nsize,ndec if nf>0 tip :=fieldtype(nf) nsize:=fieldsize(nf) do case case tip="C" ret:=replicate("X",nsize) case tip="N" ndec:=fielddeci(nf) if ndec>0 ret:= replicate("9",nsize-ndec-1)+'.'+replicate("9",ndec) else ret:=replicate("9",nsize) endif endcase endif return ret

SergeyKorotun: PSP пишет: В базе эти поля числовые? Оригинально... :) А чем вам не нравится числовой тип поля для почтового индекса? У нас нет индексов начинающихся с нуля. А программе лет 20. Поле телефон только недавно расширили до 10 знаков, а тип еще не сменили, наверно много правок надо будет внести. Ну и я же пример привел не для того чтобы обсудить именно его, а объяснить почему get переменные желательно инициализировать именно space(n)

SergeyKorotun: PSP пишет: В базе эти поля числовые? Оригинально... :) А чем вам не нравится числовой тип поля для почтового индекса? У нас нет индексов начинающихся с нуля. А программе лет 20. Поле телефон только недавно расширили до 10 знаков, а тип еще не сменили, наверно много правок надо будет внести. Ну и я же пример привел не для того чтобы обсудить именно его, а объяснить почему get переменные желательно инициализировать именно space(n)

SergeyKorotun: flag=.f. setkey(K_F10, {|p, l, v| ExitOn(p, l, v, flag)}) // по F10 выход из get даже если значения нет в справочнике @1,1 say "Фамилия" get F valid vF(,,,flag) ... read function vF(p,l,v,flg) if v="F" if flg flag:=.f. return .t. endif if F есть в справочнике return .t. else return .f. endif function ExitOn(p,l,v,flg) if v="F" flag:=.t. keyboard chr(K_DOWN) //заставить выполнить функцию из valid endif На самый важный для меня вопрос №1 ответа пока нет. Может наличие хоть какого-то кода ускорит ответ. Правда код писал по памяти, флешку с кодом забыл на работе. Что не так?

petr707: //вопрос 1 - Enter - успешный ввод, если значение поля ввода- в справочнике, Tab - вся остальная подготовка // После Tab - еще раз Enter .... @1,1 say "Фамилия" get F valid vld_get(,,,"F",@F) @2,1 say "Имя " get Name valid vld_get(,,,"Name",@Name) ... read ... // united valid Function vld_get(a,b,c,cvar_get,val_get) Local ret:=.t. do while .t. if cvar_get=NIL cvar_get := GETFLDVAR() val_get := &cvar_get endif do case case cvar_get="F" // fam ret:=.f. if lastkey()=K_TAB // get default value, pre-value val_get := space(len(val_get)) // val_get := padr("Иванов",len(val_get)) // val_get := look_internet() ret:=.f. EXIT endif ret := In_Sprav_fio(val_get) if !ret // no valid // need add val_get-> sprav message(val_get +" not in sprav ") ret:=.f. exit endif case cvar_get="Name" // name //.... otherwise endcase exit enddo return ret

PSP: SergeyKorotun пишет: А чем вам не нравится числовой тип поля для почтового индекса? Нет проблем.

SergeyKorotun: flagExit=.f. setkey(K_F10, {|p, l, v| ExitOn(p, l, v, flagExit)}) @3,1 say " 2" get gF picture replicate("X",30) valid vgF("GF",flagExit) ... read function vgF(v,flgExit) if v=="GF" if flgExit flagExit=.f. return .t. endif endif select fam seek gF if found() return .t. else alert("Hand enter - F10") return .f. endif return .t. function ExitOn(p,l,v,flgExit) if v="GF" flagExit:=.t. keyboard chr(K_DOWN) endif return А вот это решение первого вопроса. Выход по F10. Хотел сделать по Ctrl+Enter. Не нашел инкей-кода

AlexMyr: SergeyKorotun пишет: Хоте сделать по Ctrl+Enter. Не нашел инкей-кода Ctrl-Return 10 K_CTRL_RET

Haz: SergeyKorotun, все ответы уже отвечены давно .... в мануале классов Get() и TBrowse[DB]() есть все и даже больше. Сергей, поймите правильно - многие долгожители конфы уже забыли когда использовали Dbedit() и @..GET.. READ. Clipper 5.01 - 5,x - Harbour - дают очень гибкий инструмент в виде классов и вот вопросы из этой тематики привлекли бы больше внимания.

SergeyKorotun: Haz пишет: flagExit=.f. setkey(K_F10, {|p, l, v| ExitOn(p, l, v, flagExit)}) @3,1 say " 2" get gF picture replicate("X",30) valid vgF("GF",flagExit) ... read function vgF(v,flgExit) if v=="GF" if flgExit flagExit=.f. return .t. endif endif select fam seek gF if found() return .t. else alert("Hand enter - F10") return .f. endif return .t. function ExitOn(p,l,v,flgExit) if v="GF" flagExit:=.t. keyboard chr(K_DOWN) endif return А вот это решение первого вопроса. К сожалению этот вариант подходит только для первой get переменной. Когда ввел значение, отсутствующее в справочнике, программа предлагает нажать F10 и переходит в следующее поле. Но если позже надо вернуться на предыдущее поле, то это оказывается невозможным. keyboard chr(K_DOWN) все равно переводит вниз. Назначать еще одну функциональную клавишу как то нехорошо, определять по lastkey() нажатую клавишу (стрелка вверх/ вниз или энтер) до F10 тоже будет давать сбои при ошибочном нажатии пользователем какой-то иной клавиши.

SergeyKorotun: Haz пишет: Clipper 5.01 - 5,x - Harbour - дают очень гибкий инструмент в виде классов и вот вопросы из этой тематики привлекли бы больше внимания Я не только получил ответы на все свои вопросы, но и узнал очень много нового о возможностях клиппера. Мне давали ответы не только на форуме, а и присылали письма на почтовый ящик. Большое всем спасибо. Но программа хотя и в стадии завершения, но еще один большой справочник не подключен (улицы), так что тема не закрывается. По фамилиям, именам и отчествам справочники не подключал, так как не очень то они нужны. Нет смысла не добирать несколько букв в фамилии, чтобы потом вызвать справочник, в котором будет сотня продолжений. После ввода фамилии проверил наличие ее в справочнике и если ее нет, выдал сообщение пользователю. А вот справочник улиц подключить надо. Иногда в паспортном столе записывают улицу с неверным окончанием и бывает необходимо выбрать самую подходящую. Попробую создавать временную базу, в которую попадут только улицы, удовлетворяющие условию фильтра и использовать ее в качестве справочника (еще раз повторюсь: переход на харбор будет производится только после написания программы на клиппер 5.01). Вопрос: после использования этой временной базы перед ее закрытием можно как-то запретить запись базы на диск?

AlexMyr: SergeyKorotun пишет: Попробую создавать временную базу, в которую попадут только улицы, удовлетворяющие условию фильтра и использовать ее в качестве справочника (еще раз повторюсь: переход на харбор будет производится только после написания программы на клиппер 5.01). Вопрос: после использования этой временной базы перед ее закрытием можно как-то запретить запись базы на диск? А еще в харбор можно создавать базу в памяти (библиотека hbmemio): REQUEST HB_MEMIO PROC Main() LOCAL nI FIELD F1 DBCREATE("mem:test", {{"F1", "N", 9, 0}},, .T., "memarea") FOR nI := 1 TO 1000 DBAPPEND(); F1 := HB_RANDOM() * 1000000 NEXT INDEX ON F1 TAG f1 DBEVAL({|| QOUT(F1)}) DBCLOSEAREA() DBDROP("mem:test") // Free memory resource RETURN SergeyKorotun пишет: Я не только получил ответы на все свои вопросы, но и узнал очень много нового о возможностях клиппера. С такими знаниями о клиппере с русской докой, лучше уже начинать изучать харбор, плюс к харбору несколько десятков английских слов выучите и уже не будет так страшно копаться в английской доке .

Haz: SergeyKorotun пишет: Вопрос: после использования этой временной базы перед ее закрытием можно как-то запретить запись базы на диск? С трудом вериться что вообще такое возможно, учитывая размер базы и используемую память DOS приложения. Попробую создавать временную базу, в которую попадут только улицы, удовлетворяющие условию фильтра и использовать ее в качестве справочника Попробуйте перекомпилировать свою программу в Clipper 5.3, скорее всего никаких доработок не потребуется вообще, а освоить SCOPE вам тут помогут

SergejKis: SergeyKorotun пишет: Но если позже надо вернуться на предыдущее поле, то это оказывается невозможным. keyboard chr(K_DOWN) все равно переводит вниз. В С5.2 есть READEXIT(.T.). Добавляет K_UP (5), K_DOWN (24) к переходу по GET полям cMyvar = SPACE(10) lLastExit = READEXIT(.T.) // Result: Turn on exit keys @ 10, 10 SAY "Enter: " GET cMyvar READ READEXIT(lLastExit) // Result: Restore previous setting Если используются функции VALID, то в начало надо вставлять: IF LastKey() == K_UP; RETURN .T. ENDIF

SergeyKorotun: SergejKis пишет: Если используются функции VALID, то в начало надо вставлять: IF LastKey() == K_UP; RETURN .T. ENDIF Выход из get поля должен происходить по клавише, определенной в SET KEY, например по F10. При нажатии F10 вызывается функция, имитирующая нажатие на клавиатуре стрелки вниз, которая автоматически вызывает функцию из валид. Можно было бы все время какой то переменной присваивать код нажатой клавиши и в функции, вызываемой по F10 инициировать или нажатие стрелки вниз или стрелки вверх. Но где гарантия что пользователь случайно не нажмет какую то другую клавишу и нажатие какой клавиши инициировать - неизвестно.

SergeyKorotun: SergejKis пишет: В С5.2 есть READEXIT(.T.). Добавляет K_UP (5), K_DOWN (24) к переходу по GET полям в клиппер 5.01 по умолчанию переход по полям осуществляется в том числе и перечисленными вами клавишами

petr707: гарантия - проверка всех значений формы после READ ( после всех get) перед записью в БД, вывод сообщения оператору при ошибках ввода и возврат к началу формы - первому GET

SergeyKorotun: Два часа не могу загнать записи из базы send в базу reciver. Структуры баз совпадают. Путя к базам верные. Пытался и так написать: "dbf\\send.dbf" Где ошибка? pth:=alltrim(nastr->path_dbf)+"reciver.dbf" use (pth) alias reciver new select reсiver append from ("dbf\send.dbf") ALL // for ((d_zvirka>=dateBegin) .and. (d_zvirka<=dateEnd)) commit select reciver use

PSP: 1. Что получаете? Ошибку или что? 2. SergeyKorotun пишет: append from ("dbf\send.dbf") ALL ALL, по-моему, не нужно.

SergeyKorotun: PSP пишет: 1. Что получаете? Ошибку или что? Ошибок нет. Ни одной записи не переносится с базы send в базу reciver Без ALL тоже не переносятся. ALL не мешает, оно по умолчанию подразумевается

Pasha: SergeyKorotun пишет: Ошибок нет. Ни одной записи не переносится с базы send в базу reciver Проверьте, чтобы файл reciver ранее не был открыт монопольно. Если это так, то __dbApp (aka append from) не сможет его открыть

petr707: pth:=alltrim(nastr->path_dbf)+"reciver.dbf" cfile_app := ("dbf\send.dbf") do while .t. if !file(pth) // mess( " source file not found") exit endif if !file(cfile_app) // mess( " append file not found") exit endif use (pth) alias reciver new if select("reciver")=0 //mess("error use reciver") exit endif select reсiver append from (cfile_from) ALL // for ((d_zvirka>=dateBegin) .and. (d_zvirka<=dateEnd)) commit select reciver close reciver //use //mess("ok") exit enddo

SergeyKorotun: Pasha пишет: Проверьте, чтобы файл reciver ранее не был открыт монопольно. Если это так, то __dbApp (aka append from) не сможет его открыть На протяжении дня в базу send набиваются записи и в конце рабочего дня переносятся в базу reciver из другой программы. Но на время написания программы копия база reciver помещена в отдельную папку, pth настроен именно к ней, и никем занята быть не может. В приведенном выше фрагменте программы она открывается впервые. for petr707 Проверки на наличие баз я в приведенный фрагмент не включил, но они там есть. Кроме того обе базы я перед append from выводил с помощью dbedit на экран.

Pasha: Я был невнимателен. Поскольку записи добавляются из send, и именно send открывается командой append from, проверьте, чтобы до выполнения append from send не была открыта монопольно

Haz: Попробуйте открыть в эксклюзиве use (pth) alias reciver excl new

petr707: 1.Если при набивке в SEND не делать COMMIT, другая программа, возможно, их не увидит - до завершения работы первой. 2.Если в одной программе не освободить SEND, другая программа не сможет сделать APPEND 3.Для контроля занятости БД(видимости записей) таблицу SEND можно открывать сторонней утилитой DbfNavigator.exe (freeware) 4.Для контроля - лучше посчитать записи командой COUNT TO n в каждой таблице до и после Append ( вывести в лог) 5.Не очень хорошо - используются не абсолютные пути (disk\path\file) к файлам таблиц, при нечетком SET DEFAULT PATH можно случайно взять другой экземпляр SEND

SergeyKorotun: Pasha пишет: Я был невнимателен. Поскольку записи добавляются из send, и именно send открывается командой append from, проверьте, чтобы до выполнения append from send не была открыта монопольно send была открыта именно этой программой, отображалась dbedit с функцией, и в зависимости от нажатой клавиши производились какие-то действия, и в частности экспорт записей в базу reciver. Закрыв базу send перед append from, экспорт заработал. После append from сразу же открыл send и хотя dbedit этого не заметил, все равно как то не хорошо получается.

SergeyKorotun: В стартовый топик добавил вопрос № 6

petr707: Можно по другому - используйте флаг для временного выхода из Dbedit затем автоматически повторный вход. все действия по - копированию БД, выделению записей - вне тела польз. функции Dbedit Userf ... private lrepeat:=.t. private lcopy :=.f. do while lrepeat lrepeat :=.f. if lcopy lcopy:=.f. copy file(dbf) to (dbf2) endif use (dbf) alias tmp new dbedit(,,,'userf',,) close tmp if !lrepeat; exit; endif enddo ... return Function userf() ... case lastkey()=K_ESC lrepeat := .f. // out return 0 case lastkey()=K_F10 // need close-open lrepeat:=.t. lcopy :=.t. return 0 // ..

SergeyKorotun: petr707 пишет: copy file(dbf) to (dbf2) Эта строка натолкнула на другой алгоритм: командой copy to из базы send создать временную базу и к ней применить append from

SergeyKorotun: При входе в get read по умолчанию стоит режим замены. Какой командой установить режим вставки?

PSP: SergeyKorotun пишет: При входе в get read по умолчанию стоит режим замены. Какой командой установить режим вставки? http://www.ousob.com/ng/53guide/ng91ce6.php

SergeyKorotun: Программой создается база и из программы в одно из полей заносятся "запрещенные" символы. В валид функции будет проверяться, не встречается ли среди введенных символов запрещенные. Вопрос: как присвоить полю строку из запрещенных символов, в которой есть и ограничители строк (кавычки, апостроф)? Примеры запрещенных символов для некоторых get полей: `=!@#$%^&*()_+\|]}[{"?>,<' `=!@#$%^&*_+\|]}[{?><ЪЫЭ' `=!@#$%^&*_+\|]}[{?><QWERTYUIOPASDFGHJKLZXCVBNM'

PSP: SergeyKorotun пишет: как присвоить полю строку из запрещенных символов, в которой есть и ограничители строк (кавычки, апостроф)? BASE->FieldName := Chr( 34 ) // кавычки BASE->FieldName := Chr( 39 ) // апостроф

SergeyKorotun: PSP пишет: BASE->FieldName := Chr( 34 ) // кавычки BASE->FieldName := Chr( 39 ) // апостроф Чтобы занести в базу строку `=!@#$%^&*()_+\|]}[{"?>,<' надо написать команду: replace base->fieldName with "`=!@#$%^&*()_+\|]}[{"+chr(34)+"?>,<"+chr(39) в других языках есть специальный символ, обозначающий, что следующий за ним символ не часть синтаксиса языка, а просто символ. В клиппере такого нет?

Pasha: SergeyKorotun пишет: в других языках есть специальный символ, обозначающий, что следующий за ним символ не часть синтаксиса языка, а просто символ. В клиппере такого нет? В клиппере такого нет. Зато в клиппере символьная строка-константа может ограничиваться символами "" '' [] Если внутри строки есть какой-либо символ-ограничитель, то в качестве ограничителя для строки можно использовать альтернативный. Скажем, я привык использовать паскалевский ограничитель: '' Но в украинском языке апостроф присутствует в алфавите, и в тех случаях, когда надо использовать такой литерал, я использую ограничитель ""

SergeyKorotun: Pasha пишет: Если внутри строки есть какой-либо символ-ограничитель, то в качестве ограничителя для строки можно использовать альтернативный. У меня все три типа ограничителей присутствуют в строке, поэтому придется делать через chr() Строки с запрещенными символами хранятся у меня в текстовом файле. Через буфер обмена я вставил их в prg файл и командой replace занес в базу. Некоторые символы в базе отображены неверно, например вместо какого-то символа вижу желтый квадрат. Хотя в prg файле все символы соответствуют тем, что вставил из буфера.

Haz: можно держать не в базе, а в том же текстовике, или INI файле. Проблем 0, рисуйте в INI все что удасться вбить с клавы. библиотек и примеров работы с INI на том же http://www.the-oasis.net/ уверен до дури.

SergeyKorotun: можно ли в get ... read перемещать фокус в указанное в валид функции get поле, зависящее от значения, введенного в текущее поле. Запрещать у when функции нельзя, так как пользователь имеет право например стрелкой вверх переместиться в пропущенное поле и изменить его. Использую для этого keyboard replicate(chr(K_DOWN),n). Может для этого есть какая то специальная команда

Haz: Разумеется есть ::aGetList[ ::nPos ]:setFocus() класс oGet

SergeyKorotun: use reciver alias reciver new append from ("dbf\send.dbf") Требуется со своей базы send добавить записи в чужую базу reciver. Последняя база занята только в некоторых пунктах меню чужой программы. Чужая программа не сетевая, на фокспро. Как перед добавлением записей проверить занята чужая база(reciver) или нет, чтобы не заставлять пользователя полностью выходить из программы, а если он все же не вышел, не получать ошибки времени выполнения BASE/1002 АЛИАС ОТСУТСТВУЕТ

SADSTAR2: FLOCK() returns true (.T.) if an attempt to lock a database file in USE in the current work area succeeds; otherwise, it returns false (.F.). И не забыть UNLOCK

petr707: .... f_name := "reciver.dbf" do while .t. if !file_exc(f_name) exit endif use (f_name) alias reciver new if select("reciver")=0 exit endif append from (...) .... close reciver exit enddo ... #include "fileio.ch Function file_exc(f_name,nerr_ex) Local handle:=0,ret:=.f. handle=fopen(f_name,FO_EXCLUSIVE) if ( nerr_ex:=FERROR())#0.or.handle=-1 ret:=.f. else fclose(handle) ret:=.t. endif return ret

Григорьев Владимир: petr707 пишет: #include "fileio.ch Function file_exc(f_name,nerr_ex) Local handle:=0,ret:=.f. handle=fopen(f_name,FO_EXCLUSIVE) if ( nerr_ex:=FERROR())#0.or.handle=-1 ret:=.f. else fclose(handle) ret:=.t. endif return ret У меня такой вопрос к автору этого кода: "Может ли так быть, что FERROR() равна 0, но тем не менее значение, возвращаемое FOPEN равно -1?! И второй вопрос: "Вы не предполагаете, что сам файл базы данных может открыться, но тем не менее его индексные файлы не будут иметь доступа?!" Эти вопросы у меня возникли в связи с тем, что на мой взгляд проще сразу же использовать команду USE, которая и возвратит все ошибки об открытии! Зачем несколько раз делать одно и тоже: сначала в вашей функции file_exc, а затем в команде USE?

Григорьев Владимир: Кстати сказать, хотел бы добавить, что есть различия в способе открытия файлов с помощью FOPEN и USE. USE использует установки путей для поиска файлов, в то время как FOPEN открывает файл без использования установленных путей доступа к файлам баз данных по умолчанию. Так что на мой взгляд вышеприведенная программа вообще некорректная! Может так оказаться, что с помощью вашей функции file_exc и с помощью команды USE будут проверяться два разных файла!:)

petr707: Да, конечно,различия есть, лучше уточнять имена файлов абсолютными путями, или в начале кода провести установки рабочих каталогов (независимо от кого как пользователь сможет запустить приложение): exe_dir = base_dir = current_dos_dir (set path ..., set default .., dirchange(...) ) А какая альтернатива файловым операциям? Отработка исключений BEGIN sequence break END ?

SergeyKorotun: Путь к базам в программе указан полностью. Пример я просто упростил. Обе программы использует один и тот же пользователь. Но программы могут быть установлены на разных компьютерах, соединенных в сеть. На данном этапе программа выдает сообщение, чтобы вышли с чужой программы. Для пользователя это не проблема. Если он не выйдет, тоже ничего страного не произойдет. Программа выдаст ошибку времени выполнения. Никаких потерь записей не произойдет. Просто хотелось бы в случае незанятости базы пропускать это сообщение, т.к. база открыта только в нескольких пунктах меню и не всегда прийдется выходить с программы. Индексные файлы базы, в которую добавляются записи, открывать не надо, так как они фокспрошные. После добавления будет переиндексация базы средствами чужой программы.

Haz: SergeyKorotun пишет: Просто хотелось бы в случае незанятости базы пропускать это сообщение Хочется пропускать - пропускайте, есть же стандартный обработчик ситуации ( хотя может в 5.0 все както иначе ??? ) bSave := ERRORBLOCK( {|x| BREAK(x)} ) BEGIN SEQUENCE use reciver alias reciver new RECOVER ..... END SEQUENCE ERRORBLOCK(bSave)

SergeyKorotun: в последнем get поле добавил when функцию, которая проверяет значения в предыдущих полях и если что-то не так, пользователю alert-ом выдается предупреждение и функция возвращает .f. Так вот после нажатия энтера или пробела на сообщении завершается get...read, а мне еще надо ошибки поисправлять и в последнее поле значение ввести. Перенести функцию у valid предпоследнего get не предлагать (возникнут проблемы с выходом в вышележащие поля для исправления ошибок да и функция там уже есть и к тому же объемная)

petr707: Пусть исходный код имеет вид: .. get befLastVar valid vld_befLastVar() ...get LastVar when whe_LastVar() valid vld_LastVar() ---- READ завершается, потому что нет ввода в последнее поле ( из-за whe_LastVar()=.f.) Чтобы открывалось для ввода последнее поле: 1 вариант изменения - перестановка вызовов функций в другой Get: .. get befLastVar valid vld_befLastVar().and.whe_Lastvar() ...get LastVar() valid vld_LastVar() 2 вариант - When LastVar - всегда возвращать .t., а проверку - в Valid последнего поля .. get befLastVar valid vld_befLastVar() ...get LastVar when eval({|| whe_LastVar(),.t.}) valid whe_LastVar().and.vld_LastVar()

PSP: Можно так: LOCAL GetList := {} @ 1, 1 GET cVar @ 2, 1 GET nVar @ 3, 1 GET dVar nGet := 1 WHILE .T. ReadModal( GetList, nGet ) IF LastKey() <> K_ESC // здесь проверяем корректность ввода данных IF Empty( cVar ) nGet := 1 // вернуться на 1-й GET LOOP ELSEIF nVar == 0 nGet := 2 // вернуться на 2-й GET LOOP ELSEIF Empty( dVar ) nGet := 3 // вернуться на 3-й GET LOOP END // IF // если все поля заполнены корректно, делаем сохранение BASE->cVar := cVar BASE->nVar := nVar BASE->dVar := dVar END // IF EXIT END // WHILE

SergeyKorotun: petr707 пишет: Пусть исходный код имеет вид: .. get befLastVar valid vld_befLastVar() ...get LastVar when whe_LastVar() valid vld_LastVar() ---- READ завершается, потому что нет ввода в последнее поле ( из-за whe_LastVar()=.f.) Чтобы открывалось для ввода последнее поле: 1 вариант изменения - перестановка вызовов функций в другой Get: .. get befLastVar valid vld_befLastVar().and.whe_Lastvar() ...get LastVar() valid vld_LastVar() 2 вариант - When LastVar - всегда возвращать .t., а проверку - в Valid последнего поля .. get befLastVar valid vld_befLastVar() ...get LastVar when eval({|| whe_LastVar(),.t.}) valid whe_LastVar().and.vld_LastVar() 1. вариант. Если vld_befLastVar().and.whe_Lastvar() обнаружит ошибку в предпоследнем поле, порожденную неверным значением в каком то предыдущем поле(назовем его varx), то чтобы перейти в varx, придется в предпоследнем поле ввести значение, соответствующее varx, иначе не выпустит из vld_befLastVar, потом перейти в varx, ввести значение, и после этого придется снова править vld_befLastVar 2. вариант. может возникнуть та же ситуация с ненужной правкой последнего поля. when просто бы не пустило в последнее поле, а выпускать в предыдущие поле (в отличие от валид) будет.

SergeyKorotun: PSP пишет: Можно так: LOCAL GetList := {} @ 1, 1 GET cVar @ 2, 1 GET nVar @ 3, 1 GET dVar nGet := 1 WHILE .T. ReadModal( GetList, nGet ) IF LastKey() <> K_ESC // здесь проверяем корректность ввода данных IF Empty( cVar ) nGet := 1 // вернуться на 1-й GET LOOP ELSEIF nVar == 0 nGet := 2 // вернуться на 2-й GET LOOP ELSEIF Empty( dVar ) nGet := 3 // вернуться на 3-й GET LOOP END // IF // если все поля заполнены корректно, делаем сохранение BASE->cVar := cVar BASE->nVar := nVar BASE->dVar := dVar END // IF EXIT END // WHILE такой конструкцией get не пользовался, но если здесь можно переносить фокус ввода в какое то предыдущее поле, то это то-что надо. А при переносе фокуса например из пятого поля в третье в четвертом поле будут выполняться when и valid функции? Но к сожалению в этой программе не воспользуюсь. Слишком много переделывать придется. Но идеей с переносом фокуса в одно из вышележащих полей с ошибками воспользуюсь, если получится. В when функции последнего поля сделаю проверку и если есть ошибка, все равно верну .t. и перенесу фокус в вышележащее поле, используя подсказку Haz: Разумеется есть ::aGetList[ ::nPos ]:setFocus() класс oGet

SergeyKorotun: При выполнении валид функции и возвращении .f. курсор устанавливается в начало поля. Можно как то определить в какой позиции он находился, чтобы после возврата из валид установить его в ту позицию, где он был.

petr707: Как-то сложновато выстраиватся у Вас система переходов по полям ввода и запретов перехода - можно даже сделать ловушку, из которой не выбраться.. Поймет ли User - в чем был неправ и как ему пройти лабиринт формы до конца? Предложение: 1) после завершения всех GET проверять все поля формы и либо разрешать запись данных либо сообщать, какие значения полей некорректны. 2) Для того чтобы не выбирать - в какой GET перевыставить ввод - нужно дать возможность User'ам клавишей K_UP (стрелка вверх) проходить к любому предыдущему полю любой Valid без проверки значений, поскольку видимо у Вас есть зависимость:следущее поле от предыдущих. 3) Не всегда запрещать выход из поля по некорректному значению , а только давать замечание ввода и разрешать ввод следующего поля, все равно User не сможет записать форму в конечной проверке всех полей при наличии каких-либо некорректных значений.

SergeyKorotun: petr707 пишет: можно даже сделать ловушку, из которой не выбраться.. Их и делать то не надо, они в реале получаются. Поэтому на данный момент разрешаю покидать любое пустое поле. Если же поле не пустое, выполняются все проверки в валид функции и в случае наличия ошибок запрещается выход из поля. Перед входом в последнее поле (проверкой у when последнего поля) выдаю предупреждение о незаполненных обязательных полях и в случае их наличия хотел запретить вход в последнее поле. Пока ограничился только предупреждениям без запрета входа, иначе завершается get...read

SergeyKorotun: Можно ли средствами клиппера считать ключ индексного файла cdx(создан foxpro) и пересоздать этот индекс? Или хотя бы зная ключ, создать cdx индекс? Как распечатать доку из NG?

petr707: 1) Индексы CDX создавать можно, см. поиск в конфе - SIX 2) Средство для просмотра , печати и тд. NG файлов - Expert Guide for Windows http://www.davep.org/norton-guides/

PSP: CDX встроен в RDD Клиппер, начиная с 5.2e (если не ошибаюсь), а также Harbour.

PSP: SergeyKorotun пишет: А при переносе фокуса например из пятого поля в третье в четвертом поле будут выполняться when и valid функции? В такой реализации редактирование Get-объекта полностью завершается, потом проверяется корректность данных и, если найдено поле с некоректными данными, то редактирование возобновляется, начиная с этого поля. Перед LOOP можно выдать какое-нибудь сообщение на экран. В данном случае проверка происходит вне Get-объекта. WHEN и VALID функции здесь не участвуют.

SergeyKorotun: К файлу prg1 оператором do подсоединяются файлы prg1_1 prg1_2 К файлу prg1_1 оператором do подсоединяются файл prg1_1_1 Будут ли видимыми в prg1_2 функции из prg1_1_1? Тот же самый вопрос более наглядно: file prg1.prg ... do prg1_1 ... do prg1_2 ... file prg1_1.prg ... do prg1_1_1 // здесь есть функция function f1_1_1() ... file prg1_2.prg ... f1_1_1() // будет ли доступна здесь эта функция ...

PSP: Функция видна во всей программе, если она не объявлена STATIC.

SergeyKorotun: Haz пишет: Попробуйте перекомпилировать свою программу в Clipper 5.3, скорее всего никаких доработок не потребуется вообще, а освоить SCOPE вам тут помогут Из NG по SIx Driver RDD: Индексный Драйвер фирмы SuccessWare (SIx Драйвер) - это замещаемый драйвер баз данных (RDD) для Clipper'а 5.0x. В качестве RDD он присоединяется к низкоуровневой подсистеме СУБД, имеющейся в архитектуре Clipper'а 5.0. Это означает, что Вы можете буквально заменить умалчиваемый RDD (DBFNTX) на SIx Драйвер и использовать индексы FoxPro без перекомпиляции или переработки исходного кода. Откуда следует что и Scope и cdx индексы можно использовать и в клиппер 5.01? Или он глючит с клиппер 5.01?

Haz: SergeyKorotun пишет: Откуда следует что и Scope и cdx индексы можно использовать и в клиппер 5.01? Или он глючит с клиппер 5.01? можно, и тут уже предлагали SIX2 для 5.0 ... глюков серьезных не помню, хотя больше приходилось на сикс3 и клиппер 5.2 работать ( вот эта связка показывала очень высокую скорость ) SergeyKorotun пишет: без перекомпиляции а вот тут приврали - перекомпелить придется. PS все же советую попробовать harbour c cdx, 5.3 c cdx или 5.2е с Six3

SergeyKorotun: 1) После запуска программы на экран выводится сообщение "Расширенная ошибка 183". Но все работает. Что это за ошибка? 2) Как поочередно выводить сообщения то на экран, то в файл. При переключении с экрана на файл последний перезаписывается, а надо чтобы добавлялось в файл.

Dima: SergeyKorotun пишет: "Расширенная ошибка 183" Возможно Вы пытаетесь создать каталог который уже существует SergeyKorotun пишет: а надо чтобы добавлялось в файл. Про alternate речь ? Вероятно упустили ADDITIVE [pre2] Синтаксис SET ALTERNATE TO [<имя файла>] [ADDITIVE] SET ALTERNATE on | OFF | <лог. выражение> Аргументы TO <имя файла> открывает стандартный ASCII файл для вывода информации (расширение по умолчанию - (.txt)). Имя файла может содержать расширение, обозначение дисковода и (или) путь доступа. Значение аргумента <имя файла> может быть определено либо как литеральное имя, либо как символьное выражение, заключенное в кавычки. Следует отметить, что если файл с таким именем уже существует, то он перезаписывается. ADDITIVE определяет, что информация, выводимая на экран, будет добавляться в файл, а не перезаписываться. Если предложение отсутствует, то заданный файл обрезается до нулевой длины перед тем, как в него будет записываться новая информация. ON вызывает дублирование выдачи информации на экран записью ее в открытый текстовый файл. OFF прерывает дублирование выдаваемой на экран информации в текстовый файл, не закрывая его. <лог. выражение> - логическое выражение, заключенное в скобки. Его значение "истина" (.T.) аналогично условию ON, значение "ложь"(.F.) - условию OFF. [/pre2]

SergeyKorotun: Dima пишет: Возможно Вы пытаетесь создать каталог который уже существует угадали Dima пишет: Про alternate речь ? Вероятно упустили ADDITIVE пробовал через set device to printer set printer on set console off set printer to txt\report.txt после вашей подсказки использовал alternate. все получилось, но хотелось бы узнать как реализовать через set device, set printer

SergeyKorotun: база имеет индекс с ключом t+str(f,7)+str(i,7)+str(o,7) t c 10 (всегда 10-ти значное значение) f n 7 i n 7 o n 7 Ищу в базе запись "9999999999"+str(ff,7)+str(ii,7)+str(oo,7), ff, ii, oo - переменніе с кодами FIO Если нахожу, устаналиваю фильтр чтобы отобразилось только это: "9999999999"+str(ff,7)+str(ii,7)+str(oo,7) и открываю базу browse() отображается одна или несколько записей, удовлетворяющих условию фильтра. изменяю 9999999999 например на 1234567890 запись исчезает, так как уже не удовлетворяет условию фильтра, а программа зависает. Почему? И не важно одна запись была изначально отображена или несколько.

petr707: set printer to ("a.txt") set device to print npos:=prow() SETPRC(npos,0) ... @npos++,1 say " to printer= 111111" ... set device to screen @5,5 say "to screen" ... set device to print ... @npos++,1 say " to printer= 22222" set printer to ...

petr707: 1)при изменении реквизита фильтра (а также при удалении записей) желательно применять что-нить типа SKIP 0 Browse() , Dbedit() - виснут, если удалить текущую запись обзора и не сместить указатель ( в режиме set deleted on) 2) возможно, программа не зависла, а просто browse()долго перестраивает фильтр

SergeyKorotun: petr707 пишет: 2) возможно, программа не зависла, а просто browse()долго перестраивает фильтр там записей мало, ждал с пол-часа petr707 пишет: 1)при изменении реквизита фильтра (а также при удалении записей) желательно применять что-нить типа SKIP 0 Browse() , Dbedit() - виснут, если удалить текущую запись обзора и не сместить указатель ( в режиме set deleted on) а как у browse() выполнить skip?

petr707: Можно попробовать разные варианты, зависит от условий задачи 1) значение(функцию) фильтра содержать(заполнять) в отдельном служебном поле поле таблицы, фильтр ставить на служебное поле replace all indfileld with t+str(f,7)+str(i,7)+str(o,7) .. set filter to is_flt2(tt,ff,ii,oo) тогда изменение любого реквизита(поля БД) не приведет к изменению обзора и перестроению индекса 2) добавить фильтр в индексное выражение, индекс пересоздавать каждый раз для новых параметров tt,ff,ii,oo index on iif( is_flt(tt,ff,ii,oo), str(recno(),7),space(7)) to (...) UNIQUE set filter to is_flt(tt,ff,ii,oo) go top browse(..) ... Function is_flt(tt,ff,ii,oo) Local ret :=.f. ret:= (t=tt).and.(f=ff).and.(i=ii).and.(o=oo) return ret Function is_flt2(tt,ff,ii,oo) Local ret :=.f. ret:= (indfileld == tt+str(ff,7)+str(ii,7)+str(oo,7)) return ret

SergeyKorotun: petr707 пишет: ...зависит от условий задачи... подзадача такая: с базы YYY значение поля t(код клиента) (в примере переменная tt) ищу в базе GGG и если нахожу, удаляю запись в YYY, если же не нахожу, то ищу в базе GGG, но уже не tt, а по другому ключу "9999999999"+str(ff,7)+str(ii,7)+str(oo,7), где 9999999999 - это по каким то причинам неприсвоенное когда-то настоящее значение t (код клиента), ff. ii, oo - сответсвенно коды фамилии, имени та отчества с базы YYY (поля f. i. o) и если нахожу (по seek). то устанавливаю фильтр на базу GGG (t="9999999999" .and. yyy->f=ff .and. yyy->i=ii .and. yyy->o=oo) и browse() отображаю найденные записи. Их может быть несколько. Далее визуально просматривая все поля, определяю, есть ли такая запись, которая соответствует значению поля t из базы YYY и если есть, то заменяю 9999999999 на t. petr707 пишет: 2) добавить фильтр в индексное выражение, индекс пересоздавать каждый раз для новых параметров tt,ff,ii,oo tt я то знаю наперед, но сначала отобразятся записи базы командой browse() те, у которых t="9999999999", и только после этого возможно в одной из записей 9999999999 изменятся на t. Так что пересоздать индекс до открытия базы функцией browse() не получится. Первый вариант тоже не подходит, структуру базы изменять нельзя

Pasha: SergeyKorotun пишет: Ищу в базе запись "9999999999"+str(ff,7)+str(ii,7)+str(oo,7), ff, ii, oo - переменніе с кодами FIO Если нахожу, устаналиваю фильтр чтобы отобразилось только это: "9999999999"+str(ff,7)+str(ii,7)+str(oo,7) и открываю базу browse() отображается одна или несколько записей, удовлетворяющих условию фильтра. изменяю 9999999999 например на 1234567890 запись исчезает, так как уже не удовлетворяет условию фильтра, а программа зависает. Почему? И не важно одна запись была изначально отображена или несколько. Вы scope так и не используете ? Если бы использовали, то никакой задержки (которая выглядит как зависание) не возникло бы. Но даже при этом вопрос решается элементарно просто. Изменение ключевого значения записи и перестройка фильтра: nr1 := recno() skip 1 // переходим на запись (причем быстро), которая должна стать текущей после изменения nr2 := recno() dbgoto(nr1) // возвращаемся field->key := newval // изменяем dbgoto(nr2) // переходим на новую текущую запись

petr707: 1) для второй ветки процедуры ( поиск по 99999) для работы с отфильтрованными записями можно ставить фильтр не на основную таблицу GGG, а на рабочую производную таблицу TTT: только записи фильтра с полем ссылки(номер записи таблицы GGG).По ссылке можно править таблицу GGG. То есть смотрим на одну таблицу(с близкой структурой), правим другую. select ggg arr:=dbstruct() aadd(arr,{"recno","N",7,0}) dbcreate("ttt.dbf",arr) use (''ttt.dbf") alias ttt new exclusive select ggg .. if ggg->(dbseek(ckey,.f.)) do ggg->(!eof().and.ckey=...) ttt->(dbappend()) for i=1 to len(arr)-1 ttt->(fielput(i,ggg->(fieldget(i)))) next i ttt->recno:=ggg->(recno()) ggg->(dbskip()) enddo endif ... 2) Возможно, проще изменить основной алгоритм? Проход по базе YYY c двумя вариантами обработки для каждой записи YYY - меняем на Два прохода по YYY - первый проход -обработка1(без установки фильтров и визуальной правки) , при втором проходе - обработка2

SergeyKorotun: для данной задачи перед использованием browse() отключил индекс, а после включил. После правки ключевых полей зависаний нет. Записей только несколько тысяч, отсутствие индексов тормозов не вызывает. А для больших индексированных баз использовать browse() наверно не получиться. В этой программе вместо browse() лучше было бы использовать dbedit (можно было бы по нажатию гарячей клавиши изменить значения полей в визуально выбранной записи), но программа для одноразового использование (навести порядок в базах, которые используются где то на протяжении 15 лет) и не хотелось лишний код писать.

SergeyKorotun: Pasha пишет: Вы scope так и не используете ? Если бы использовали, то никакой задержки (которая выглядит как зависание) не возникло бы. Пока даже не приступал к изучению. Не хватает времени. Я писал не про задержку, а про зависание программы при правке хотя бы одного ключевого поля в browse()

SergeyKorotun: Pasha пишет: Изменение ключевого значения записи и перестройка фильтра: nr1 := recno() skip 1 // переходим на запись (причем быстро), которая должна стать текущей после изменения nr2 := recno() dbgoto(nr1) // возвращаемся field->key := newval // изменяем dbgoto(nr2) // переходим на новую текущую запись И как все это выполнить из browse()

petr707: 1) browse - dbedit - различие невелико, правка кода минимальна. Можно заменить в коде BROWSE() на BROWSE2() (см. ниже , код не проверял) Function browse2(w1,w2,w3,w4,harr,ctitle,arrf_) local oldshd,arr_str if w1=NIL w1:=1;w2:=0;w3:=maxrow();w4:=maxcol() endif private arrf:=arrf_ if arrf=NIL arrf:={} arr_str:=DBSTRUCT() aeval(arr_str,{|x| aadd(arrf,x[1])}) endif oldshd=wsetshadow(-1) wopen(w1,w2,w3,w4) wbox(1) if (ctitle=NIL) ctitle:="["+alias()+"]" endif private ctitlef:=ctitle private y1:= 0 private x1:= 0 private y2:=(w3-w1-2) private x2:=(w4-w2-2) @-1,1 say ctitle color c_pict dbedit(,,,,arrf,"f_browse2",,harr) wsetshadow(oldshd) wclose() return Function f_browse2(mode,col) Local arr_str:={},arrm:={} private cur_field := arrf[col] private var do case case mode<4 return 1 case lastkey()=K_ESC return 0 case lastkey()=K_ENTER //изменение значения в поле БД r1:=row();c1:=col() cVar_get:=FIELDBLOCK(cur_field) @ r ,c get cVar_get read if lastkey()#K_ESC &cur_field := cVar_get endif return 2 case lastkey()=K_DOWN // dbappend // можно дописать добавление записи return 2 otherwise return 2 endcase return 1 2) цитата: "для данной задачи перед использованием browse() отключил индекс, а после включил." переиндексация была ? или на время правки индекса не было ? set index to ..set index to ("index.ntx") лучше: nind :=indexord(); set order to 0 ;.... set order to nind

SergeyKorotun: petr707 пишет: 2) цитата: "для данной задачи перед использованием browse() отключил индекс, а после включил." переиндексация была ? Спасибо, как то упустил, что надо переиндексировать. Почитал ng, оказалось ничего не надо править, при set order to 0 индексы все равно корректируются

SergeyKorotun: Как бы попроще реализовать средствами clipper 5.01 поиск в базе снизу вверх(без сортировки)? Descend() не подойдет, так как для просмотра базы в обратном порядке надо было бы проиндексировать ее по одному ключу(например "номер заказа"), о поиск вести по второму ключу (например "код клиента").

petr707: В условии не вполне задано определение для "обратного порядка" 1) index on (kod_client)+(str(1000000-recno(),7) to tmp можно делать ретроспективу записей клиента - по факту добавления записей в таблицу 2) index on (kod_client)+(str(100000-nomer_zakaz,7) to tmp можно делать ретроспективу записей клиента - по убыванию номера заказа этого клиента .. seek cod0 do while !eof().and. kod_client==cod0 ... // обработка в порядке убывания - номера записи или номера заказа skip enddo

SergeyKorotun: petr707 пишет: seek cod0 Как я понял, у вас cod0=(kod_client)+(str(100000-nomer_zakaz,7) Проблема в том, что нужно искать только по ключу kod_client, а не по ключу (kod_client+что-то еще) Или seek ищет подстроку в ключе(а не точное совпадение)? Если да, то тогда наверно вместо do while !eof().and. kod_client==cod0 надо do while !eof().and. kod_client=cod0, т.е. вместо == писать = Чтобы было понятней, что мне надо, напишу пример не оптимальной реализации задачи. Можно было бы сделать так: создать временную базу и занести в нее записи в обратном порядке (последняя запись в исходной базе становиться первой во временной базе, предпоследняя - второй и т.д.) Потом проиндексировать временную базу по kod_client и затем поиск по seek. Но хотелось бы обойтись без временной базы.

petr707: Вариант 1 - соответствует условию seek ищет не подстроку, а совпадение слева, поэтому и можно искать левую часть индексного выражения Лучше вместо seek применить dbseek( padr(cod0,len(kod_klient),.f.) "==" - нужно, иначе неверный результат отбора записей, например для cod0="12" при наличии в базе клиентов "12" "123" "1234".. сравнивается не индексное выражение, и именно значение поля.

SergeyKorotun: Как в dbedit изменить размер колонок динамически? Нижеприведенный фрагмент программы не изменят ширину колонок (на самом деле колонок много, есть что подтягивать из за пределов монитора) apict дублировал и в процедуре - не помогает. С функции f1 с возвратом return 2 - ширина неизменна SET KEY K_F9 TO PictureSwich() pictFlg:=.t. pictSum:=13 apict:={replicate("9",pictSum)+".99"} acol:={"Summa"} aname:={"Сумма"} dbedit(2,1,21,78,acol,"f1",apict,aname) ... procedure PictureSwich() if pictFlg pictSum:=5 else pictSum:=13 endif pictFlg:=!pictFlg

petr707: pictFlg:=.t. pictSum:=13 private lexit:=.f. private lwas_f9:=.f. do while !lexit if lwas_f9 lwas_f9:=.f. if pictFlg pictSum:=5 else pictSum:=13 endif pictFlg:=!pictFlg endif apict:={replicate("9",pictSum)+".99"} acol:={"Summa"} aname:={"Сумма"} dbedit(2,1,21,78,acol,"f1",apict,aname) if lexit; EXITl endif enddo .... Function f1(..) do case .. case lastkey()=K_F9 lexit:=.f. lwas_f9 :=.t. return 0 // exit from dbedit .. case lastkey()=K_ESC lexit:=.t. return 0 // exit from dbedit ....

Andrey: SergeyKorotun пишет: Вопрос № 6 Смиотри функцию AT()

SergeyKorotun: sum:=0 ReadExit(.f.) @1,1 say 'Сумма' get sum picture("9999999999999.99") read Почему выходит с read после набора двух цифр после запятой? Как разрешить выход только по Enter?

petr707: conf:=SET(_SET_CONFIRM) ... SET(_SET_CONFIRM,.t.) read ... SET(_SET_CONFIRM,conf) SET CONFIRM определяет, требуется ли ввод подтверждения для окончания ввода текущего GET. Если задано SET CONFIRM OFF, то после ввода пользователем последнего символа текущего GET, курсор перемещается на следующий GET, если он есть. Если следующего GET нет, выполнение команды READ прекращается. Если задано SET CONFIRM ON, ввод данных в переменную очередного GET должен завершаться нажатием клавиши выхода (<Enter> или <Esc>). Во всех случаях, если ввод в переменную текущего GET не был прерван нажатием клавиши <Esc>, выполняется предложение RANGE или VALID. За дополнительной информацией о команде GET следует обратиться к описанию команды @...GET.



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