По форматам файлов которые создают цифровые осциллографы. Простая рисовалка для wfm файлов от Rigol и waf файлов от АКИП (Hantek ?)


Почему то производители осциллоскопов пишут программки просмотра осциллограм, которые работают только в связке с самим осциллографом. Конечно не все, у OWON есть возможность просматривать осциллограммы сохранённые на флеш диск и автономно. А вот у Rigol, АКИП и Hantek такой возможности нет.
Написал простую програмку для отрисовки осциллограмм полученных на осциллографах Rigol или АКИП. Первый из них сохраняет файлы в формате wfm, второй в формате waf. Насколько я понял waf формат от АКИП и Hantek совпадают, хотя проверить это у меня возможности нет, на руках есть только АКИП4113/2.

Внутри эти файлы устроенны в общем то просто.
wfm файл выглядит вот так

                   49(uint8_t )  -  наличие данных по 1 каналу 
                   73(uint8_t )  -  наличие данных по 2 каналу 

                   28(uint32_t)  -  число точек * 
                   84(uint64_t)  -  эта величина умножается на число 1e-12, 
                                    получаем деление шкалы Х по времени (секунд/деление) 
                   92(int64_t)   -  время задержки 

                   100 (float)   -  частота дискретизации в герцах 
                   142(uint16_t) -  число(от 0 до 6) в младших 4 битах определяет выбранный  режим синхронизации 
                                              "Edge" 
                                              "Pulse" 
                                              "Slope" 
                                              "VID" 
                                              "Alt" 
                                               "Pattern" 
                                               "Duration" 

                    142(uint8_t) - число (от 0 до 5) определяет источник синронизации 
                                "CH1" 
                                "CH2" 
                                "EXT" 
                                "EXT/5" 
                                "AC Line" 
                                "Digital Channel" 


информация по настройкам 
                   канал 1 
                   46(uint16_t) - число по этому адресу определяет коэффициент деления делителя на щупе 
                                          0x3F80  - 1 
                                          0x4120  - 10 
                                          0x42C8 -  100 
                                          0x447A  - 1000 
                    36(uint32_t) - масштаб по вертикали, умножается на коэфф. делителя щупа и 1e-6 (вольт/деление) 
                    40(int16_t)  - смещение по вертикали, делится на величину (25 * коэфф. делителя щупа) 
                                         и умножается на масштаб по вертикали 

                   канал 2  - всё также как и для первого канала 
                   70  
                   60 
                   64 


сами данные 
                   272(uint8_t)  - блок данных канала 1 длиной равной числу точек * 
                                   данные 8 битные                                               
                                   блок данных для канала 2 начинается сразу за блоком 
                                   данных для первого канала


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

waf файл выглядит так
сначала идёт описание первого канала
  7(uint8_t) - 0 канал выключен, 1 канал включён
  9(uint8_t) - число находящееся здесь определяет выставленную чувствительность по вертикали
               число 2 соответсвует чувствительности 2mV/del, дальше идут числа 3,4,5...
               которые просто определяют чувствительность в порядке 1,2,5. Т.е. к примеру
               сначала идёт 2mV/del, следующее это 5mV/del, затем 10mV/del и так далее.
  13(uint8_t)- число находящееся здесь определяет каэффициент деление выставленный на щупе,
               и соответсвенно выставленную настройку на осциллографе.
               0 - это 1
               1 - 10
               2 - 100
               3 - 1000
               на это число в итоге умножается чувствительность по вертикали.
  
 29(uint8_t)- число находящееся здесь определяет установленное смещение, относительно центра шкалы,
              по вертикали. Смещение кратно 40mV умноженном на коэффициент деления щупа. 
              Нулевому смещению соответсвует десятичное число 100. Изменение в сторону уменьшения
              соответсвует положительному смещению(луч смещается наверх). Например число 99 по этому
              адресу соответсвует смещению в 40mV, число 98 соответсвует смещению в 80mV....
              Изменение в сторону увеличения соответсвует отрицательному смещению, т.е. число 101 это
              смещение в минус 40mV. Количество шагов в сторону положительного смещения 100, в сторону 
              отрицательного смещения шагов 99.

 Второй канал описывается точно таким же образом начиная со смещения 71 (число десятичное). 
С соответсвующим относительным смещением остальных данных.

 Настройка скорости развёртки для обоих каналов одинакова, и соответственно вынесена в отдельную ячейку.

 135(uint8_t) - число находящееся здесь определяет время развёртки. Число 9 соответствует
                времени развёртки в 2nS/div. Дальше всё идет по тому же принципу что и чувствительность 
                по вертикали. Т.е. всё идёт по ряду 1,2,5. Числу 10 соответствует время развёртки
                5nS/div, числу 11 соответсвует 10nS/div и так далее.

Блок самих 8 битных данных начинается со смещения 199. Если включены оба канала то начиная с этого смещения идёт блок самих
данных длиной в 32768 байт. Первая половина из них соответствует первому каналу, вторая второму. Если один из каналов выключен то блок данных
имеет длину 16384 байт.


Ниже картинки с самой програмки. Первая это картинка полученная из файла wfm от Rigol, вторая из файла waf от АКИП

rigol
акип

В самой программе можно прокручивать осциллограмму вправо/влево мышкой, удерживая нажатой левую кнопку мышки.
Можно прокручивать вверх/вниз — это делается колёсиком мыши, но для этого нужно активировать эту возможность нажав на кнопку со стрелками
«вверх-вниз» и выбрав нужный канал, или оба канала сразу.
Настройки по вертикальной чувствительности меняются в выпадающем меню. Это меню будет появлятся если нажать на правую кнопку мышки в области «экрана». Там же можно изменить парамметры скорости развёртки и скорость вертикальной прокрутки.
Можно сохранить картинку с «экрана» в формате bmp или jpeg.
  • +12
  • 12 июля 2013, 19:59
  • worker
  • 2
Файлы в топике: akipr.zip, waf_wfm_files.zip

Комментарии (146)

RSS свернуть / развернуть
Исходный код закрыт?
0
  • avatar
  • Vga
  • 12 июля 2013, 20:37
Если честно открывать его не хочу из за его кривости. Могу выложить часть в которой собственно происходит распарсивание. Но это будет только завтра.
0
ИМХО, лучше выложить полный. Заодно, возможно, в кривости ткнут и посоветуют как исправить.
На чем написано?
0
Писал на C++ Borland 6. Я не программист и поэтому код там соответствующий. Более менее причешу и может быть выставлю в полном виде.
0
А почему ты думаешь, что будучи не программистом сможешь причесать как следует?) Выложи как есть, какая разница?
0
Vga, угомонись.
Если и правда хочешь помочь, то в приват предложи.
А разводить бессмысленный оффтоп (а это уже оффтоп, поскольку автор парсер собирается выложить) уже хватит.
Я согласен, что исходники хорошо, но надо знать и меру.
+1
Да здесь не один ты такой, кто пишет кривой код, так что не переживай, а выложи — проще использовать за базу. Я хотел писать парсер, но для разборки различных протоколов, начиная с USB — полезная штука бы получилась.
+3
Неплохая программа получилась, работает просто и адекватно. Еще бы курсорные измерения в ней делать, но в целом понравилась. Буду пользоваться.
+1
Обнаружил косяк в алгоритме. То что касается Rigol всё нормально.
А вот с АКИП есть проблема. Я не нашёл в каком месте файла есть информация о частоте дискретизации и поэтому не факт что по времянкам осциллограмма будет правильно отображаться при различных сигналах и настройках самого скопа. Плюс к этому у меня в программе неверно отображается уровень самого сигнала. Шаг смещения в 40mV верен только для установки в 1V/div.
В общем если кто скачал программу имейте это в виду. Пока ищу как это всё исправить.
Исходники всё таки я решил выставить в полном виде. Если кто будет разбираться в этом «спагетти» коде прошу выносить сюда на обсуждение.
0
Плюс к этому то что касается АКИП я не нашёл в каком месте файла сохраняется информация о количестве записанных точек. Поэтому в программе я выделяю для этого постоянное количесвто памяти в 16384 байт на канал.
0
Сорцы — это хорошо.
Ну, для начала несколько наиболее заметных промахов (из которых существенен только первый):
1) Неудачная (но типичная для Delphi/C++Builder) архитектура — алгоритм работы и данные размазаны по обработчикам событий формы и ее полям. Лучше в той или иной мере придерживаться модели MVC, где форма с ее обработчиками — View, возможно — Controller. А вот Model лучше в любом случае выделить. В данном случае желательно некоторый базовый класс осциллограммы, предоставляющей необходимую метаинфу о себе в универсальном виде (типа там чувствительность в вольтах, развертка в секундах, etc) и собственно данные. Желательно подумать о том, что, возможно, потом будет добавляться поддержка новых осциллографов, у которых могут быть, например, другие варианты триггера, или другое число каналов, или другое разрешение АЦП (например, Nano DSO тоже умеет сохранять захваченный сигнал в свой формат, а разрешение у него 12 бит). Ну и в форме работать с осциллограммой только как с объектом этого класса. Это дает две возможности — во первых, теперь можно реализовать загрузчик более-менее любого формата в виде наследника этого класса, не трогая остальной код вообще (и возможно, даже в виде плагина), а во вторых — можно не трогая загрузчики реализовать возможность отображения любого количества осциллограмм одновременно, и возможно даже с разных осциллографов.
2) Сорцы желательно чистить от мусора перед выкладыванием. В данном случае это файлы *.obj (эти иногда бывают нужными, если программа линкуется с объектником, для которого нет исходника), *.ddp и *.tds. Из мусора бывают еще бэкапы (*.~*), компилируемые из *.rc-скриптов ресурсы *.res (дефолтный %projectname%.res — нужный, для него нет исходника), файлы десктопа (*.dsk, кажется), возможно — еще какие-то, с билдером я плохо знаком.
3) Весьма странная индентация. Выглядит как смешанная табо-пробельная на редакторе с другим размером таба, но при этом табов в файле нет. Видимо, изначально кривая.
0
Спасибо за разбор кода на таком системном уровне.
1) MVC это visual studio? С ним я не работал, если не считать hello world.
По поводу классов и ООП вообще, у меня мышление ещё только в стадии начального понимания. Я так думаю всё это
можно реализовать и на Builder-е. Но смысл понятен, надо было как то всё описать в классе и в событиях
вызывать методы этого класса.
2) Какие из файлов исходников за что отвечают я, пока что, тоже имею в общем то смутное представление.
3) А что такое идентация?
0
1) MVC — Model-View-Controller, один из архитектурных паттернов.
2) Ну дык, изучать инструмент надо) В данном случае .c/.cpp/.h/.hpp — собственно исходный код, .bpr — файл проекта, .dfm — файлы форм, .dpp — мусор какой-то, .obj — скомпилированные исходники, .tds — символы для отладчика, .res — скомпилированные ресурсы (в данном случае среда сразу формирует его в скомпилированном виде, этот файл нужен — в нем хранится иконка и versioninfo проекта).
3) Отступы кода от левого края.
+2
1. Буду вникать. :)
2. Так я ведь не программист. Назначение файлов .c/.cpp/.h/.hpp мне понятны. А вот с остальными пока что сложности.
3. Понятно. Я там в ручную выставлял отступы. В редакторе 6-го билдера нет автоматического сворачивания блоков кода и прочих вещей которые есть в более современных IDE.
У меня опыта в программировании в общем то нет. Поэтому такие вот вышли глупые вопросы.
И для ригола я писал код в течении одной недели, по вечерам. Для АКИП добавил код в течении одного дня.
+2
И для ригола я писал код в течении одной недели, по вечерам. Для АКИП добавил код в течении одного дня.
С точки зрения банальной эрудиции, это весьма неплохо для «не программиста».
+1
Тем не менее я не программист. И мне казалось что одна неделя для такого кода это много.
Да и в сети информации по программрованию что называется хоть залейся.
+1
Для простого viewer'а никакой модели MVC не нужно. Не забивайте голову лишним. Что это за совет по реализации MVC в C++ Builder 6 от человека, который «с билдером плохо знаком». Может сначала познакомиться, а потом советы колоссальных масштабов давать? И без собственных конкретных примеров.

