Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2006.01.08;
Скачать: [xml.tar.bz2];

Вниз

Как устроены электронные таблицы?   Найти похожие ветки 

 
TStas ©   (2005-12-10 15:06) [0]

Правда, интересно. Например, рабочая книша эксель. Понятно, что каждая ячейка - это объект. Есть у него, похоже, три основных свойства: текст ячейки, формула и список ячеек, которые надо уведомлять об изменении. Еще, вроде, должен быть метод пересчета и событие, когда содержимое изменилось. А вот дальше? Пытался сообразить, и как-то непонятно все получается.


 
TUser ©   (2005-12-10 15:29) [1]

Формат экселя можно посмотреть в сети (открытый хакерами), например на www.wotsit.org.

То, что каждая ячейка - это объект (в понимании ООП) - очень сомнительно, много объектов получается 2^24 на каждом листе.


 
TStas ©   (2005-12-10 15:36) [2]

Вопрос-то не о формате. Мне интересно, как электронная таблица устроена. Эксель - это частный случай. А потом ведь объекты, если речь идет об экселе, могут создаваться лишь для заполненных ячеек, а вот их не 2 в 24


 
TAntonn   (2005-12-10 16:41) [3]


> А вот дальше? Пытался сообразить, и как-то непонятно все
> получается.

А что, собсвенно, не получается? :-)


 
TStas ©   (2005-12-10 16:50) [4]

>TAntonn
Например:
В том же экселе можно удалить строку или столбец. При этом сами ячейки накуда не деваются, трутся, судя по всему, лишь объекты. А когда они создаются и как поддерживается целостность этой цепочки?
Редактируется формула. Значит, при этом должны переписываться ссылки по всей цепочкие. Вот поддержание этих цепочек и никак не соображу. Они ведь не как в базах один ко многим и т. д. Ведь таблица-то одна.


 
Antonn ©   (2005-12-10 17:03) [5]

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


 
wicked ©   (2005-12-10 17:16) [6]

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


 
jack128 ©   (2005-12-10 17:39) [7]

TStas ©   (10.12.05 16:50) [4]
Вот поддержание этих цепочек и никак не соображу. Они ведь не как в базах один ко многим и т. д. Ведь таблица-то одна.


А в чем проблема?? Простейшей рекурсией реализуется..

TCell = class
protected
 procedure Changed(RefreshRefCells: boolean);
public  
 property RefCells: TCells; // список ячеек, содержимое которых зависет от содержимого текущей ячейки..
 procedure Refresh(IsRefCellsRefresh: boolean);
end;

procedure TCell.Refresh(IsRefCellsRefresh: boolean);
begin  
 ..
 if IsRefCellsRefresh then
   for i := to RefCells.Count - 1 do
     RefCells[i].Refresh(True);
end;


 
TStas ©   (2005-12-10 20:42) [8]

>wicked
Я и сам так думал, но это не получится - важен порядок. Иначе просто невозможно посчитать.
Непонятно, как там вылавливается бесконечная рекурсия. А экселем она вылавливается на ура и моментально. Цепочка же не линейеая получается. В общем случае она ветвится.
>jack128
Обноление - действительно простейшая рекурсия. А вот целостность?
Получается, что у ячеки должно быть свойство, на какие она ссылается, чтобы при изменении формулы, она уведомляла, что ее уже не надо или наборот, теперь надо пересчитать.
Но если с обновлением хоть как-то понятно, скорее всего там так как-то и сделано, вот как вылавливают бесконечную рекурсию - это выше моего понимания. Ну когда ячейка сама от себя зависит.


 
jack128 ©   (2005-12-10 20:50) [9]

Легко и просто это обходится
procedure TCell.Refresh(IsRefCellsRefresh: boolean);
begin  
if FInRefreshing then
  // raise Exception.Create("Бесконечная рекурсия");
  Exit;
