Jump to content

Recommended Posts

Posted

Здравствуйте.

В нашем приложении UniGui (1.95.0.1607 / 1.95.0.1579 - всё нижеописанное проверяли на этих версиях) мы используем рисовальный движок ExtJS. Нам в нашем приложении потребовалось реализовать сложные графики. Имеющегося функционала графиков UniGui оказалось не достаточно. Решили воспользоваться имеющимся draw-движком ExtJS. Получилось хорошо, но недавно в нашем приложении мы столкнулись с одной ошибкой. Ошибка возникает в коде ExtJS.

Я сделал небольшое тестовой приложение, надеясь что ошибка воспроизведётся и в этом приложении. Но, к сожалению, не получилось воспроизвести ошибку. В тестовом приложении всё хорошо работает.
Тестовое приложение построил с похожей структурой, что и в рабочем приложение. На его примере поясню что происходит в рабочем приложении.

Архив с тестовым приложением прикладываю (проверял на UniGui 1.95.0.1579).

Пояснения по тестовому приложению:
- ..\Win32\Debug\files\jsdrawtest.js - здесь весь тестовый код, который работает с движком ExtJS. Здесь объявляется объект наследник от Ext.draw.Container. При создании этого объекта он добавляется в качестве дочернего элемента на TUniPanel. В этом объекте рисуется прямоугольник по заданным координатам, заданного размера и заданного цвета, рисуется текст рядом с прямоугольником и прямоугольник реагирует на наведение мышки;
- DrawContainerFrame.pas - TUniDrawContainerFrame наследник TUniFrame для подключения кода из jsdrawtest.js к UniGui приложению;
- в Main.pas создаются два экземпляра TUniDrawContainerFrame. 1-ый экземпляр становится видимым через меню "Test2", 2-ой через "Test3". "Test1" - скрывает оба экземпляра (вид по умолчанию). "Test4" - выводит PDF (код добавлен в рамках попытки воспроизвести ошибку из рабочего приложения);
- в Main.pas работает клиентский таймер, в рамках которого меняется текст рядом с прямоугольниками для двух экземпляров TUniDrawContainerFrame.

Как я написал ранее, я сделал тестовую программу со схожей структурой что и в нашей рабочей программе. В рабочей программе код TUniDrawContainerFrame и jsdrawtest.js просто значительно сложнее. 

Ошибка в рабочей программе возникает в момент 1-го перехода (именно 1-го) с вида "Test2" на "Test3" (или наоборот, в момент 1-го перехода с вида "Test3" на вид "Test2"), т.е. в момент переключения с одного графика на другой и только в том случае если при этом 2-ой график при таком переходе отображается первый раз и до этого не отображался. Если в начале сделать переход с "Tst1" на "Tst2" (1-ый раз отобразить график "Tst2"), потом вернуться на "Tst1" и потом сделать переход на "Tst3" (1-ый раз отобразить график "Tst3"), то ошибка НЕ возникнет и после этого любые переключения между "Tst2" и "Tst3" к ошибке приводить НЕ будут. Т.е. ошибка возникает именно при 1-ом отображении 2-го по порядку графика.

Так же ошибка возникает в момент перехода с вида "Test4" на вид "Test2" или на вид "Test3", т.е. в момент перехода с просмотрщика PDF на графики и так же только при условии что до этого график не отображался. Если до этого графики отобразить хотя бы одни раз, то ошибки при таком переключении уже не будет.

Иногда ошибки при таких действиях не возникают. Редко, но такое тоже бывает.

Поведение ошибки несколько странное. Видимо я что-то не понимаю и не так делаю в коде.

Вот здесь привожу текст ошибки из консоли браузера:

charts.js:1 Uncaught TypeError: Cannot read properties of null (reading 'style')
    at constructor.updateRect (charts.js:1:123986)
    at constructor.b [as setRect] (ext-all.js:20:86530)
    at constructor.<anonymous> (charts.js:1:128905)
    at ctor.each (ext-all.js:20:353053)
    at constructor.defaultResizeHandler (charts.js:1:128886)
    at Object.callback (ext-all.js:20:76002)
    at Object.callback (ext-sync-min.js:9:11516)
    at constructor.handleResize (charts.js:1:128647)
    at constructor.onResize (charts.js:1:846)
    at constructor.afterComponentLayout (ext-all.js:20:563268)

Вот код charts.js, в котором эта ошибка возникает:

...
 updateRect: function(c) {
        this.callParent([c]);
        var a = this, s = Math.floor(c[0]), u = Math.floor(c[1]), t = Math.ceil(c[0] + c[2]), r = Math.ceil(c[1] + c[3]), f = a.devicePixelRatio, g = a.canvases, q = t - s, p = r - u, d = Math.round(a.splitThreshold / f), n = a.xSplits = Math.ceil(q / d), o = a.ySplits = Math.ceil(p / d), l, m, e, h, i, b, k, j;
        for (m = 0,
        i = 0; m < o; m++,
        i += d) {
            for (l = 0,
            h = 0; l < n; l++,
            h += d) {
                e = m * n + l;
                if (e >= g.length) {
                    a.createCanvas()
                }
                b = g[e].dom;
                b.style.left = h + 'px';
                b.style.top = i + 'px';
                j = Math.min(d, p - i);
                if (j * f !== b.height) {
                    b.height = j * f;
                    b.style.height = j + 'px'
                }
                k = Math.min(d, q - h);
                if (k * f !== b.width) {
                    b.width = k * f;
                    b.style.width = k + 'px'
                }
                a.applyDefaults(a.contexts[e])
            }
        }
        a.activeCanvases = e = n * o;
        while (g.length > e) {
            g.pop().destroy()
        }
        a.clear()
    },
...

Может кто-нибудь даст какой-нибудь совет по устранению этой ошибки? Что я не так делаю, что не учитываю?

UniGuiDrawContainerTest.zip

Posted

Вероятнее всего, проблема связана с тем, что Ext.draw.Container получает события resize/layout до того, как его внутренние canvas-элементы полностью отрендерены и добавлены в DOM.

В качестве решения можно попробовать:

  • инициализировать и добавлять draw-контейнер только после afterrender или непосредственно после show(), а не в скрытом состоянии;

  • откладывать операции resize/перерисовки через Ext.defer(...);

  • перед обращением к canvas.dom проверять, что элемент уже существует;

  • принудительно вызывать перерасчёт размеров графика после первого отображения компонента.

В тестовом проекте ошибка, скорее всего, не воспроизводится из-за более простой структуры и меньшего количества асинхронных layout/resize операций. В рабочем проекте при большем количестве компонентов и переключений возникает race condition между render и resize, которая и приводит к описанной ошибке.

  • Thanks 1
Posted

Спасибо, Ваши комментарии очень интересны, подумаю над ними.

  • Upvote 1
Posted

Проблему в рабочем приложении решить удалось. Использовал Вашу 1-ую рекомендацию. Связывание (через add) наследника от Ext.draw.Container c TuniPanel сделал не сразу после создания (как было до этого), а уже после непосредственного show() и всё стало нормально работать без каких-либо ошибок.

Спасибо!

  • Thanks 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...