Привет всем, кто интересуется развитием бесплатной библиотеки табличных вычислений ExTab, COM-объекта, цель которого — сделать функциональность электронных таблиц (wiki:Электронная таблица) более доступной для программистов. ExTab — это виртуальная электронная таблица; ExTab может наделить новыми возможностями таблицу значений, или табличный документ, или другой табличный элемент пользовательского интерфейса.
Напоминаю: COM-компоненты регистрируются командой «regsvr32 имя_файла.dll», для чего могут понадобиться права администратора. Для работы компонента требуется установленный в системе «Microsoft Visual C++ 2010 Redistributable Package» (http://www.microsoft.com/download/en/details.aspx?id=5555).
Начну с нововведений. Вот небольшой ролик: ссылка на youtube. Коротко: после некоторых багфиксов функции работы с колонками и строками стали пригодными к работе (хотя ещё не в той степени, как я хочу). Подробнее — позже.
Теперь несколько общих комментариев:
1) Об интерфейсе. Программный интерфейс ExTab в его нынешнем виде не претендует называться изящным. Но я считаю его достаточным и пригодным к работе. Сначала я доделаю ExTab до конца, а уж после буду наводить красоту.
2) О производительности. На самом деле, я толком ещё не проводил никаких тестов, но ExTab скорее всего сумеет покрыть ваши нужды, если вы не будете сильно его мучать. ExTab должен быть быстрее аналогичной реализации на встроенном в 1С:Предприятии языке и избавит вас от необходимости изобретать свой велосипед. В общем, ExTab хоть и должен быть достаточно шустр для большинства случаев, но прошу не ждать чудес от крохотной (< 100 килобайт) программки, которую делает «на коленке» в свободное время программист-самоучка.
3) Об использовании памяти. ExTab весьма экономичен в плане расхода памяти. Вот в общем-то и всё.
4) О работоспособности. В текущем виде библиотека не закончена. Многое ещё предстоит реализовать. Однако тем, что она умеет, уже вполне можно пользоваться. Багов быть не должно . А если они вдруг объявятся — прошу мне сообщить.
5) О демо-конфигурации. Это просто демо, пример, и не более того. Я не сомневаюсь, что вы способны придумать более удачный код интеграции.
А теперь к делу. Рассмотрим код интеграции ExTab и табличного документа на управлямой форме (конечно, ExTab можно использовать и в обычных формах, например, для таблиц значений и вообще для всех компонентов интерфейса, похожих на таблицу… да и без визуального интерфейса тоже можно, но в этом я не вижу смысла). Методика использования такова — вы должны параллельно с вводимыми в табличный документ данными отправлять эти же самые данные в ExTab, а об остальном он позаботится сам.
Первое, что нужно сделать — завести переменную для ExTab в нашей форме:
&НаКлиенте
Перем ЭксТаб;
При открытии формы создадим ExTab, в нём создадим таблицу функцией CreateTable, которая возвращает числовой идентификатор созданной таблицы, и сделаем созданную таблицу текущей используемой при помощи функции SelectTable, принимающей идентификатор созданной таблицы.
ЭксТаб = Новый COMОбъект(«ExTabCom»);
ЭксТаб.SelectTable( ЭксТаб.CreateTable() );
Если не создать таблицу, или создать, но не выбрать — ничего работать не будет. Да, кстати, вы можете создать 2 или 3 таблицы, например, для нескольких закладок на форме. Также мы можем предусмотреть удаление из памяти созданных таблиц при закрытии формы процедурой DeleteTable, принимающей в качестве аргумента идентификатор таблицы, полученный при создании таблицы. Вообще делать это не обязательно, т.к. память будет освобождена автоматически при уничтожении COM-объекта, но форма может существовать какое-то время в памяти после её закрытия, так что, если вы заботитесь об экономии памяти — используйте DeleteTable. Само собой, заведите переменные для хранения идентификаторов таблиц в этом случае.
Теперь пришло время отправить данные в ячейку. Для этого сначала нужно спозиционироваться на ячейке. Для этого существуют две функции:
1) SelectCell(стр, кол). Аргументы — номер колонки и номер строки, 2 числа;
2) SelectCell2(ячейка). Аргумент — строковое представление адреса ячейки. Поддерживается как обычный формат (пример: «C4»), так и RC-стиль (пример: «R3C4»). В формулах тоже применимы оба варианта записи адреса.
Да, кстати. Все строки, которые ExTab принимает, должны быть представлены в кодировке Unicode. Строки, которые ExTab возвращает из функций — это также Unicode-строки. К счастью для пользователей восьмёрки в ней все строковые данные хранятся именно в Unicode. Однако, если вы решите использовать ExTab из какого-то другого языка программирования — учитывайте это. Как дружит с Unicode семёрка я, если честно, не в курсе.
После того, как мы спозиционировались на ячейке, можно выполнить собственно отправку в неё данных. Для этого существуют 4 функции:
1) SetValueI(значение). Отправляет в ячейку целочисленное значение.
2) SetValueF(значение). Отправляет в ячейку число с плавающей запятой, например 3.14.
3) SetValueS(значение). Отправляет в ячейку строковое значение.
4) SetValueB(значение). Отправляет в ячейку значение типа «булево».
Да, согласен, не очень изящно. Но в демо-конфигурации есть функция-обёртка ЭксТаб_ОтправитьЗначение, которая позволяет избавиться от необходимости следить за типом отправляемого в ячейку значения.
Чтобы прочитать значение из ячейки, на которой мы спозиционировались функцией SelectCell или SelectCell2, сначала нужно обязательно вызвать функцию ReadValue(), не имеющую аргументов. После этого можно запросить тип хранящегося в ячейке значения функцией GetValueType(), которая возвращает числовой идентификатор типа: 0 — ячейка не содержит значения, 1 — целое число, 2 — число с плавающей запятой, 3 — строковое значение, 4 — булево. Зная тип, можно получить значение одной из этих функций: GetValueI(), GetValueF(), GetValueS(), GetValueB(). Очередной ужас , да? И это при имеющихся-то возможностях COM-программирования… Но я не закапывался пока в COM слишком глубоко, я изучил необходимый минимум, чтобы как можно быстрее идти дальше. Бантики и украшательства будут позже. Чтобы упростить себе жизнь, посмотрите пример обёртки в функции ТабПриАктивизацииОбласти в демо-конфигурации. Кстати, вы можете, например, независимо от типа значения в ячейке всегда использовать GetValueS, если вы твёрдо вознамерились во что бы то ни стало получать из ячеек только строковые данные. Или GetValueF, которая будет пытаться конвертировать находящееся в ячейке значение именно в формат числа с плавающей запятой, и со строкой вида «36.6» она запросто управится. Т.о. вы можете использовать встроенные в ExTab возможности конвертации значений между разными типами, хотя 1С:Предприятие обладает достаточными возможностями по этой части, так что это не слишком-то актуально.
Чтобы удалить значение из ячейки, на которую проведено позиционирование, и т.о. удалить занятую им память, используйте функцию EraseValue(). Для полного удаления текущей ячейки из памяти, если она больше совсем не нужна ни для хранения значения, ни для хранения формулы — используйте DeleteCell(). Если вы дорожите оперативной памятью — эта функция для вас. Удалённую ячейку без каких-либо проблем можно будет снова занять каким-то значением позже.
А теперь мы переходим к самой интересной части — к формулам. Методика работы с формулами такова — вы заполняете ячейки данными и формулами, после чего вызываете специальную функцию для полного пересчёта. Это работает достаточно быстро, хотя мне есть что оптимизировать. Частичный пересчёт я собираюсь реализовать в поздних версиях. Кстати, если в процессе вычисления обнаружатся циклические ссылки (например, «a1=a2» и «a2=a1+1»), пересчёт будет отменён. Избегайте циклических ссылок.
Чтобы назначить формулу текущей ячейке, используйте функцию SetFormula(f), принимающую строку формулы в качестве аргумента. Если формула ошибочна, то просто ничего не произойдёт. Чтобы получить формулу, используйте GetFormula(); в случае отсутствия формулы в ячейке функция вернёт пустую строку. Чтобы удалить назначенную текущей ячейке формулу, используйте функцию EraseFormula().
Формулы, которые можно использовать, напоминают формулы экселя. Но количество встроенных функций пока не велико, в будущем добавлю и другие. Вот примеры корректных формул:
- a1+a1*b3+(b4/e10)
- 10+»ой»*2
- sum(a1:r5c5;10;-1.23)
Обратите внимание на пример 2. В математических выражениях операции с нечисловыми значениями просто пропускаются, т.е. формула эквивалентна этой: «10+2». ExTab стремится создавать пользователю меньше проблем, часть его идеологии — позволять пользователю мелкие неточности, не заваливая его сообщениями об ошибках. Например, в формуле «2*4*6*» последнее умножение будет пропущено, и такая формула тоже считается корректной.
Аргументы в формулах разделяются точкой с запятой (;). ExTab сейчас поддерживает следующие формулы:
- Sum. Арифметически суммирует аргументы. В качестве аргумента можно указать адрес, значение, выражение, диапазон адресов; Нечисловые аргументы игнорируются.
- Sumc. Та же сама функция Sum с суффиксом. Стремится сконвертировать нечисловые значения аргументов в числовые. Например, булево «истина» — это 1, строковое «101 долматинец» — это 101.
- If(условие;значение1;значение2). В случае истинности условия, возвращает значение 1, иначе — значение 2. Условие может быть любого типа. Функция будет стремиться подобрать самый подходящий аналог. Например, пустая строка — это ложь. Число 0 — это ложь. Значение2 можно не указывать (ExTab не вредный ), тогда в случае ложности условия функция возвратит спецзначение НИЧТО. Это аналог 1С-ного «Неопределено». Операции с НИЧТО игнорируется в арифметических выражениях.
- Concat. Выполняет строковое суммирование аргументов. Нестроковые аргументы игнорируются.
- Concatc. Та же самая функция Concat с суффиксом. Стремится сконвертировать нестроковые аргументы в строковые. Например, булево «ложь» будет преобразовано в строку «false».
После того, как значения и формулы расставлены по своим местам, для пересчёта нужно вызвать функцию Calculate(). Эта функция — одна из тех, которые обновляют список изменённых ячеек. Этот список после выполнения функции заполняется значениями адресов ячеек, которые вам нужно будет обновить в табличном документе, чтобы привести его в соответствие с новым внутренним состоянием, в котором находится ExTab. Чтобы узнать количество изменённых ячеек, воспользуйтесь функцией GetUpdatedCellsCount(). Скажем, она вернула значение Y. Далее, в цикле X от 0 до Y (не включая Y!) нужно вызвать функцию GetUpdatedCellRC(X, стр, кол), которая поместит по номеру обновлённой ячейки X в переменные стр и кол соответственно номер строки и номер колонки адреса изменённой ячейки. Соответственно, для каждого полученного таким образом адреса нужно получить данные из ячейки ExTab и обновить табличный документ. Ещё одно некрасивое решение… А как же передача массивов и всё такое? Будет, всё будет. Но позже. В демо-конфигурации есть образец функции-обёртки (ЭксТаб_ОбновитьИзменённые), которая позволяет дистанцироваться от всего этого ужаса.
А теперь перейдём к новым трюкам, которым я успел научить ExTab к этой версии — ссылка на демо-видео есть в начале. Вот она снова: ссылка на youtube. Итак, вот список новых функций, пригодных к использованию:
- EraseRow(стр). Очистить строку с номером стр.
- EraseColumn(кол). Очистить колонку с номером кол.
- DeleteRow(стр). Удаляет строку с номером стр. Строки снизу сдвигаются вверх.
- DeleteColumn(кол). Удаляет колонку с номером кол. Колонки справа сдвигаются влево.
- InsertRow(стр). Вставляет строку на место строки с номером стр. Строка стр и строки снизу сдвигаются вниз.
- InsertColumn(кол). Вставляет колонку на место колонки с номером кол. Колонка кол и колонки справа сдвигаются вправо.
Эти 6 функций обновляют список изменённых ячеек! ЭксТаб_ОбновитьИзменённые вам в помощь . А функции 3…6 также выполняют модификацию формул в сдвинутых ячейках по аналогии с тем, как это делает Эксель. Признаюсь — я не очень хорошо оттестировал этот момент. Кроме того, текстовое представление формул, хранящееся внутри ExTab, не обновляется в случае таких переносов. Исправить эти недоразумения я собираюсь к следующей версии (ну ещё пару багов надо устранить…). Однако, если вы не будете использовать GetFormula, то вам эти исправления без надобности. Но на случай, если вы решите предоставить пользователю возможность редактировать формулу, я реализую вышеназванное.
Фуфф… Наш табличный документ стал чем-то похож на Эксель. Хотя, конечно, это не является нашей целью.
Жду ваших комментариев . В следующий раз вернусь с обновлённой версией и новыми планами на будущее. Ну или не вернусь, если меня закидают тухлыми помидорами, в которых я благополучно утону.
Читал и не понял: а на фига эта компонента нужна? В чем ее фишка?
Вообще-то если что-то сделал, то это и означает, что красота наведена. Верно и обратное: если красота не наведена, то ничего до конца и не доделано.
Вообще-то когда-то Б.Гейтс утверждал, что пользователю на всё про всё хватит за глаза 64 килобайта, а тут «недоделанная программа всего в 100 килобайт».
Идея проста. Берём некий компонент интерфейса, имеющий табличный вид, и делаем из него электронную таблицу — расчёты по формулам, удаление/вставка строк. Вот, в общем-то и всё.
А зачем такие сложности?
1) SetValueI(значение). Отправляет в ячейку целочисленное значение.
2) SetValueF(значение). Отправляет в ячейку число с плавающей запятой, например 3.14.
3) SetValueS(значение). Отправляет в ячейку строковое значение.
4) SetValueB(значение). Отправляет в ячейку значение типа «булево».
Есть тип VARIANT, реализованный в WinAPI. Переменная этого типа может содержать любой тип данных и описание типа данных, в ней содержащихся. И не надо было бы для каждого типа свою функцию описывать. И дополнительный функционал можно было бы реализовать, например вставку в таблицу COM объектов.
Насчёт небольшого размера dll-ки (< 100 килобайт): MS Visual Studio 2010 Redistributable Package несёт в себе .NET Framework 4. Отсюда — небольшой размер, ведь большинство функционала берут на себя библиотеки .NET Framework. И, в зависимости от того, какие библиотеки подгружаются, объём использованной памяти может в десятки раз быть больше, чем dll-ка весит.
В общем, MSDN в помощь, как говорится 🙂
поделка для сравнения:http://infostart.ru/public/123090/
(3) premier, всё верно… Но я уже немного всё переделал, теперь ввод строковой — одной функцией. Хотя, глядя на реализацию от dolter, подумываю бросить эту затею. В любом случае, хоть кодить научился лучше — уже неплохо.
(5) В любом случае, хоть кодить научился лучше — уже неплохо.
Даже в отрицательном опыте есть положительные стороны 🙂
Кстати, если вспомнить про технологии OLE Automatiom и DDE, можно использовать и навороченные табличные редакторы для создания отчетов, используя данные 1С:Предприятия! MS Excel, например…
И никаких компонент и обработок и даже в 1С-ку входить не надо 🙂
(6) premier, но навороты MS Excel нужны далеко не всегда, да и денюжек он стоит. Хотя, конечно, куда мне тягаться с такими монстрами, глупо даже сравнивать.
Я сейчас подумываю натравить свой компонент на таблицу значений, которая, как мне кажется, куда хуже подходит на роль электронной таблицы, чем табличный документ. Хотя вот думаю, а зачем это всё… Но закончу своё дело наверно чисто из упрямства 🙂 пусть даже оно никому будет не нужно. Зато потом смогу, например, на питоне написать мини-электронную таблицу. Опять-таки бесполезно, но баранье упрямство не даёт остановиться.
Хотя я уже опасаюсь, что потом снова объявится dolter и окажется, что я изобрёл велосипед с квадратными колёсами 😀
Проект кардинально переосмысливается — благодарю пользователя dolter за очень дельные замечания.
(9) было бы полезно сделать компоненту, которая могла бы работать и в 7.7, вариант dolter-а — это только частный случай для 8-ки, привязанный к полю табличного документа. А вот универсальная штука, которую можно было бы привязать к любому визуальному объекту отображения по желанию программиста, вот это была бы вещь 😉
Рад, что вы поняли мою идею 🙂
COM-объект должен и в 7.7 работать. Правда тот, что здесь лежит, очень устарел. Есть новая версия. Если вам подобная штука пригодится — пишите, обсудим.
(11) спасибо, у меня в данном случае не коммерческий интерес, поэтому пока попробую эту версию 🙂
Наконец-то дошли руки до попробовать, но что-то у меня длл-ка отказалась регистрироваться в системе. Ошибка при вызове LoadLibrary, не найдена указанная процедура.
(13) wolfsoft, Этот проектик мне интересен и не в коммерческом ключе. Я нехило так прокачиваю свои навыки, занимаясь столь заковыристой задачей.
Извиняюсь, точно, проверил на виртуалке — и правда ошибка есть… Как я мог такое прошляпить? Дело в том, что я тут использую в процедуре отмены регистрации функцию, доступную только начиная с висты. В общем, я нашёл кое-как проект в закромах и перекомпилил, вырезав процедуру дерегистрации. Это значит, что отменить регистрацию библиотеки не получится, и останется немного мусора в реестре. Если это вас не испугает, вот отсюда можно скачать:rghost
(13) wolfsoft, а вот ссылка на более свежую версию.cyberforum.ru
Точнее, это уже внутренне совсем другая программа, и по возможностям другая.
(15) спасибо, попробую 🙂
Описалово бы ещё к ней…
По теме обработка:http://infostart.ru/public/15970/