FInRefreshing := True;
try
  ..
  if IsRefCellsRefresh then
    for i := to RefCells.Count - 1 do
      RefCells[i].Refresh(True);
 finally
   FInRefreshing := False;
 end;
end;


 
VirEx ©   (2005-12-10 21:08) [10]

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


 
Prohodil Mimo ©   (2005-12-10 22:33) [11]

VirEx ©   (10.12.05 21:08) [10]
и всё это делается многопоточно


если убрать лишние действия, достаточно и одного потока.
Подобие Excela делал (у каждой ячейки свой текст, фонт, угол наклона, выравнивание, цвет фона, патерны, цвет и стиль каждой линии рамки, объединение ячеек (верт и гориз), мож ещё что упустил), правда без формул, но это в будущем.


 
TStas ©   (2005-12-10 22:48) [12]

>Prohodil Mimo
А можете посоветовать как? Я не из чистого любопытства это ветку поднял. Что они невизуальные это я догадался сразу, видимыми ячейками они пользуются исключительно как интерфейсом. Что у них вырисовываются 4 свойства я понял: текст: Строка, формула, то, наверно, строка, массивы на кого ссылается и кто на нее ссылается, ТЛист, скорее всего, а того лучше - потомок ТЛиста. Метод пересчета, естественно, и событие, а, может, два даже события - изменение формулы и изменение текста. Внешний вид менять, вроде, мне не требуется. Еще, возможно, координаты ячейки, через которую объект с пользовательницей общается. Как его в файл сохранить не просто, но примерно представляю.
>jack128
Возможно я туп, но не понял. Ведь ячейка может на себя же ссылаться очень издалека, да хоть через весь лист.


 
TUser ©   (2005-12-10 22:50) [13]

А как насчет вот такой формулы? Результат - значение ячейки из столбца А, а номер троки пишется в А2. Короче говоря, при вводе этой формулы неизвестно, изменение какой ячейки должно приводить к ее пересчету, скажем А3 или А125.

=ДВССЫЛ(СЦЕПИТЬ("a";ТЕКСТ(A2;"0"));A1)


 
TStas ©   (2005-12-10 22:59) [14]

>TUser
Ну, вообще-то это экзотика. Только я не вижу, в чем здесь особенная трудность? Если вычислять в правильном порядке, то все должно получиться. А с целостностью непонятно мне до конца.
Если, конечно, в другую сторону делать, начинать парсить формулу, то все получится. Но тогда неясно, как должны ячейки узнавать, что источник-ячейка поменялся.


 
TUser ©   (2005-12-10 23:03) [15]


> TStas ©   (10.12.05 22:59) [14]

Пользователь изменил значение ячейки А20. Какие ячейки должы быть изменены? Те, которые ссылаются на А20, так? А ссылается ли эта формула на А20 - неизвестно, это что-то вроде позднего связывания, определяется на этапе работы формулы, а не на этапе ее ввода.

Экзотика или нет - но работает.


 
TStas ©   (2005-12-10 23:08) [16]

>TUser
Я думал так:
Сослались на ячейку - вписали ей в список ссылок ту, которая сослалась.
Узменилась она - с ленинской прямотой уведомила ссылающуюся и даже заставила ее пересчитаться.
А ваш пример... Ну не знаю как.


 
TStas ©   (2005-12-10 23:52) [17]

>jack128 Или имеется в виду, что у ячейки есть внутреннее поле, указывающее, что она находится в состоянии обновления, она возбуждает события, заставляющее другие ячейки обновиться, но, естественно, из процедуры обновления не выходит при этом. Тогда, если по цепочке события заставляют ее же обновиться (ссылка на себя же), она возбуждает исключение? Говорю же - тупой я :)
А что, приходилось самому что-то подобное писать?


 
jack128 ©   (2005-12-10 23:54) [18]

TStas ©   (10.12.05 22:48) [12]
Ведь ячейка может на себя же ссылаться очень издалека, да хоть через весь лист.