Сама среда разработчика в этой версии убога, чтобы там поддерживать комплексы классов какой-то модели. У меня даже CnPack установлен, но и он не сильно улучшает картину, хотя с ним чувствуешь себя человеком.

Я бы посоветовал обратить внимание на структурированность кода, как уже было сказано, и на использование блоков try… catch() для отлова ошибок. Использование reinterpret_cast() не нужно при работе с файлами, отображаемыми в память. Можете посмотреть примеры кода для работы похожим образом тут: peviewer.cpp. Вообще, при таком способе чтения данных делают описание формата в виде структуры, потом создают указатель на структуру как переменную и этому указателю присваивают адрес в памяти. Это то, что у вас буковкой f обозначено. Это просто мучения смотреть на такой код, когда вы смещения вручную задаёте.

Вот, посмотрите как читается wav-файл описанным способом: Структура WAV файла.

Остальное всё нормально, разве что я бы рекомендовал научиться использовать компонент ActionList, в котором можно удобно собрать все обработчки, чтобы всё в одном месте было. Каждый компонент поддерживает выбор обработчика из этого списка.

Если вы с Win32API разобрались и можете файлы в память отображать, то уже много для начала, а всякие там MVC тут совершенно не нужны.
+2
Что это за совет по реализации MVC в C++ Builder 6 от человека, который «с билдером плохо знаком»
Ты еще скажи, что MVC вообще какое-то отношение к языку имеет, лол.
Сама среда разработчика в этой версии убога, чтобы там поддерживать комплексы классов какой-то модели.
Спасибо, посмешил.
Для простого viewer'а никакой модели MVC не нужно.
Угу, прекрасно. Да здравствует спагетти-код, где все смешано в кучу!
Автору: да, для начала оно неплохо, но это не значит, что не надо улучшать и не надо учиться дальше.

А насчет обработки ошибок и советов по чтению — это поддерживаю. Хотя сам бы я при таких размерах файлов с маппингом связываться не стал, но это особого значения не имеет.
+2
Использование reinterpret_cast() не нужно при работе с файлами, отображаемыми в память
У меня какая то ерунда получалась при обычном приведении типов, поэтому я и перешёл к
reinterpret_cast.
Это просто мучения смотреть на такой код, когда вы смещения вручную задаёте
Честно сказать меня тоже «ломало» от такого, но по другому сделать я
на тот момент не додумался.
я бы рекомендовал научиться использовать компонент ActionList
Я читал о нём, но пока не разобрался что к чему.
Если вы с Win32API разобрались и можете файлы в память отображать
Мне показалось что так удобнее работать с данными. И в принципе у меня сложилось такое ощущение
что лучше использовать WinAPI, чем функции самой среды. А код с отображением файла в память я практически целиком выдрал из книжки Рихтера по по программированию в Windows.
0
Смысл ActionList в группировке всех «конпочных» событий приложения в одном не визуальном компоненте:

ActionList

Это очень удобно, т.к. позволяет организованно работать с обработчиками событий интерфейса. Все команды как на ладони, можешь перейти к коду любой команды. Удобство в навигации по коду с его помощью. «Навешать» такой Action можно в любом компоненте, который совместим по параметрам.

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

Дело в том, что в C++ Builder нет необходимости так погружаться в Win32API, так как у него есть готовые «обёртки» для этого: TFileStream(). Насколько я понимаю, он должен работать через технику файлов отображаемых в память и нет необходимости вызывать CreateFile() и пр. напрямую.

Вот тут можно посмотреть пример работы с чтением данных при помощи TFileStream() и TMemoryStream(): UnitMain.cpp:

// Расшифровываем файл
    TMemoryStream * instream;

    Memo3->Lines->Add( "=========================" );
    Memo3->Lines->Add( "Расшифровка файла..." );

    // Создаём поток в памяти
    instream = new TMemoryStream;

    // Загружаем в него файл
    instream->LoadFromFile( Edit5->Text );

    try {

        // Создаём выходной файл
        TFileStream *DecFile = new TFileStream( Edit6->Text, fmCreate );

        try {

            // Осуществляем дешифровку файлового потока с записью выходного
            // потока сразу в файл
            Decrypt( ( TMemoryStream * ) DecFile, instream, Weights, q, 65279ui16 );
            
        }
        __finally {

            DecFile->Free();
        }

    }
    __finally {

        delete instream;
    }
0
я бы рекомендовал научиться использовать компонент ActionList
Я читал о нём, но пока не разобрался что к чему.
TAction и TActionList просты, как трусы без резинки, но при этом значительно упрощают жизнь:
1. Как уже упоминал камрад uni , TActionList позволяет собрать до кучи обработчики пользовательских действий, назначить этим действиям основные свойства, такие, как надпись, глиф, горячие клавиши вызова, всплывающая подсказка. Далее достаточно у конкретного контрола назначить свойство Action — все эти настройки будут автоматом применены к контролу.
2. Вытекающее из первого — конкретный Action может быть установлен в нескольких контролах одновременно, например пункте главного меню, пункте всплывающего меню и кнопке на панели инструментов. Достаточно изменить значение свойства в Action и это изменение отобразится во всех привязанных контролах.
3. С обработчиком TAction.OnExecute все понятно, вызывается, как правило, при событии OnClick привязанного контрола.
4. А теперь самая мякотка — событие TAction.OnUpdate. При каких обстоятельствах вызывается, я до конца не разобрался, если не ошибаюсь, вызов диспетчеризируется классом TApplication при переходе приложения в состояние ожидания пользовательского ввода. Позволяет изменять настройки конкретного экземпляра класса TAction (и соответственно привязанных к нему контролов) в зависимости от изменений, произошедших во время выполнения программы. Например можно сделать так(код на Паскале, пишу по памяти, поэтому могут быть небольшие ляпы, что, в общем, неважно):

  TMainForm = class(TForm);
    actSaveFile : TAction;
  ...
  private 
    FMyFileName : TFileName;
  ...
  public
    property MyFileName : TFileName read FMyFileName write FMyFileName;
  ...
  end;

  ...
  uses uMyRoutines;

  // Сохранение файла
  procedure TMainForm.actSaveFileUpdate(Sender : TObject);
  begin
    // Если не задано имя файла - запрещаем вызов процедуры сохранения
    TAction(Sender).Enabled := MyFileName <> '';
  end;

  procedure TMainForm.actSaveFileExecute(Sender : TObject);
  begin
    // Вызываем процедуру сохранения файла
    MySaveFileRoutine(MyFileName); // Процедура сохранения прописана в файле uMyRoutines
  end;


Всего две строки кода в двух обработчиках, и можно изменять MyFileName из любого места программы не заморачиваясь установкой свойства Enabled у контролов, вызывающих процедуру сохранения файла. Также можно без заморочек добавлять/удалять эти контролы не меняя ни одной строчки в коде.

Короче говоря, настоятельно рекомендуется к использованию. Единственное, что я бы не рекомендовал использовать, так это TActionManager. ЕМНИП, Borland/Codegear/Embarcadero так и не удосужились до конца его обезглючить.
+1
Ну, у автора, насколько я вижу, меню нет — только тулбар из нескольких кнопочек. Вряд ли в таком проекте оно реально надо.
А вот с более развитым интерфейсом приложения — там да.
0
Зато добавить, если вдруг захочется, раз плюнуть. Лучше об этом позаботиться заранее, чем потом переписывать кучу кода, тем более, что абсолютно ничего сложного тут нет. TAction — это базовый функционал VCL (не даром он находится во вкладке Standart), и игнорировать его возможности, ИМХО, не стоит.
+1
У него контекстное меню есть с вложенными пунктами, привязанное к окну графика.
0
Vga . Вы вот не постеснялись новичку тулить модель MVC, а куда более логичное и рациональное TActionList вдруг оказывается «вряд ли нужным».
Как расценивать Ваши комментарии? «Мое это супер нужное и необходимое, я даю только полезные и нужные всем советы, а все остальные предлагают всякую хрень»? Выглядит именно так.
Я Вас уже призывал охладить свой пыл, но Вы продолжаете нести какую-то фигню, не имея оснований. Конкретно в данной ветке Вам на самом деле следовало бы помолчать. uni и B-Screw дают дельные советы, основанные на опыте использования среды, и ваша личная неприязнь к кому либо из них не повод препятствовать распространению принятых подходов (свою личную неприязнь к uni я вроде не скрываю, но тут он прав).
-1
Vga. Вы вот не постеснялись новичку тулить модель MVC, а куда более логичное и рациональное TActionList вдруг оказывается «вряд ли нужным».
Я оказался неправ, B-Screw это понятно разъяснил. И я не говорил «а все остальные предлагают всякую хрень», только усомнился насчет нужности одного конкретного совета и унялся, получив краткий и внятный ответ.
uni и B-Screw дают дельные советы, основанные на опыте использования среды, и ваша личная неприязнь к кому либо из них не повод препятствовать распространению принятых подходов
Неприязнь неприязнью, но часть советов uni я проплюсовал. Так что мимо кассы.
0
Неприязнь неприязнью, но часть советов uni я проплюсовал. Так что мимо кассы.
Я вот тоже, но он почему-то по нулям :)
0
Это не ко мне, во всей теме я поставил только один минус (дополнив комментарием) и то оно в плюсе и осталось.
0
Для простого viewer'а никакой модели MVC не нужно. Не забивайте голову лишним.
Не соглашусь. Привыкать правильно проектировать создаваемое ПО, МИХО, нужно с самого начала, с самых первых, пусть и простых, проектов. Лишний юнит для процедур обработки, что отделить интерфейс от реализации, много места не займет, зато при его доводке перед глазами не будет пестрить код формы, да и правильно оформленный юнит можно без проблем включить в любой другой проект. Конечно, до фанатизма, как в «SAS Планета», доходить тоже не стоит.
0
Лучше пусть освоит ведение журнала ошибок и то польза будет, на чём бы не программировал, а давать советы и не показывать пример — вот, имхо, не очень хорошо. Если я вот что советую, то беру пример из своей практики и даю ссылку на конкретное место, где это можно посмотреть. Либо даю ссылку на исходник, где я видел как это делается. А писать — «пиши правильно и реализуй MVC» — и при этом ничего не показывать реального, это бесполезный совет. Людям, которое в глаза этого не видели, нельзя словами объяснить и нужно показывать на примере, а дойдёт только, когда потребность возникнет.

Вот он прочитал у Рихтера про файлы отображаемые в память и применил, хотя у Рихтера рассказывалось про общий случай использования Win32API без указания на применение конкретной среды разработки. Если бы читалась книжка по BCB6, то он бы узнал, что существует обёртка Win32API для работы с файлами как потоками и не нужно знать кучу параметров CreaeteFile() и пр. Вот я ему показал, он подумает что проще в следующий раз использовать.
0
А какой тут пример можно привести? Все просто — форма, интерфейсная требуха, в том числе и экземпляры TAction, идут в один файл, реализация функционала (в данном случае, например, парсеры) в другой. В своем примере, кстати, я намеренно добавил
uses uMyRoutines;
...
MySaveFileRoutine(MyFileName); // Процедура сохранения прописана в файле uMyRoutines

как простейший пример отделения мух от котлет. Так что Ваш упрек немного мимо кассы. :)
Конечно, пример на паскале тут не совсем уместен, но я еще не достаточно свободно изъясняюсь на С.
И еще одна мысль по поводу
Для простого viewer'а никакой модели MVC не нужно. Не забивайте голову лишним.
ИМХО, реализовав один из первых проектов, пусть этот проект совсем простой (это даже лучше, если он будет простым), по всем канонам, пользуясь советами и подсказками более опытных людей, использовав весь доступный арсенал, т.е. доведя этот проект до совершенства, начинающий программист получит отличный старт для дальнейшего развития и опыт, ИМХО, более ценный, чем чтение умных книг. А уже потом, когда он на практике будет понимать о чем идет речь, брать умные книги и полировать полученные знания.
В этом проекте есть где развернуться — парсеры можно реализовать в виде классов с наследованием от базового класса для конкретных протоколов, окно графика — в виде компонента и т.д. А поскольку проект небольшой, то и времени это займет немного. Потом как следует откомментировать исходники и можно все это использовать как руководство для начинающих.
Дополнительный плюс для топикстартера — как он сам писал, он еще не программист, т.е. ломать неверные стереотипы ему будет гораздо проще.
Если бы у меня в свое время была такая возможность, я бы ее обязательно использовал. А то пишу на Delphi уже более 10 лет, а избавляться от спагетти-кода в своей писанине начал приблизительно с даты моей регистрации в сообществе. Почитал здешние холивары и начал избавляться. :) Просто обработчик события OnClick оказывает разрушительное воздействие на умы начинающих ООП программеров. Мне от последствий этой разрухи пришлось избавляться долго и нудно. А все потому, что некому было ткнуть носом.
+2
окно графика — в виде компонента
Кстати, а билдер позволяет писать компоненты на С++? Сколько видел компонентов, ЕМНИП они все на Delphi написаны, да и VCL достаточно сильно завязана на возможности языка.
Но если сделать — то это будет еще и выделение части View, а в форме останется только контроллер.
Просто обработчик события OnClick оказывает разрушительное воздействие на умы начинающих ООП программеров.
О да. Правда, такой стиль довольно удобен для того, чтобы быстро набыдлокодить небольшую утилитку. Правда, она при этом станет неразвиваемой.
0
Слушайте, ребят, вы какие-то странные. Оба любителя паскаля, оба похоже не открывали проекта в BCB6 и не смотрели его, оба слабо разбираются в C++, оба не привели ни строчки C++ кода и оба любители покласть на это всё, давать советы и упоминать какую-то кассу. Что ещё за касса? Нафик она тут нужна? Вы код покажите.

Знаете чем Рихтер с его книжками так читаем? Он не ходит вокруг да около, а приводит код и поясняет как он работает с подробностями. Если бы он мусолил из пустого в порожнее, то никто бы его не читал.

«Мимо кассы, мимо кассы», а вы оба мимо кода проходите, ибо разделение функционала декодера от интерфейса есть в указанной мной консольной утилите по чтению PE-файлов: class CPEViewer {}. Поскольку утилита консольная, то там более понятно как и что отделено. Вообще, если код декодера можно просто использовать в консольной утилите, то можно сказать, что функционал отделён от интерфейса. Вот я бы и порекомендовал автору попробовать написать класс для такой же консольной утилиты, который бы показывал структуру декодируемого файла. После этого он поймёт почему это удобно с точки зрения структуры кода и его повторного использования.

Если мне нужно показать структуру PE-файла, то я делаю это вызывая методы класса-просмотрщика:

int _tmain( int argc, _TCHAR * argv[] ) {

    CPEViewer * pPEViewer = NULL;

    // Устанавливаем русскую локаль
    setlocale( LC_ALL, "Russian" );   

    try {
    
        // Проверяем количество аргументов командной строки
        if ( argc < 2 ) throw Exception( ERROR_FILE_NOT_SPECIFIED );

        // Вывод структуры PE-файла
        if ( argc == 2 ) {

            // Проверяем корректность входного файла
            if ( !FileIsCorrect( argv[1] ) ) throw Exception( ERROR_FILE_NOT_CORRECT );

            // Создаём экземпляр для работы с просмотрщиком PE-файла
            pPEViewer = new CPEViewer();

            pPEViewer->Initialize( argv[1] );

            // Показываем DOS заголовок
            cout << endl << "Показываем DOS заголовок" << endl << endl;
        
            pPEViewer->ShowImageDosHeader();

            // Показываем заголовок PE-файла
            cout << endl << "Показываем заголовок PE-файла" << endl << endl;

            pPEViewer->ShowImagePEHeader();

            // Показываем таблицу секций
            cout << endl << "Показываем таблицу секций" << endl << endl;

            pPEViewer->ShowSectionTable();
            
            // Показываем секции
            cout << endl << "Показываем секции" << endl << endl;

            // Сортировка секций по виртуальным адресам
            vSort( vTypeRva );

            pPEViewer->ShowSections();

            // Показываем директории
            cout << endl << "Показываем директории" << endl << endl;

            pPEViewer->ShowDirectories();

            // Завершаем работу с просмотрщиком PE-файла
            delete pPEViewer;

Вы бы лучше ему про SEH рассказали и показали как он используется. У меня в просмотрщике и это можно увидеть. Свой собственный класс обработки ошибок и их перечисление:
// Класс, реализующий работу с ошибками
class Exception {

    private:

        EnError ErrorCode;

    public:

        Exception( EnError ErrorCode ) { this->ErrorCode = ErrorCode; }
        EnError GetError() { return ErrorCode; }

};

} catch ( Exception e ) {

        switch ( e.GetError() ) {
        
            case ERROR_UNKNOWN: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Неизвестная ошибка.\n" ) );
                break;

            }

            case ERROR_INVALID_HANDLE_VALUE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Ошибка при работе с файлом: INVALID_HANDLE_VALUE\n" ) );
                break;

            }

            case ERROR_INVALID_SIZE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Ошибка при работе с файлом: ERROR_INVALID_SIZE\n" ) );
                break;

            }

            case ERROR_INVALID_ZERO_SIZE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Ошибка при работе с файлом: ERROR_INVALID_ZERO_SIZE\n" ) );
                break;

            }

            case ERROR_INVALID_SMALL_SIZE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Ошибка при работе с файлом: ERROR_INVALID_SMALL_SIZE\n" ) );
                wprintf( _T( "Размер файла (%d) меньше, чем ( sizeof( IMAGE_DOS_HEADER ) + sizeof( IMAGE_NT_HEADERS ) ).\n" ), ddFileSize.QuadPart );
                break;

            }

            case ERROR_FILE_MAPPING: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Ошибка при отображении файла в память: ERROR_FILE_MAPPING\n" ) );
                break;

            }

            case ERROR_DOS_SIGNATURE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Файл не является исполняемым файлом DOS.\n" ) );
                break;

            }

            case ERROR_FILE_ADDR_RELOC_TABLE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Значение поля e_lfarlc меньше 0x40.\n" ) );
                break;

            }

            case ERROR_PE_SIGNATURE: {

                wprintf( _T( "[CPEViewer::FileIsCorrect] Файл не является исполняемым PE-файлом.\n" ) );
                break;

            }

            default:;

        }

Также можно делать и в BCB6, но у него опять же есть обёртки и там это выглядит чуть-чуть по-другому. Человек даже не знает как структуры можно использовать для их наложения в качестве маски на память, а вы ему какой-то MVC предлагаете. Как он его писать будет вы подумали? Я использую готовые структуры, которые описаны в SDK Windows, а ему нужно сначала посидеть ещё неделю и покумекать над описанием структур, потом над видом класса и его частей, а также над централизованной обработке ошибок при помощи SEH. Он месяц будет свою утилиту писать, если не дольше.

Если вы до этого 10 лет доходили, то почему он за неделю освоит? Ведь проблема не в используемых конструкциях языка, которых он тоже не знает как использовать, а в том, что он не видел конкретных примеров как сделано правильно и как это могло бы быть сделано удобнее. Вы только разглагольствуете об этом и не привносите никакого понимания. А это нужно «мозгами потрогать» нечто конкретное.

Туристы.

Раз такие «спецы», то напишите класс на паскале, который делает чтение этих типов файлов удобным без GUI, просто сам функционал. Я покажу как использовать ваш код на паскале в BCB6, он может такое, порою это очень удобно, как, например, при использовании известного класса RegExpr, который написан на паскале. Я его использую в BCB6 без проблем.
0
Оба любителя паскаля, оба похоже не открывали проекта в BCB6 и не смотрели его, оба слабо разбираются в C++, оба не привели ни строчки C++ кода и оба любители покласть на это всё, давать советы и упоминать какую-то кассу.
Да, я еще не разбирал проект по косточкам, глянул только на общую структуру — именно поэтому не даю советов по чтению при помощи структур и подобным вещам. Что до остального — я не даю никаких language-specific советов.
Знаете чем Рихтер с его книжками так читаем? Он не ходит вокруг да около, а приводит код и поясняет как он работает с подробностями.
Я пишу комментарий, а не книжку. И я уже забыл, что именно непонятно новичкам. Так что пусть спрашивает то, что непонятно в моих комментариях, охотно поясню или проиллюстрирую кодом.
Человек даже не знает как структуры можно использовать для их наложения в качестве маски на память, а вы ему какой-то MVC предлагаете.
Ты так говоришь, как будто MVC сложнее чем чтение файла приведением к структуре. Или как будто это взаимоисключающие вещи, а меж тем желательно сделать и то, и то. Ну и оба момента корнями уходят в то, что автор — начинающий программист и не знаком с типовыми решениями.
Если вспомнить претензии angel5a, то это как раз у тебя «Мое это супер нужное и необходимое, я даю только полезные и нужные всем советы, а все остальные предлагают всякую хрень».
Если вы до этого 10 лет доходили, то почему он за неделю освоит?
Потому, что я читал книги вроде Рихтера и некому было ткнуть меня в MVC и вообще вопросы проектирования архитектуры. Или хотя бы сказать «размазывать логику по обработчикам зло» — вместо этого везде примеры, где логика по ним и размазана.
Ведь проблема не в используемых конструкциях языка, которых он тоже не знает как использовать, а в том, что он не видел конкретных примеров как сделано правильно и как это могло бы быть сделано удобнее.
Нет, проблема в том, что начинающий программист даже не знает, что можно сделать иначе. А ведь можно сразу ткнуть в базовые принципы проектирования, достаточно простые и достаточно понятно описанные в той же википедии, которые позволяют писать не сложнее, но понятнее.
Я покажу как использовать ваш код на паскале в BCB6, он может такое
Еще бы, учитывая что BCB — это Borland C+ с прикрученной Delphi VCL.
Раз такие «спецы», то напишите класс на паскале, который делает чтение этих типов файлов удобным без GUI, просто сам функционал.
Да я и весь вьювер на Delphi за пару дней напишу, но будет ли с того польза автору?

А вот на адресовавшийся тебе вопрос «можно ли в билдере делать компоненты на С++» ты так и не ответил.
-1
Почему «адресовавшейся мне вопрос» адресовался не мне, а был ответом B-Screw?

«можно ли в билдере делать компоненты на С++»
Господи, установи наконец BCB6 и загляни в папку: «C:\Program Files\Borland\CBuilder6\Examples\Controls\Source»

Если тебе и это лень делать, то сходи в Google: Как создать компонент.

Что до остального — я не даю никаких language-specific советов.
Теперь ясно почему.
0
Теперь ясно почему.
Разве я когда-то скрывал, что я дельфист, а не плюсовик? Кроме того, из всего семейства С билдер меня интересует меньше всего, т.к. для его задач есть Delphi.
Господи, установи наконец BCB6 и загляни в папку: «C:\Program Files\Borland\CBuilder6\Examples\Controls\Source»
Зачем ставить ненужную программу, если можно спросить у того, кто знает? В конце концов, для этого сообщества и придуманы.
0
Просто обработчик события OnClick оказывает разрушительное воздействие на умы начинающих ООП программеров. Мне от последствий этой разрухи пришлось избавляться долго и нудно. А все потому, что некому было ткнуть носом.
Можешь пояснить про разрушительное воздействие OnClick?
Пока что, для меня это непонятно.
В этом проекте есть где развернуться — парсеры можно реализовать в виде классов с наследованием от базового класса для конкретных протоколов, окно графика — в виде компонента и т.д.
Пытаюсь думать над этим, но отвязать сам парсер от построения графика, не могу пока понять как это сделать. Т.е. как это всё реализовать в виде классов, которые смогут как то взаимодействовать между собой.
0
Можешь пояснить про разрушительное воздействие OnClick?
На этих обработчиках удобно строить программу в самой примитивной парадигме — толкаем все глобальные переменные в форму, весь код в обработчики, получаем работающую программу. Это приводит к тому, что все решаемые программой задачи оказываются перемешаны и размазаны по обработчикам. Вот только такой код становится сложно контролировать по мере разрастания, проблемы начинаются уже в районе единиц килострок.
Вот, например TForm1::Open_btnClick. Часть кода вынесена в методы AkipFile и RigolFile, но по сути они остаются кусками этого обработчика — например потому, что имя файла берут из OpenDialog'а, куда корректное имя попадает только после выполнения Open_btnClick. Плюс между ними есть общий код, который сбрасывает настройки рисования.
Меж тем, в общей задаче «вьювер waveform-файлов» можно выделить несколько подзадач: загрузка файлов разных форматов, отрисовка загруженного файла, интерфейс пользователя. Предположим, что проект разбит на модули, соответсвующие этим задачам, тогда обработчик мог бы выглядеть так:
void __fastcall TForm1::Open_btnClick(TObject *Sender)
{
  if (OpenDialog1->Execute())
  {
    WaveformLoader->Load(OpenDialog1->FileName);
    WaveformPainter->ResetSettings();
    WafeformPainter->Refresh();
  }
}

А код загрузки не содержал бы кусков кода, связанных с отрисовкой, которые приходится копипастить между лоадерами (а в случае модификации отрисовщика — править раскопипащенные куски во всех лоадерах).
+2
Спасибо. Т.е. к примеру, можно создать класс который содержит метод Load. Это будет класс загружающий данные. Этот класс должен работать по разному с разным набором входных данных и приводить эти данные к виду понятному рисовалке. И плюс создать класс который уже непосредственно отрисовкой занимается, получая данные из класса загрузчика.
В голове что то начинает прояснятся. )
0
Пытаюсь думать над этим, но отвязать сам парсер от построения графика, не могу пока понять как это сделать. Т.е. как это всё реализовать в виде классов, которые смогут как то взаимодействовать между собой.
Класс, сам по себе — всего лишь средство собрать воедино функции и относящиеся к ним данные и ограничить доступ к потрохам извне.
Прежде всего нужно определиться с общей структурой классов — какие классы, что делают, как взаимодействуют. Например, в твоем случае можно выделить форму, которая обслуживает UI и управляет остальной часть программы, вьювер, который рисует данные осциллограммы и осциллограмма, которая умеет загружаться из файла.
Начнем с осциллограммы. Она должна уметь грузиться из файла и предоставлять все необходимые для отрисовки данные. Если взглянуть на форматы файлов осциллографов, то видно, что они предоставляют массив точек, необходимую информацию о нем (количество точек, разрешение, масштабы по X и Y) и дополнительную информацию о настройках (настройки триггера и так далее). Теперь нужно определиться, что из этого реально требуется для отрисовки. Допустим, что рисовать будем только саму осциллограмму, а дополнительную инфу просто выкинем текстом. Получаем интерфейс класса (я приведу на Delphi, но вообще-то, на билдер оно переписывается практически 1 в 1 по несложным правилам):
TWaveform = class
  public
    constructor Create;
    destructor Destroy; override;
    function Load(Stream: TStream); virtual; abstract;
    property Points[Index: Integer]: Integer read GetPoint;
    property PointsCount: Integer read GetPointCount;
    property VoltsPerDiv: Single read FVoltsPerDiv;
    property VoltsPerCount: Single read FVoltsPerCount;
    property SecsPerDiv: Double read FSecsPerDiv;
    property SecsPerPoint: Double read FSecsPerPoint;
    property Metainfo: string read FMetainfo;
  private
    FPoints: array of Integer;
    FVoltsPerDiv, FVoltsPerCount: Single;
    FSecsPerDiv, FSecsPerPoint: Double;
    FMetainfo: string;
    function GetPoint(Index: Integer): Integer;
    function GetPointsCount: Integer;
end

Такой класс содержит все необходимое для рисования осциллограммы в некотором унифицированном виде. Теперь остается только создать его наследников TWFMWaveform = class(TWaveform), TWAFWaveform = class(TWaveform) и перекрыть в них метод Load, который будет читать из потока данные файла, парсить их и складывать в поля FPoints, FVoltsPerDiv, FVoltsPerCount, FSecsPerDiv, FSecsPerPoint, FMetainfo — так же, как сейчас в поля формы data, CG1, CH2, time_scale, etc. При этом он не должен обращаться ни к форме, ни к чему-либо еще (ну, кроме библиотечных функций, разумеется), работая только с полученным Stream и полями класса. Этим достигается отсутствие зависимостей — теперь этот класс можно выдернуть, воткнуть в совершенно другой проект, создать, скормить ему поток с данными и получить от него распарсенные данные.
+2
Далее, рисовалка. Как уже отмечено, есть смысл оформить ее компонентом, uni привел ссылку на то, как это делается для BCB.
На сей раз следует определиться, от чего наследоваться. Поскольку это довольно похожий по функциям на TImage контрол, который тоже что-то рисует — то вполне разумным вариантом предка будет TGraphicControl. На сей раз большая часть интерфейса диктуется предком — что нужно перекрыть, чтобы оно работало как требуется. Но кроме этого — контрол должен знать, что ему вообще рисовать надо. Поэтому ему в свойства попадают все настройки рисования — шаг сетки, положения курсоров, значения надписей, ну и, конечно же, TWaveform, который он рисует. Есть смысл сразу предусмотреть возможность отрисовки нескольких вейвформов.
По желанию можно часть управления (скажем, обработку колеса мышки и тычков по осциллограмме) сделать в этом же контроле, а можно оставить на форму. Лучше, пожалуй, второе.

Еще стоит заметить, что в предыдущем комментарии я забыл о том, что в файле содержатся данные нескольких каналов. Ко всем имеющимся там свойствам нужно добавить дополнительный индекс — номер канала, плюс новое свойство — число каналов. Будет примерно так:

    property ChannelsCount: Integer read GetChannelsCount;
    property Points[Channel, Index: Integer]: Integer read GetPoint;
    property PointsCount[Channel: Integer]: Integer read GetPointCount; //можно оставить невекторным, если предположить, что количество точек во всех каналах одинаковое - даже если это в каком-то формате окажется не так, слишком короткие каналы можно добить нулями
    property VoltsPerDiv[Channel: Integer]: Single read GetVoltsPerDiv;
    property VoltsPerCount[Channel: Integer]: Single read GetVoltsPerCount;
    property SecsPerDiv[Channel: Integer]: Double read GetSecsPerDiv;
    property SecsPerPoint[Channel: Integer]: Double read GetSecsPerPoint;
    property Metainfo[Channel: Integer]: string read GetMetainfo;
+2
Есть еще одна нерешенная проблема. Теперь каждый тип waveform'а умеет загрузить себя из файла (точнее, из потока — для загрузки из файла туда просто нужно передать TFileStream с соответствующим файлом), но нужно еще как-то определить, какой конкретно наследник TWaveform требуется создавать и загружать.
Можно сделать это в форме:
if (OpenDialog1->Execute())
  {
    if (lowercase(ExtractFileExt(OpenDialog1->FileName))==".wfm")
       Waveform = new TWFMWaveform;
    if (lowercase(ExtractFileExt(OpenDialog1->FileName))==".waf")
       Waveform = new TWAFWaveform;
    ...
    Wafeform->Load(OpenDialog1->FileName); //для этого можно перегрузить метод TWaveform.Load, добавив вариант, который принимает строку с именем файла, создает TFileStream и вызывает Load(TStream), передав ему созданный TFileStream (не забудь прибить в конце поток и гарантировать его прибитие при помощи try..finally)
    WaveformPainter->Refresh();
  }

Но у данного вариант есть недостаток — форма должна знать о всех существующих наследниках TWaveform и какие форматы файлов они могут грузить — т.е. для добавления нового формата нужно не только создать новый наследник TWaveform, но и добавить новый if в LoadBtnClick(). В проекте такого размера это хоть и допустимо, но в целом некошерно.
Решение этой задачи известно как IoD (Inversion of Dependency)/IoC (Inversion of Control, подробности — в вики. Один из вариантов реализации этой концепции демонстрирует сама VCL — вместо вписывания в IDE нового типа контрола и ее перекомпиляции при установке каждого нового контрола (ха-ха) контрол компилируется в отдельную библиотечку и при ее загрузке вызывается функция Register, которая вносит имеющиеся в библиотечке контролы в список доступных контролов где-то в потрохах IDE.
Здесь можно сделать аналогично — создать список расширений и соответствующих им классов-загрузчиков, и в секции initialization модуля с загрузчиком добавить вызов функции, добавляющей реализованный в этом модуле загрузчик в список.
0
Ну и еще на тему IoC. В проектировании есть такое понятие, как SOLID. Его тоже желательно осмыслить, это пять наиболее важных при проектировании ОО-архитектуры принципов (впрочем, я считаю, что все или почти все из них полезны не только в ООП). Хотя какие-то из них могут быть поначалу непонятными, но в принципе они все простые и следовать им несложно.
0
Но у данного вариант есть недостаток — форма должна знать о всех существующих наследниках TWaveform и какие форматы файлов они могут грузить — т.е. для добавления нового формата нужно не только создать новый наследник TWaveform, но и добавить новый if в LoadBtnClick(). В проекте такого размера это хоть и допустимо, но в целом некошерно
А вот тут уже стоит остановиться. Если не планируется делать программу модульной, то в монолитном приложении все равно классы-наследники придется перечислять, в форме ли, или городить класс-перечислитель при базовом классе парсера, но придется. Если плагины реализовывать, то да, огород городить придется, но плагины в такой задаче оправданы только в случае, если планируется создать открытый API и дать возможность лепить новые парсеры сторонним разработчикам. В случае же монолитного приложения, польза от такого оверхэда будет исключительно академической, но ни как не практической в плане развития проекта.
Как говорят в Украине: «Що занадто, то не здраво». Такими темпами и до интерфейсов договориться можно :)
0
в монолитном приложении все равно классы-наследники придется перечислять, в форме ли, или городить класс-перечислитель при базовом классе парсера, но придется.
Задача здесь — составлять этот список автоматически и минимизировать количество точек изменения кода при добавлении формата.
Но в целом ты прав, такому проекту это уже нужно только если делать поддержку плагинов (что, в принципе, тоже полезно — плагин проще написать, чем впилить новый класс в программу — не требуется поднимать среду, в которой она скомпилится, можно писать на практически любом языке).
0
Ключевые слова: «Открытый API» и «Сторонние разработчики». В остальных случаях данному проекту плагины нужны, как козе баян.
Впилить новый класс в проект из одной формы куда проще, чем впилить туда же поддержку плагинов. Если уж это так принципиально, то куда логичнее в данном случае форматозависимый код повесить на какой нибудь скриптовый движок, а скрипты хранить можно где угодно. Добавил скрипт, а приложение вообще не трогаешь.
+1
Поддержка плагинов — это весьма просто, у меня есть программка вдвое меньшего размера (~1-1.3 kloc) с поддержкой плагинов. Зато позволяет скачавшему запилить плагин под свой осциллограф, не устанавливая BCB6, в любимой среде программирования.
0
property
Это что то чисто борландовское? В описаниях языка С++ я этого не видел.
Мне пока хватит наверное информации для переваривания.
0
Это вообще дельфовое. Property (свойство) — это член класса, который выглядит как поле (переменная), но позволяет реализовывать доступ к себе через методы-акцессоры. В C++ обычно свойства реализуются в виде пары акцессоров (int pointsCount(), void setPointsCount(int Count)), но в билдере для его поддержки введено ключевое слово __property. Кроме методов, поле может ссылаться на переменную — скажем, не редкость код вида property SomeProperty: Integer read FSomeProperty write SetSomeProperty. В этом варианте данные читаются напрямую из скрытого в private поля FSomeProperty, но при записи выполняются дополнительные действия — скажем, обновляются внутренние данные, которые зависят от значения этого поля, либо просто проверяется корректность переданного значения.
0
Чтобы использовать то, что VGA пытается представить как оно должно быть, нужно иметь опыт в частных задачах, которые будут представлять из себя тело указанных методов. Чтобы что-то «обёртывать», ты должен понимать как вообще это работает без обёртывания. Если таких навыков нет, то будешь 150 раз переделать «обёртыватель» вместо того, чтобы написать что-то рабочее по типу вот этого (RS2009, C++). Используется библиотека GDI+ с родными заголовочными файлами. Рисуется всё на PaintBox'е, т.к. только у этого визуального компонента есть обработчик OnPaint(), который позволяет рисовать в реальном времени. Писать компоненты не надо, ибо нормально написанный компонент для рисования графиков очень сложен. Я имею представление, т.к. видел как надо писать такие вещи. Даже имея исходники в двух экземплярах, правда на C#, я пока не нашёл в себе смелости применить их в виде компонента для Delphi или BCB, ибо это отдельная задача. Там очень много неочевидных тонкостей.

GDI+ была выбрана специально, т.к. там есть всё что нужно не только для рисования, но и для красивого рисования (на рис. видно, что все графики сглаженные).

Сигнал 1

Сигнал 2

Спектр пачки радиоимпульсов
0
Но навыки-то где-то набивать надо. Благо программа уже есть и работает, давая представление о частных задачах. Теперь это вполне можно подвергнуть рефакторингу, при желании. А так оно и без MVC обходится, и без приведения отмапленных файлов к структурам.
Насчет компонента — да, надобности в том нет, еще и устанавливать лишний компонент… С другой стороны, для экспериментирования с их созданием вполне пойдет.
Писать компоненты не надо, ибо нормально написанный компонент для рисования графиков очень сложен.
Не обязательно ж сразу пытаться написать ZedGraph-killer, достаточно простенькую рисовалку осциллограмм.
+1
Вот зачем так всё усложнять? Зачем столько полей, дублирующих друг друга? Пусть напишет две структуры а-ля WAVHEADER и никаких куч полей не нужно. Достаточно всего одного — указателя на эту структуру. Все эти файлы — это упрощённые варианты WAV-структуры. Какой смысл изобретать велосипед? Вот же ссылку даже дал как это выглядит: Структура, описывающая заголовок WAV файла.

В классе-обёртке нужно лишь иметь ссылку на эту структуру в памяти и больше ничего не надо. Полей структуры будет достаточно для рисования. Даже можно с самим классом не заморачиваться. Достаточно простой функции DrawGraph() с параметрами области просмотра, а всё, остальное доставать прямо из памяти по указателю. Мутить какой-то MVC ради MVC нету смысла. Код для рисования правильных графиков и то больше получится, чем работа с самим файлом данных.
0
Зачем вводить в класс структуру, когда класс сам по себе и есть структура?
Зачем столько полей, дублирующих друг друга?
В структуре будут ровно те же поля. Ну, два варианта VoltsPer могут и быть лишними, но у разных осциллографов может быть разное количество отсчетов на деление, так что *PerCount нужнее, *PerDiv — информационные, можно перенести в метаинфо.
Все эти файлы — это упрощённые варианты WAV-структуры. Какой смысл изобретать велосипед?
Почему бы тебе не спросить самих китайцев, зачем они выдумали новый формат? Сохраняли бы в WAV…
Даже можно с самим классом не заморачиваться. Достаточно простой функции DrawGraph() с параметрами области просмотра, а всё, остальное доставать прямо из памяти по указателю.
Можно. Но DrawGraph — это тот самый View из MVC. А структурка с функциями ее загрузки из файлов — Model. В итоге получается все тот же MVC — просто потому, что такого рода задачу удобно разделить на структуру данных, ее загрузчик и ее рисовальщик.
+1
P.S. Прежде чем говорить про усложнение — глянь сколько полей для данных осциллограммы у автора в форме. Я их далеко не все перечислил сообщением выше.
поля формы data, CH1, CH2, time_scale, etc
0
У wav-заголовочника полей не меньше, также как и каналов :) Я имел в виду про дублирование, что нужно делать скрытые поля и свойства для обращения к ним. Вот зачем это надо? ООП ради ООП?
0
Правила хорошего тона. Они практически ничего не стоят — чуть-чуть больше писанины, но это мелочи.
У wav-заголовочника полей не меньше, также как и каналов :)
Только нету специфических для осциллографа полей и есть куча ненужных (WAV вообще говоря контейнерный формат и может содержать отнюдь не waveform, а, например, ее произвольный сжатый вариант).
Так что лучше уж сделать структуру, соответствующую осциллографам и имеющую только требуемые поля.
0
Там ещё дело в том что сам набор данных по настройкам осциллографа идёт не последовательно по ячейкам. Если оформлять в виде структуры тогда наверное придётся для выравниния вставлять какие-то переменные пустышки в саму структуру.
Но смысл понятен.
0
Такая структура, которая соответствует формату файла — формато-специфична, соответсвенно, данные из нее в любом случае придется переносить в выходную структуру (либо класс) общего формата.
А для чтения из файла она полезна, дырки действительно закрываются пустышками. И не забудь, что подобные «форматные» структуры должны быть упакованы (выровнены по адресам, кратным 1).
0
Ну вот, пришел Vga и все рассказал.
Можешь пояснить про разрушительное воздействие OnClick?
С точки зрения начинающего программиста, слабо знакомого с принципами ООП, самым логичным кажется поместить код, который должен выполниться при нажатии на Button1, в обработчик события Button1.OnClick, а не городить оверхед в виде Actions, классов, помещенных в другие файлы процедур. Вроде все просто и логично — есть кнопка, есть обработчик ее нажатия. Вот только подход этот не создаст геморроя только в случае, если на форме будет одна единственная кнопка, а в ее обработчике — ShowMessage('Hello World!');. А даже Ваш не сложный проект уже заслуживает более углубленного проектирования. Зачем? Хотя бы для быстрого и удобного добавления новых форматов.
отвязать сам парсер от построения графика, не могу пока понять как это сделать.
Все очень просто. В конкретном случае Вам нужно на стадии проектирования парсера забыть о построении графика. Ничего не рисуем, а, скажем, будем выводить расшифрованные данные в файл. Или сразу в Excel. Или, все таки, рисовать будем. А еще лучше — все сразу. Вот тут и вырисовывается круг задач, решением которых должен озадачиться парсер:
1. Загрузить файл;
2. Выдать результирующий массив данных, формат которых будет понятен и рисовалке, и процедуре выгрузки в файл, и процедуре выгрузки в Excel. И вот когда мы увидим в выгруженном файле нужные нам данные, тогда можно приступать к реализации рисовалки.

Кстати, если Вас не затруднит, выложите примеры исходных файлов, причем чем больше, тем лучше. А то у меня ни одного из перечисленных осцилов нет, а поковыряться хочется.
+2
Выдать результирующий массив данных, формат которых будет понятен и рисовалке, и процедуре выгрузки в файл, и процедуре выгрузки в Excel.
Стоит еще отметить, что выходной формат должен быть общим для всех вариантов входных файлов (и соответсвенно, должен быть способен адекватно представить данные из WMF, WAF, а в перспективе — DAV, XML (NanoDSO), etc). Это довольно очевидно, но, все же, ты новичку рассказываешь.
Кстати, если Вас не затруднит, выложите примеры исходных файлов, причем чем больше, тем лучше.
Вот сэмпл в WFM и еще паре неподдерживаемых форматов.
0
Вот сэмпл в WFM и еще паре неподдерживаемых форматов.
Дополнительные форматы — это круто. Спасибо.
0
DAV — ATTEN, XML — DSO Nano.
0
Приложил файл waf_wfm_files.zip с примерами осциллограм.
0
Спасибо. Пороюсь вечером.
0
И про ООП вообще. Классы в книжках вроде бы описываются просто. Есть обёртка какая то
с секциями private, protected, public. Но как к примеру описать с помощью всего этого к примеру машину (как пишут в книжках) я пока не очень понимаю. Т.е. у машины есть руль, фары, педали газа и тормоза, двигатель, колёса… Мне непонятно как это всё можно описать в виде класса, так чтобы это всё програмно выглядело именно как машина а не набор всех этих деталей. В общем пока пытаюсь мышление свое перестроить в этом направлении. Абстрактное мышление тут нужно как то развить. )
0
И про ООП вообще… Но как к примеру описать с помощью всего этого к примеру машину (как пишут в книжках)

Рассмотри другую аналогию. Между классом и ящиком с инструментами(Toolbox).В ящике(классе) много инструментов(функций, методов класса), каждый инструмент предназначен для выполнения своей работы. Есть инструменты(функции) которые ты предоставишь(public) соседу для работы, а есть инструмент(функции и переменные) которым ты будешь пользоваться только сам(privat).
Получится примерно так:
class Toolbox
{
	privat:
		Payalnik();
		  Tester();
	public:
	        Toolbox();
		Ruletka();
};

Классы в книжках вроде бы описываются просто
Практика обязательна. И не забывай про точку с запятой после закрывающей скобки при объявлении класса )
0
И да, кстати, если вы ещё не совсем освоились в IDE C++ Builder 6 и начинаете её осваивать, то рекомендую сразу перейти, например, на Rad Studio 2009 или XE3. Ничего страшного нет, всё почти то же самое, только вид сбоку. Ваш проект там запускается после 4-х не сложных правок, связанных с приведением типа. К RS2009 тоже можно приделать специальные дополнения: GExperts и CnPack (нужно поискать в сети подходящие версии, они облегчают жизнь программисту). После этого можно замахнуться и на Вильяма, на нашего, Шекспира.

CodeGear Rad Studio 2009
+1
Установил я RAD. Какая то громоздкая она, эта IDE. Тяжело выглядит. Неуютная в общем.
И если честно, то, плюса от перехода на неё я пока не вижу. Для моих нужд, по крайней мере. )
+1
У меня установлено куча C++ Builder'ов, но код я пишу не только в этих средах. Когда иногда приходится возвращаться обратно на 6-ку, то чувствую, что всё обрезано :)
Да, действительно, если не планируется писать и поддерживать больших проектов, а только утилиты для себя, то можно обойтись даже Visual Basic 6, я порой пишу такие утилиты на бейсике, так как там нет ничего лишнего.

Что меня конкретно доставало в Delphi 7 и C++ Builder 6, так это разделённая на части IDE и постоянный вылет путей в глобальной переменной path. Когда у тебя всё настроено и вдруг ни с того ни с сего не можешь запустить среду и при запуске идёт нескончаевый поток ругательств о не найденном компоненте *.bpl. Это действует на нервы, т.к. у меня периодически слетают пути что при водит к невозможности запуска IDE и исправлению всех путей вручную (попробуй вспомни как что и куда вписать нужно). С RAD2009 у меня пока такого не было и монолитная IDE мне больше нравится, не нужно помнить комбинации клавиш для вызова конкретного окна, когда оно скрылось.

Есть ещё одна проблема. Если ты используешь сторонние компоненты, то шанс найти их версии для CB6 уже не велик. Народ либо не будет поддерживать CB6, либо будет делать это в последнюю очередь. В своё время всем так нравился набор RxLib, что многие до сих пор на Delphi 7 сидят, так как привыкли к этому набору, а библиотека давно не поддерживается.
0
Слет путей из-за запуска разных сред. Они используют одни и те же ключи. Восстановить работоспособность можно добавив све компоненты (на некоторые он ошибками ругается, но то пофигу).
Мне «дырявый» интерфейс шестого билдера нравится гораздо больше. Все полноэкранные студии своими незаполненными/заполненными не нужной информацией пространствами закрывают полезные фоновые окна. Очень удобно когда один монитор.
0
А я настраиваю и Delphi и BCB6 под себя. Они тоже могут быть почти «монолитными», но не каждый об этом знает :)

Всё, за исключением работы с формами, можно встроить в одно окно, которое максимизируется. Вот и получаем практически то же, что и на старших версиях. Меня «дырявый» интерфейс настолько утомил, что пришлось покумекать и прийти к такому решению.

Монолитный вид IDE BCB6

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

Без нижней панели
0
Мне «дырявый» интерфейс шестого билдера нравится гораздо больше.
Ну, Galileo можно настроить на старый интерфейс (для этого приложен уже готовый шаблон десктопа «Classic Undocked»). А вот старая IDE менее гибка и не все в ней можно настроить так, как в новой (это не только невозможность приаттачить все к главному окну — в ней вообще несколько менее функциональные доки).
Меня, в принципе, устраивают оба стиля — семерка настроена на классический, в остальных — все в доках. Что мне не нравится в классическом интерфейсе, так это то, что окна легко случайно сдвинуть с назначенных мест.
0
+1 Так как автор жмется по сырцам, буду как и хотел писать программку на MVC с нуля, дольше, но в своих грязных тряпках проще копаться :)
0
Прошу прощения у автора — он уже добавил сырцы в аттаче
0
Пиши с использованием скажем wxWidgets, чтобы пользователей Linux тоже осчастливить )
0
Прошу прощения за, возможно, тупой вопрос. А где посмотреть исходники?
P.S. Недавно на форуме.
0
Судя по всему, автор их уже удолил. Если они тебе действительно нужны — могу залить куда-нить.
0
Не все осциллографы сохраняют снимки осциллограмм в своём формате, некоторые сохраняют сразу в виде exel'евских таблиц + png рисунок. Например gwinstek.
0
может проще было сделать конвертер в CSV и просмотр в программах, понимающих этот формат (напр.Gnuplot)?
0
Мне было просто интересно сделать это. И в csv нет полной информации о данных.
0
Позвольте поинтересоваться, почему для реализации выбран именно C Builder?
0
Никаких особых причин нет. Просто под руки попался. Устанавливал ещё visual studio 6, в принципе сам её интерфейс более дружественный чем у борланда, но при этом у борланда создать форму проще и кнопочки всякие накидать тоже. Хотя, опять же, при этом остаётся какое то ощущение «непонятности». Из-за того что не сам описываешь всё это, а задаёшь нажатием каких то кнопок. Т.е. что там происходит внутри при этом непонятно.
Ощущение того что не можешь этого контролировать, что немного напрягает что ли. )
Сейчас более менее вникая уже начинаю понимать что все эти формы, кнопочки и т.д. описываются в файле ресурсов автоматически самой средой. Раньше я не мог понять где же находится описание всего этого в самом коде.
0
Хотя, опять же, при этом остаётся какое то ощущение «непонятности». Из-за того что не сам описываешь всё это, а задаёшь нажатием каких то кнопок. Т.е. что там происходит внутри при этом непонятно.
Ну, поначалу хватает работы с этим как с «черным ящиком» — благо, оно задокументировано и имеет довольно понятный интерфейс. А потом начинаешь копаться внутри и становится понятно, как оно работает. Впрочем, копания хватает надолго — библиотечка довольно хитрая, написана на Delphi и в С++ введена тонна расширений для совместимости с ней.
0
В таком случае стоит определиться, для чего Вам программирование вообще. Если исключительно для решения собственных задач, то Borland-овские инструменты вполне для этого подходят. Вот только из BCB vs Delphi я бы выбрал Delphi, поскольку BCB, ИМХО, набор костылей. VCL целиком реализована на паскале, основная масса сторонних библиотек — опять таки на паскале.
Если же хочется пойти дальше, чем писанина для себя любимого, т.е. в коммерческую разработку, то тут есть смысл обратить внимание на более востребованные технологии (Java/.Net). Borland создал хорошие инструменты, но на рынке он сейчас явно не в фаворе. Firemonkey может что-то и изменит, но на данный момент имеем, что имеем.
+2
Спасибо всем кто откликнулся на разбор кода. Какие то вещи мне стали более понятны.
0
Потыкал программу вживую. Интерфейс довольно неудобный и неочевидный. Например, неясно назначение кнопок в тулбаре — им следует назначить хинты. Несколько странен выбор колеса мыши для перемещения осциллограммы по вертикали — логичней таскать ее мышкой, как и по горизонтали, а на колесо повесить масштаб. Две последние кнопки тулбара странно себя ведут — «таскание по горизонтали» не отжимается, а «таскание по вертикали» не нажимается непосредственно — только при выборе пункта в ее меню. Кроме того, отжатие кнопки «таскать по вертикали» ничего не дает — осциллограмма по прежнему таскается.
Из плюсов — график отрисовывается весьма похоже на картинку в осциллографе.
0
  • avatar
  • Vga
  • 16 июля 2013, 16:55
С кнопками, да, согласен. Но перемотка колесом по вертикали по моему всё таки логична. Осцилограмму по горизонтали нужно таскать синхронно для двух каналов сразу. Смещать же по вертикали осциллограммы бывает нужно независимо, при этом сохраняя возможность двигать по горизонтали.
А по графику — а как он ещё должен отрисовываться? )
0
Ну и в чем проблема? Выбрал нужную крыжиком и таскаешь (а лучше определять на какой осциллограмме юзер кликнул и таскать ее, без всяких там крыжиков). Колесом нелогично, неудобно и страшно медленно (в твоей реализации).
А по графику — а как он ещё должен отрисовываться? )
Ну, это уже от криворукости автора зависит)
0
Ну, это уже от криворукости автора зависит)
Значит тут у меня с руками всё хорошо )))
Колесом нелогично, неудобно и страшно медленно (в твоей реализации).
По моему всё же так удобнее. А скорость перемотки можно менять в выпадающем меню. Там 5 градаций скорости. Каждой цифре соответствует количество пикселей на которое смещается график на один щелчок колеса.
0
Ну, на мой взгляд неудобно, плюс неоднообразно — по горизонтали мышкой таскается, а по вертикали колесом.
0
Устроим вопрос-опрос по этому поводу? )
0
большинство графических сред обрабатывает колесо мыши именно как zoom. Ну или при зажатом Ctrl колесо работает как zoom.
+2
Ну, это же не графическая среда где нужно редактировать какие то отдельные слои. Это просто просмотрщик осциллограм.
0
«Средство просмотра фотографий Windows» обрабатывает скрол, хотя и не умеет редактировать (не то что отдельные слои, а вообще).
Это просто вопрос привычки и стандартизации интерфейса. Если человек привык что масштабирование осуществляется скроллингом, то именно это он и попробует в первую очередь в новой программе. Остальные способы для него уже будут не очевидны.
Но есть ещё такой момент, что изначально скролинг используется как прокрутка большой области (на то он и scroll). Тогда для масштабирования добавляют нажатие Ctrl.
+1
В программах трёхмерной графики есть по крайней мере три действия в окне просмотра модели: сдвиг (pan), вращение (rotate), масштаб (zoom). Ещё выделение (select). Все эти действия можно выполнять левой кнопкой мыши при обработке модификаторов Ctrl, Alt, Shift; возможно нажатие нескольких модификаторов. В двумерной графике нет вращения, выйдет проще, чем в трёхмерной.
Также нужно предусматривать маловероятную ситуацию, когда у мышки нет колёсика, и управление с клавиатуры для ещё более печального случая, когда мышки нет совсем.
0
В Windows многие комбинации клавиш имеют стандартное значение, но пользователи часто не знают их, даже вместо Ctrl‑C и Ctrl‑V копируют мышкой.
Основные горячие клавиши Windows, часть 3.
0
Похоже я могу дополнить список одной часто используемой комбинацией: Win + M. Эта штука делает то же, что и Win + D, но только в одном направлении — просто сворачивает окна без разворачивания. Поэтому, если вы случайно два раза сделали Win + D, то окна опять развернутся, а если два раза Win + M, то они гарантированно будут свёрнутыми.

Я это использую для «очистки» рабочего стола.
0
Первоисточник: Guidelines for Keyboard User Interface Design. На русском языке нету.
Well-designed applications include two basic requirements:
• They must be usable even when the keyboard is the sole input device.
• They must also be functional and user-friendly.
+3
Т.е. вы как-бы намекаете, что нужно пользоваться user-friendly проводником вместо Far'а или TC? Так что ли? Сами чем пользуетесь? А примеры там в статье многие из Проводника взяты.

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

А вот MS Visual Studio я пользуюсь, это удобно, но используемые там технологии в статье 2002 г. не могут быть описаны. Они ведь и Ribbon могли бы статью включить и написать как это удобно, но реально лично я его использую только в MS Office и Mathcad Prime только потому, что ничего другого именно в этих программах нет.
-3
Также нужно предусматривать маловероятную ситуацию, когда у мышки нет колёсика, и управление с клавиатуры для ещё более печального случая, когда мышки нет совсем.
И как вы после этого будете пользоваться MS Office? Может не надо до абсудра доводить? Каждому своё. В Far'е я без мышки обхожусь, а вот MS Office это бессмысленно. А в браузере как без мышки работать? Я уж не говорю про графические редакторы и упомянутые программы 3D графики. Некоторые под мышь просто заточены, т.к. работая без неё вы будете как черепаха.

У DiHalt'a на youtube есть ролик по использованию SketchUp, попробуйте представить всё то, что он там делал без использования мыши :) Дизайнер без мыши — это инвалид.
-4
 По моему опыту, в MS Office мышка мало нужна.
 В машиностроительных САПР мастера работают в основном с клавиатуры, я видел — это непередаваемо. Я не смогу объяснить, потому что мне самому такая работа казалась волшебством.
 Вместо мышки у профессионалов и мастеров немножко другие инструменты. У художников — графический планшет с чувствительностью к нажатию и наклону. У моделировщиков — spaceball и потенциометры, например.
 Мышка — это инструмент начинающих и пользователей-середняков.
 Только vi, только хардкор! :-)
+2
Этот хардкор наверняка должен быть отражен на народном youtube, может покажите как работают профи? Я не прочь овладеть волшебством.
-2
Я не профессионал и не мастер клавиатуры, вы не по адресу.
Я всего лишь заострял внимание, что не всегда у мышки может быть колёсико и что программы должны быть в принципе работоспособны без мышки вообще. Вы не согласны с этим?
+3
раскрыть комментарий
-5
Например: Rhinoceros v5.0, Level 1, Training Manual, глава 4, Precision modeling, страницы 49‒55.
Это официальный учебник для начинающих пользователей Rhinoceros 3D, если что, а не какой‑то там youtube.
0
Этот Precision modeling я в своё время в автокаде изучал :) Да, он используется тоже, теми кто командный синтаксис понимает. И для этого не обязательно было так далеко за примерами ходить. Обычный Автокад управляется командами, только удобнее это делать вместе с мышкой, т.к. команды лишь уточняют точное положение в виде чисел для координат точек или размеров объектов. Это как бы не совсем то. Если вы отлистнёте на пару страниц назад, то увидите, что прежде чем этот Precision modeling применять, нужно кликнуть по инструменту, точно также как и в автокаде набор числами используется для ввода конкретных параметров функции рисования, но саму функцию рисования писать руками никто не будет :) Для этого их знать надо :)

Exercise 10—Entering absolute coordinates:
1 Double-click the viewport title to maximize the Top viewport.
2 From the Curve menu click Polyline, and then click Polyline.
3 To Start type 0 and press Enter.
4 For the Next point type 5,0 and press Enter.
5 For the Next point type 5,5 and press Enter.
6 For the Next point type 0,5 and press Enter.
7 Click Close to close the polyline.

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

А как убирать или подчищать за собой остатки от рисования? Я даже себе этого представить вручную не могу. В Автокаде том же есть функция пометки кусков линий между любыми пересечениями, они при этом пунктиром помечаются, можно выделить так совершенно любые куски и удалить лишние линии с графика. Мне это в частном порядке показал профи, который нас обучал. При этом он показал это только мне, а не всей нашей группе :) он пояснил, что те товарищи могут этого просто не понять :) уже в возрасте.

Я уж молчу про использование команд при рисовании. Это очень удобно, да, но только как дополнение к основному инструменту.
0
Для этого их знать надо :)
Колоссально! Конгениально!
Разумеется же, чтобы применять команды, их нужно знать, это скрупулёзно подмечено! Военному делу нужно учиться настоящим образом, Волга впадет в Каспийское море, лошади кушают овёс и сено, а команды нужно знать и уметь их применять, а не тыкаться как слепой котёнок.
прежде чем этот Precision modeling применять, нужно кликнуть по инструменту...
Очень хорошо, молодец. К примеру команда
From the Curve menu click Polyline, and then click Polyline
чудненько вызывается комбинацией M-c-p-right-p. Сложно? Тогда проще: polyl.

Вы менторски изрекаете прописные истины, выдавая их за снизошедшее свыше наитие, доверенное исключительно вам, а таких же прописных истин, высказываемых другими, в том числе и мной, вы в упор не признаёте.
Например, что программа не имеет права принуждать пользователя иметь мышку с колёсиком и любую мышку вообще. Или, например, что все не обязаны переписывать всё на .NET потому что «это удобно».
+3
раскрыть комментарий
-5
• Вы проходили одни курсы, я — некоторые другие.
• Для некоторых САПР равняется автокаду, для некоторых автокад — это вообще не САПР.
• Вы работали в автокаде, вы лучше знаете, как удалять вспомогательные линии в автокаде.
• «Волнующее действие» на меня оказала не работа мастера в автокаде, а в некоторой совсем другой САПР.
• Кому-то проще кликнуть туда, кому-то — сюда (эх, молодость, молодость...).
• Некоторые более другие могут творить на клавиатуре волшебство, а некоторые совсем менее другие — без мышки и в МСофисе «инвалиды».
• Некоторые немножко более другие САПР, чем автокад, имеют немножко более другие возможности в сравнени с менее более другой неСАПР как автокад. Поэтому наш разговор как бы немножко совсем ни о чём, по сравнению с тем, что вы совсем немножко более, чем полностью, не ответили, почему программа отображения осциллограмм должна настаивать на наличии колёсика у мышки пользователя таковой программы.

Уффф… извините, спасибо, и ещё извините, мне более не интересно продолжать разговор дебила с имбецилом.
+1
раскрыть комментарий
-5
Вы сами себе противоречите. Векторные карты должны быть выполнены в масштабе. Это значит, что все координаты точек известны. Автокад предназначен для работы с чертежами (это его прямое назначение). А на чертеже всегда все размеры известны. И ненужные вспомогательные линии при черчении не используются, потому и удалять на нормальном чертеже ничего не надо.
Понятно, что мышь в автокаде используется, но это примерно 10% от всей работы.
0
Не пойму где противоречие? Вы сам процесс векторизации видели когда-нибудь? Хотите покажу? Правда не на примере Автокада, но принцип от этого не меняется. Берётся карта, сканируется и подкладывается в слой изображения под будущий чёртёж, после этого согласуются размеры по известному масштабу карты — её увеличивают или уменьшают. После этого, когда размер картинки подогнан, начинается процесс векторизации прямо по картинке. Не знаю зачем там нужна будет клавиатура, ах да, толщину линий нужно будет выбирать. Почему там будет 10% только работы мышкой мне не понятно.

Вот как та же самая процедура выглядит в Sprint Layout (AVR910-USB для ATmega8 в DIP'е):

AVR910-USB

Если вы можете развести плату похожим образом используя мышь на 10%, то я хотел бы на это посмотреть, поучиться. На своём личном опыте могу сказать обратное, что в этом случае на 10% используется клавиатура для указания всё тех же параметров: толщина линий, диаметр отверстий и т.п. Подчищать же за собой требуется постоянно, а карты векторизуют для того, чтобы потом было проще вводить в них исправления.
+1
тут я с Вами согласен, но это всего-лишь отдельная группа задач, которые можно решать с помощью автокада.
0
Как художник художнику, опишите процесс построения в Автокаде вот такой вот детальки. Достаточно одного вида сверху. Только Боже вас упаси (!!!) строить внутреннюю вспомогательную окружность. Она не нужна, только простыми фигурами: квадратами, полилиниями и треугольниками!

Как я бы это делал. Нарисовал внешнюю окружность, потом внутреннюю (это вспомогательная окружность), потом при помощи инструмента Array размножил по кругу мелкие окружности. Дальше мышкой выделил бы все обрезки на пересечениях внутренней окружности и мелких окружностей и разом их удалил. Всё.

Деталь
0
Вот, вспомнил былое. Как я бы чертил такую детальку (это не натуральная модель, сам принцип). Так вот, я просто не могу себе представить такой процесс удаления вспомогательных линий вручную без мышки. И как такое нарисовать только с клавиатуры… ну я просто не знаю, видимо потому что «дебил или имбецил» :)

Обрезка в Autocad
0
Так вот, я просто не могу себе представить такой процесс удаления вспомогательных линий вручную без мышки
Можно все окружности(кроме внешней) сделать регионами(ком. region), а после ко всем регионам применить команду union. И никаких вспомогательных линий, но и тут без мыши не обойтись. ACAD не именует объекты, в отличие от, например, 3dsMax. Если бы именовал, то можно было бы их выбирать в командной строке.
+1
Да, это работает (у меня правда полностью русифицированный Автокад и команды соответственно: область и объединение), но я просил без мышки :) Тут народ настаивает, что профи работают только с клавиатуры и мышь только в 10 %. Лично я не буду вбивать команду массив, чтобы размножить окружности. Я выберу кнопку на панели, а потом появится диалоговое окно, в диалоговом окне нужно выбрать мышкой характер массива, указать точку, относительно которой создавать массив и т.д., и т.п. Всё это я делаю тоже мышкой.
0
у меня правда полностью русифицированный Автокад и команды соответственно: область и объединение
Перед написанием команды(на английском) ставьте нижнее подчёркивание:
_list
_region
_subtract
Всё работает как и раньше. Мне за 14 лет периодической работы с акадом уже непривычны команды на родном русском. Хорошо что локализаторы не добрались до системных переменных, это было бы что-то…

Тут народ настаивает, что профи работают только с клавиатуры и мышь только в 10 %.
Командная строка имела явное преимущество во времена мониторов с малой диагональю, можно было убрать с экрана плавающие панели и работать с командной строкой и мышью. Больше графики при этом на экране.

Я выберу кнопку на панели, а потом появится диалоговое окно… Всё это я делаю тоже мышкой.
Это похоже на «религиозные войны». Кому как удобно. Я использую и диалоговые окна и командную строку. Иногда требуется указывать координаты, например если нужно распечатать лист формата А1(A1хN) на принтере формата А4, с последующим склеиванием листов. Плоттеры пока не во всех организациях имеются(увы). Хорошо когда интерфейс гибкий и можно всё настроить под себя )
+2
Спасибо, я сам изредка им пользуюсь, когда нужно что-нить небольшое начертить. А мне русский интерфейс нравится, в 2008 правда. Всё локализовано, справка по-русски. Помнится когда-то был какой-то неадекватный перевод в главном меню и народу это не понравилось.

Да я о том же, порою требуется указывать явно какой объект тебе нужен и как ты это будешь делать в командной строке, если можешь банально подвести мышку и щёлкнуть по точке или объекту, тем более что многие «особенные» точки подсвечиваются.

Я не веду религиозных войн, я просто за здравый смысл. Кто будет полностью всё делать командами, когда быстрее и проще, а иногда и диалоговые окна на это указывают, делать многие вещи мышкой. Если ты знаешь направление, угол и размер относительно текущих координат, то точнее ввести их с клавиатуры, если же нужно что-то подрезать в общем случае методом, который я выше показал, то как это без мышки-то сделать? Т.е. здесь идёт на равных кооперативная работа инструментов и я считаю глупо принижать роль мыши в общем процессе рисования.

Я всего лишь заострял внимание, что не всегда у мышки может быть колёсико и что программы должны быть в принципе работоспособны без мышки вообще. Вы не согласны с этим?
Автокад без мыши насколько реально работоспособен? :) Имеет вообще смысл им пользоваться без мыши?
0
А мне русский интерфейс нравится
По возможности стараюсь работать с английским интерфейсом(+help) ради тренировки памяти и лучшего знания английского.

Помнится когда-то был какой-то неадекватный перевод в главном меню и народу это не понравилось.
В акаде версии до 2005г(к примеру) команда break была переведена как «разбить», что по смыслу абсолютно правильно. В последующих версиях чью-то(у локализаторов) голову посетила светлая мысль и команда теперь имеет значение «расчленить»(слово то какое, ужос!). Ляпы были, иногда перевод элемента static не умещался на поле диалогового окна. Встречалось и такое.

… если можешь банально подвести мышку и щёлкнуть по точке или объекту, тем более что многие «особенные» точки подсвечиваются.
Знаю, знаю. Ну что тут сказать? В достижении поставленной цели все разумные способы хороши.
Ведь никто не будет спрашивать как ты создал чертёж, использую команды или кликая по баттонам?
Это дело только чертёжника. От командной строки отказываться пока не собираюсь.
Даже в 3dsMax есть возможность убрать панели с экрана и работать только с командами.

Автокад без мыши насколько реально работоспособен? :) Имеет вообще смысл им пользоваться без мыши?
Нет. Не то что бы смысла нет, а и возможности нет. Некоторые операции можно выполнять без мыши, а что-то в принципе невозможно. Ведь курсор мыши на экране — это словно карандаш и кульман )
0
Я тоже не веду религиозных войн. Просто не стоит незаслуженно обижать инструмент (командную строку) если им не пользуешься. Как тут уже писали — это дело самого чертежника. Кто-то знает и пользуется, кто-то не пользуется. На вкус и цвет… ну Вы, в общем, в курсе…
0
А кто им не пользуется? Вы это про кого? Если на меня намекаете, то вы тут встряли не прочитав комменты. Я нигде не писал, что я не пользуюсь командным интерфейсом, более того я написал, что мы проходили его наравне со всем остальным.
Это вот другой тут товарищ настаивал, то какие-то неизвестные профи пользуются ТОЛЬКО командным интерфейсом, в чём я усомнился и привёл простой практический пример.
Командная строка искуссьвенным интеллектом не обладает, это обычные лисп-функции и если вы хотите заставить их работать, то должны указать объект в качестве параметров. Если объекты простые типа чисел, то их можно указать прямо, если объекты неявные, то есть человек их должен выбрать, то вы не сделаете это при помощи командной строки. Вот в общем и всё, у командной строки как у любого другого инструмента есть ограничения, свои плюсы и минусы.
Повторяю ещё раз, я изучал командную строку и специально для этого купил сборник тестовых чертежей с проставленными размерами, на таких чертежах набиваешь руку и оттачиваешь оптимальные пути решения задачи. Я считаю, что реальный инженер не будет заниматься мазохизмом, пытаясь ВООБЩЕ не использовать мышь, так как это невозможно.
0
Я уже лет 15 как не работаю с автокадом (у меня его даже на компе нету). Потому рабочий пример не приведу. По памяти — такое построение выполнялось с консоли не намного сложней чем Вы описали с помощью мыши. Если я вдруг запамятовал и ошибаюсь — то знающие люди меня поправят. Деталька-то тривиальная для производства.
0
Или, например, что все не обязаны переписывать всё на .NET потому что «это удобно».

Могу и по этой теме привести практический пример прямо здешний. Вот человек рисует с использованием GDI в BCB6, когда этот GDI уже как 10 лет устарел. Почему он не использует новый GDI+? А я объясню, потому что примеров мало, потому что писать и поддерживать GDI+ в BCB6 и выше неудобно. А почему неудобно? Потому что родные заголовочные файлы написаны для VC++, там удобно, а в BCB6 нет.

Я пробовал использовать GDI+ на следующих языках/средах: VB6, Delphi 7, BCB6/RS2009, C++/CLI и C#. И знаете где GDI+ удобнее пользоваться? Да, на .Net, т.к. она там такая же родная как GDI в Win32API. Поэтому все те, кто хочет использовать GDI+ вынуждены как-то приспосабливаться к новому средству, вместо того, чтобы делать это на родной платформе.

VB6 приспосабливается, Delphi 7 так вообще через костыли, т.к. хоть VCL и написана на паскале, но GDI+ написана на C++, поэтому именования всех объектов GDI+ на паскале отличаются от оригинала и так просто переписать пример с C++ на Delphi не получится, как я это делаю, переписывая с VC++ в BCB6. Писать в C++ ещё как-то можно, но проще всего это делать было на C#.

Помучившись с рисованием в разных средах я для себя вынес, что больше пробовать нигде не буду, а если понадобится, то напишу в .Net, как и делаю.
-2
многие из тех, кто профессионально работает в автокаде, мышку вообще не трогают, а работают именно с консоли. Да Вы правы, что команды надо знать, но профессионало-то их как раз и знают. Я, когда-то давно, тоже учился автокадом пользоваться, так мне консольный режим понравился намного больше мышового (отчасти потому, что после Corel автокадовский мышовый интерфейс кажется сделанным через… опу.
0
Итак, вы не согласны, что:
не всегда у мышки может быть колёсико;
программы должны быть в принципе работоспособны без мышки.
0
Без мышки они путь как просмотрщики работают, если этого достаточно, а в чертёжных и прочих программах без мышки вы как инвалид. Я привёл выше простой пример из практики. Обычно построение чертежа идёт при помощи вспомогательных линий. Это было и до эпохи компьютеризации, осталось и сейчас. Так вот как вы без мышки уберёте вспомогательные линии?

Нашёл где это описано в Руководстве: Trim стр. 120 (естественно с мышкой всё). В Автокаде я бы пример этот сделал не так как там написано, в Автокаде можно не указывать линии, до которых нужно обрезать. Можно выделить любые отрезки сразу и их удалить. В этой программе наверняка можно сделать также, только я этого с ходу не нашёл. Какой смысл в данном примере я вообще не понял, когда можно просто выбрать нужные куски и удалить?
0
простите, но так рисуют в кореле, а не в автокаде. На чертежах вспомогательные линии нужны только для построения сложных геометрических фигур и написания текста шрифтом. КАДы шрифтом пишут сами и большинство основных геометрических фигур тоже рисуют сами, включая и многоугольники. Потому использование дополнительной геометрии сведено к минимуму.
0
Win+E; далее стрелки и enter, дабы добраться до нужного мне диска-файла; Ctrl+End и стрелки (PgUP/PgDown ещё); вбиваю памятку; Ctrl+S; Alt+F4; Alt+F4
Такой ритуал я провожу каждый день.
Ну и Alt+F4 (нужное количество раз) Enter — вырубаем комп.

А отсутствие мышки в современном мире это реальная проблема, потому что «мегаразработчики» считают что она просто обязана быть, и не рассматривают возможность её поломки. А без мышки даже программу закрыть нельзя, в частности потому что они перехватываю сообщение закрытия и показывают свое «красивое окошко» предложения закрыть программу, в котором клавиатура ни как не поддерживается.
+2
Кстати говоря мне самому в программе не нравится скорость перерисовки картинки. Когда окно разворачивается на весь экран это бывает заметно.
Выше uni писал про GDI+, думаю что и эту технологию надо будет поштудировать, предполагаю что она быстрее должна быть.
И в общем по обсуждению тут, после некоторого мозгового штурма сейчас всё легче уже ложится в голову. Ещё раз могу сказать спасибо за участие в этом обсуждении и за советы.
0
Можно и на GDI достаточно быстро рисовать, если оптимизировать отрисовку. Я знаю людей, которые игры на GDI делают с неплохой двухмерной графикой. Не тормозит.
Ну и сам я, если бы искал быструю альтернативу GDI, то воспользовался бы OpenGL.
0
Дело не в API, а в количестве данных и числа вызовов процедур рисования. Если рисовать не много и компьютер мощный, то можно прямо на канве рисовать. Если же времени не хватает, чтобы завершить прорисовку, то используют двойную буферизацию, т.е. рисуют всё в память, а потом выводят на канву.

Второй случай создаёт эффект мгновености, а в первом порою всё притормаживает. Быстро можно рисовать как в GDI, так и в GDI+. Нужно просто в инете посикать: GDI + двойная буферизация.
0
Я прореживаю поток самих данных перед прорисовкой. У меня максимум входных данных для прорисовки равен числу пикселей по горизонтали умноженных на четыре. Независимо от количества данных в самом файле. Хотя я вот сейчас думаю что можно уменьшить в 2 раза это количество.
0
API для рисования придумывали для того, чтобы программист не занимался такими расчётами самостоятельно. Сейчас это может быть не очевидно, но вообще на весь этот процесс рисования влияют характеристики монитора (дисплея). Поэтому, на компе разработчика все выглядеть может одним образом, а вот на компе пользователя почему-то всё съехало.

Лучше при рисовании использовать только API GDI и расчёты делать только с его помощью. В одних системах размеры измеряются в пикселях, в других — в относительных единицах (VB6), а всё для того, чтобы вид интерфейса был одинаковым при разных настройках монитора.

Ещё одна проблема — это попиксельная дискретизация. У тебя сигнал в double, а при округлении в целое появляется неоднозначность в рисовании, как рисовать линию: на один пиксель левее или правее, или в текущей позиции? Поэтому в GDI+ ввели тип данных float для поддержки рисования на холсте и рисование стало более адекватным.

Посмотрел, что вы рисуете на Image. В общем, есть какая-то проблема при рисовании с использованием Image, я точно не помню уже какая и сам рисую на PaintBox. Дело ещё в одной вещи. Дочерние компоненты зависят от родителей, а у компонентов есть некоторые свойства типа ParentColor, которые заставляют перерисовывать себя цветом окна родителя. Такие вложенные по иерархии прорисовки происходят без контроля пользователя сами по себе и отнимают лишнее время. Если это свойство установлено в true, то все компоненты по иерархии должны сначала себя перерисовать, а потом уже дело доходит до вашего обработчика. На это уходит время, т.к. нужно залить всю область каждого вложенного компонента.

Вообще, рисовать на компонентах не хорошо, потому что от них идут тормоза из-за внутренней работы. По идее нужно рисовать прямо в каком-нить окне, он это не удобно, т.к. нужно такое окно создавать в runtime, типа как с OpenGL и он получается не визуальным. Поэтому, как нечто среднее используют PaintBox.

Придётся, наверное, ликбез давать.
0
есть какая-то проблема при рисовании с использованием Image, я точно не помню уже какая и сам рисую на PaintBox
Image я взял потому что при этом не надо в ручную контролировать перерисовку при перекрытии окон, к примеру.
0
На Image рисовать тоже советуют, т.к. происходит кеширование результата и его автоматическая отрисовка. Нет мерцаний в итоге.
PaintBox же больше предназначен для «динамичного» рисования, когда нет смысла кешировать результат. Для исключения мерцания же просто создают тот-же самый фоновый имедж в котором рисуют, и который затем переносят на паинт-бокс.
Но вообще да, логично нарисовать в фоновый имедж, и из него переносить в паинт бокс. Так будет гораздо меньше накладных расходов, связанных с реализацией курсора (в виде вертикальных/горизонтальных линий с подписью значений) — не потребуется перерисовывать весь график по линиям.
+1
У меня в принципе так и происходит, я рисую на невидимом Image, и только потом копирую его на экран, на основной Image. Я пробовал разными функциями это делать, и в разных режимах копирования. В итоге остановился на том что есть.
0
По-нормальному нужно рисовать на Bitmap и копировать его на холст. Вот можете посмотреть насколько быстро происходит прорисовка у меня.

Ссылка на программу: Example2_20130717.zip.

Нужно изменить поля: Количество точек и Функция s(t), увеличив частоту. Потом попробовать изменять размер при небольшом окне и при большом. Это работа двойной буферизации в GDI+, без каких-либо оптимизаций и подсчётом точек в сглаженном режиме.

Так вот, чтобы научиться более менее так рисовать и вообще что-то рисовать, может занять времени больше, чем написание обёрток типа MVC, потому что нужно иметь больше опыта в понимании внутренней архитектуры работы GDI или GDI+. В данном конкретном случае это не просто, т.к. поддержка GDI+ в BCB и в RS вообще немного косячная, начиная с того, что вы не сможете скомпилировать ни одного примера, пока не поправите родной заголовок GdiplusGraphics.h. Есть ещё всякие косяки и при борьбе с ними MVC отходит на десятый план. А вот когда всё заработает на черновую, вот тогда уже можно что-то ваять или обёртывать.

В данном примере у меня были две проблемы:
1. Release сборка вылетает с ошибкой при запуске. Причина не установлена.
2. При перекрытии окна оно не обновляется, когда перекрытие исчезло. У меня не было времени с этим разбираться, нужно поискать какое событие приходит к окну в этом случае и вызвать обработчик перерисовки.

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

Некоторые небольшие тормоза при выводе также могут быть связаны с тем, что я делаю кучу расчётов, чтобы вывести все нужные подписи в нужных местах. Т.е. я не просто график показываю, но и просчитываю где должно находится всё его окружение. Это не оптимизировано. Точки графика тоже пересчитываются налету в зависимости от области рисования. Здесь используется float-арифметика.

Работа GDI+ с двойной буферизацией
0
Да, кстати, если Smoothing ещё отключить у меня, то скорость ещё увеличивается, но мне сглаживание нравится, пусть даже с некоторыми тормозами. Отчасти именно из-за неё я на GDI+ перешёл. Ну и других всяких удобных функций там хватает.
0
Вот тут написано как использовать GDI+ в BCB: GDI+ and C++Builder. В основном это работает, правда в RS2009 у меня Release сборка не запускается. Ошибка даже try catch() не ловится, пока не понял в чём дело, но Debug версия работает.

А вот тут написано как, используя GDI+, можно ускорить отображение, чтобы было без тормозов: Drawing Speed in GDI+. У меня в программе используется: Step 2 — Using off-screen graphics. Есть ещё куда расти оказывается.
0
А вот тут написано как, используя GDI+, можно ускорить отображение, чтобы было без тормозов: Drawing Speed in GDI+.
Не вникал я что он там отрисовывает, но его тайминги
OnPaint 130 ms
Prepare off-screen bitmap 40 ms
Without resizing (draw from off-screen graphics to screen): OnPaint 70 ms
не впечатляют.
У себя в коде я измерял время отрисовки(через GetTickCount()),
значение крутится вокруг 50ms. И то при пермещении осциллограммы по горизонтали тормоза чувствуются.
0
By Alex Fr, 9 Aug 2001
Быстродействие может зависеть от производительности компьютера?
+1
Ну а мои «тайминги»? :) Впечатляют? Я пробовал изменять размеры вашей программы при отрисовке файла 1.waf. Попробуйте задать те же по объёму данные у меня: количество точек, частоту сигнала (нажать F9 для пересчёта). Потом изменяйте размеры у себя и у меня. Скорость прорисовки у меня как минимум в 2 раза выше (при изменении размеров окна).

В примере тоже используется GetTickCount(), только он обёрнут в класс для удобства (файл TimeCounter.h в архиве проекта):

// Class prints to Debug output stream 
// time between instance creating and destroying
class CTimeCounter
{
public:
    CTimeCounter(LPCTSTR sName)
    {
        m_sName = sName;        // keep only pointer and hope
                                // user will not destroy original string

        m_nStart = GetTickCount();
        m_bShowTime = FALSE;
    }
    ~CTimeCounter()
    {
        if ( m_bShowTime )
        {
            TCHAR* s = new TCHAR[lstrlen(m_sName) + 10];
            _stprintf(s, _T("%s  %d ms\n"), m_sName, GetTickCount() - m_nStart);
            TRACE(s);
            delete[] s;
        }
    }

    void SetShowTime()          // prints only after call to this function
    {
        m_bShowTime = TRUE;
    }

protected:
    long m_nStart;
    LPCTSTR m_sName;
    BOOL m_bShowTime;
};
0
Используйте __try/__catch — они ловят виндовый сеш.
0
Нет, это тоже не ловит. Думаю, что тут проблема куда глубже. Нужно смотреть чем отличаются настройки конфигураций проекта, а они для Debug и Release во многих местах отличаются. Какой-то флаг где-то всё портит. Плохо то, что Debug-то работает, получается, что я не могу отладить косячный Release. Придётся дедовскими методами вставлять где-нить после каждой команды ведение лога и смотреть на каком этапе вылет происходит (ещё до создания форм). Это один вариант. Другой вариант — тупо перебрать все отличные флаги, пока не запустится. Скорее всего придётся действовать вторым способом, т.к. у меня такое чувство, что косяк где-то не на моей стороне, а внутри библиотек RS. Туда вставлять ведение лога как-то не очень хочется.

На страничке, которую я указывал, тоже не гарантируют работу, там есть комментарии, что у некоторых наоборот Release сборка работала.

Я ещё забыл добавить одну ссылку: дружим Gdiplus и C++Builder. Без этого тоже работать ничего не будет.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.