И что? Если ячейка ссылается сама на себя, то при вызове Refresh(True) пройдется по всему листу и венется сама на себя. Дл япримера, ячейка 1 вызывает изменение ячейки2, та - изменение ячейки 3 -> 4 -> 5.
Если мы вызовем Cell4.Refresh(True), то получим такой стек вызовов

Cell4.Refresh(True)
 Cell5.Refresh(True)
   Cell1.Refresh(True)
     Cell2.Refresh(True)
       Cell3.Refresh(True)
         Cell4.Refresh(True) // Но Cell4.FInRefreshing = True, поэтому мы можем поднять исключение или просто прекратить обновление ячеек, в зависимости от задачи.


То есть мы прошли по всем ячейкам и вернуль в исходную..

TUser ©   (10.12.05 22:50) [13]
Короче говоря, при вводе этой формулы неизвестно, изменение какой ячейки должно приводить к ее пересчету, скажем А3 или А125.

Как это не известно??  Известно.  Ячейки по адресу СЦЕПИТЬ("a";ТЕКСТ(A2;"0")).  Эту формулу нужно вычислить и получим нужную ячейку. Соответствуенно, получим две ячейки, от какторых зависит данная. При изменении любой из них, нужно её обновить..


 
TStas ©   (2005-12-10 23:57) [19]

>jack128 Так я верно написал в (17)?
А с вопросом ТЮзера мне все равно не ясно. Если цепучку изменений должна начинать ИЗМЕНИВШАЯСЯ ячейка, то здесь получается, что поряд изменится. Или опять торможу?


 
iZEN_   (2005-12-11 00:02) [20]

Есть такой паттерн Flyweight... ;)


 
TStas ©   (2005-12-11 00:07) [21]

>iZEN_
А что это? Можно чуть подробнее?


 
atruhin ©   (2005-12-11 00:07) [22]

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


 
TStas ©   (2005-12-11 00:11) [23]

>atruhin
Я под ячейкой имел ввиду объект. Понятно, что это не ячейка Грида, конечно. И Женя в примере кода написал TCell. Надо же ее как-то назвать было.


 
jack128 ©   (2005-12-11 00:18) [24]

TStas ©   (10.12.05 23:57) [19]
Так я верно написал в (17)?

Да.
TStas ©   (10.12.05 23:52) [17]
А что, приходилось самому что-то подобное писать?

И писать в том числе.  Сейчас разбираю систему, в которой всё построено на этих бесконечных натификациях.. Уже готов вешаться, чесно говоря.

TStas ©   (10.12.05 23:57) [19]
Если цепучку изменений должна начинать ИЗМЕНИВШАЯСЯ ячейка, то здесь получается, что поряд изменится

Инициатором всех рефрешей, стественно должна быть ячейка изменнённая юзером. А вот кто о ком должен знать - это вопрос полический ;) В первом, предложенном мной варианте, ячейка знает вс ячейки, которые от неё зависит и напрямую вызвает их метод Refresh.   Другой вариант - у ячейки (например A1) есть массив событий OnChanged.  Те ячейки, которые поняли, они завиcят от ячеки A1, цепляют свой обработчик на этот событие и в нем вызывают себе Refresh. В большестве случаев - второй вариант предпочтительней.


 
jack128 ©   (2005-12-11 00:26) [25]

TStas ©   (11.12.05 0:07) [21]
А что это? Можно чуть подробнее?


http://www.piter.com/book_about.phtml?id=978527200355&refer=101
Но сразу применять, то что там написано к текущему проэекту не стоит ;)


 
Lamer@fools.ua ©   (2005-12-11 01:29) [26]

>>TStas ©   (11.12.05 00:07) [21]

http://www.google.com/search?client=opera&rls=en&q=Flyweight&sourceid=opera&ie=utf-8&oe=utf-8


 
Prohodil Mimo ©   (2005-12-11 13:37) [27]

TStas ©   (10.12.05 22:48) [12]
А можете посоветовать как?


Насколько я понял - вопрос в том, как пересчитывать формулы и как находить ссылки саму на себя. Но, как я уже писал, формулы я пока не делал, это планы на будущее. Примерно представляю, но пока не проверил - утверждать ничего не буду.

А для самой таблицы я создал тип:

 PADCell=^TADCell;
 PADCellBorders=^TADCellBorders;
 PADCellInterior=^TADCellInterior;

 TADCell=Record
    Text:String;
    HAlign:Byte;
    VAlign:Byte;
    Wrap:Boolean;
    TextAngle:Word;
    NumberFormat:String[20];
    Borders:PADCellBorders;
    Interior:PADCellInterior;
 End;

 TADCellBorders=Record
    Top:Byte;
    Bottom:Byte;
    Left:Byte;
    Right:Byte;
    TopColor:TColor;
    BottomColor:TColor;
    LeftColor:TColor;
    RightColor:TColor;
 End;

 TADCellInterior=Record
    Font:TFont;
    Pattern:Byte;
    PatternColor:TColor;
    BackGround:TColor;
 End;

 TADCellsArr=Array[0..SheetRowCount,0..SheetColCount] of PADCell;

TADSheet = Class(TCustomControl)


т.к. Delphi3, то TADCellsArr имеет постоянный размер, в 2005 переделаю на динамическое изменение.

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


 
TStas ©   (2005-12-11 14:09) [28]

>jack128
Вот и мне он показался предпочтительным. То есть ячейка должна знать только о НАПРЯМУЮ завясящих от нее ячейках. Мне показалось, что всеж надо иметь и второй массив - от кого завист ячейка. НО он нужен только, чтобы при изменении формулы править массив ссылок ячеек, на которые ссылает. Вроде, массивы ссылок должны редактироваться при редактировании формулы. А еще, наверно, у ячейки (объекта) должны быть две координаты - через какую клетку сетки с пользователем общаться. Я пока склоняюсь к тому, что все же ячека сама невизуальная.
Тогда, вроде, получается так:
Два события: изменение текста и изменение формулы.
Свойсва: текст, формула и массив ссылок на ячейку (прямых только).
Метод - обновление ячейки.
При изменении текста (любом), надо просматривать массив ссылок и вызывать метод обновления всех ячеек в нем.
А вот при изменении формулы надо подумать. Ведь формула должна содержать и выражения и адреса. Ее, похоже, надо перед изменением сохранять, парсить, вытаскивая из нее адреса, стирать ссылки на нее из ячеек, от которых она зависит. Потом, когда формула изменилась, опять парсить, вытаскивая адреса, вставлять ссылки на ячеки, от которых она зависит, и только потом вызывать метод обновления самой ячейки. При вызове этого метода адреса ячеек из формулы надо заменять на их текст.
Вроде бы так.
Еще у ячеки должен быть метод сохранения и загрузки из потока. Чтобы в файла сохранять и копировать/вставлять.

К самому проекту еще ничего пока не приделываю. Есть лишь идея и та очень сырая.
PS. Форум жестоко глючит


 
TStas ©   (2005-12-12 19:07) [29]

Женя, спасибо за советы дельные. Все получилось и работает



Страницы: 1 вся ветка

Форум: "Потрепаться";
Текущий архив: 2006.01.08;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.009 c
14-1134363176
АлексейК
2005-12-12 07:52
2006.01.08
Лама Итигелов


3-1131991173
ivb2001
2005-11-14 20:59
2006.01.08
TClientDataSet не видит TDatasetProvider


4-1130664226
злобная танька
2005-10-30 12:23
2006.01.08
Открыть с помощью...


8-1123224565
worldmen
2005-08-05 10:49
2006.01.08
Gif-анимация + TAnimate


2-1135240151
Вт
2005-12-22 11:29
2006.01.08
Виртуальный диск в памяти





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский