Разработка и реализация программных средств для работы с веб-контентом в рамках проекта INTERIN PROMIS

  • Вид работы:
    Дипломная (ВКР)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    797,18 Кб
  • Опубликовано:
    2017-08-12
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Разработка и реализация программных средств для работы с веб-контентом в рамках проекта INTERIN PROMIS

СОДЕРЖАНИЕ


ВВЕДЕНИЕ

1. АНАЛИТИЧЕСКИЙ ОБЗОР

1.1 Расширение возможностей браузера плагинами

.2 Проксирование вызовов через теневую службу

.3 Создание собственного веб-клиента

.4 Анализ требований и выбор варианта решений

2. ПРОЕКТИРОВАНИЕ СТРУКТУРЫ СИСТЕМЫ

2.1 Основная идея модульного деления

.2 Межмодульное взаимодействие

.3 Проектирование ядра системы

.4 Проектирование главного окна системы

.5 Проектирования модуля печати

.6 Допущения и ограничения

.7 Структурная схема системы

.8 Функциональная схема системы

3. РАЗРАБОТКА ОСНОВНЫХ УЗЛОВ СИСТЕМЫ

3.1 Среда разработки Qt Creator

.2 Разработка ядра системы

.3 Разработка модуля главного окна системы и модуля печати

. ТЕСТИРОВАНИЕ СИСТЕМЫ

4.1 Инсталяция программы

.2 Тестирование функциональной части программы

.3 Тестирование модульной расширяемости

5. ВНЕДРЕНИЕ

ЗАКЛЮЧЕНИЕ

ПРИЛОЖЕНИЕ


ВВЕДЕНИЕ

 

Актуальность темы.

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

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

Цель данной работы:

Целью данной работы является разработка идеи объединения сторонних решений и реализация многофункционального, легко масштабируемого приложения, в рамках проекта «Interin Promis».

Задачи работы:

1.      Аналитический обзор предметной области и выбор средств разработки.

.        Разработка функциональной и структурной схемы программного средства.

.        Реализация основного модуля программы (Модуля отображения веб-страниц).

.        Реализация модуля печати и модуля обновлений. Создание инсталлятора приложения.

.        Изучение взаимодействия информационных технологий, методов их интеграции и обмена данными.

Научная новизна работы:

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

Практическая значимость работы:

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

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

Апробация работы.

Результаты работы используются в тестовом режиме в медицинском центре ООО «Клиника Говорово».

Публикации. Основные результаты диссертационного исследования изложены в статье «Кроссплатформенный клиент корпоративной информационной системы», опубликованной в выпуске №67 научного журнала «NovaInfo».

Выпускная квалификационная работа состоит из пяти разделов.

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

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

В третьем разделе представлена реализация основных модулей системы, а так же дано описание среды разработки QtCreator.

В четвертом разделе представлены материалы функционального тестирования системы с представлением материала их выполнения.

В пятом разделе описывается процесс внедрения программной системы в рамках проекта «InterinPromis».

 

1. АНАЛИТИЧЕСКИЙ ОБЗОР


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

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

Для организации работы МИС Интерин PROMIS одним из ключевых моментов является взаимодействие клиента (веб-браузера) с периферийными устройствами и операционной системы. Такое двухстороннее взаимодействие невозможно, используя стандартный браузер. Далее будут представлены возможные варианты организации двухстороннего взаимодействия веб-клиента и операционной системы, а так же подставлены достоинства и недостатки таких подходов.

1.1 Расширение возможностей браузера плагинами

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

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

1.2 Проксирование вызовов через теневую службу

Прокси-сервер представляет собой «промежуточный» сервер, который выступает в роле своеобразного посредника между сайтом и браузером. Название этого сервера произошло от английского слова «proxy» - «уполномоченный, представитель» [1].

Прокси-серверы различаются в зависимости от конфигурации. Бывают открытые и закрытые прокси-серверы.

Открытым прокси-сервером может воспользоваться любой желающий. Такие промежуточные серверы помогают оказаться на сайтах, доступ к которым с конкретного IP-адреса может быть запрещенным по той или иной причине. Чтобы начать использовать данный сервер, пользователь должен сначала подключиться к нему (либо же зайти на определенный сайт), а после этого послать запрос, содержащий адрес необходимого ресурса. Получив необходимую команду от пользователя, прокси-сервер начинает подключаться к необходимому серверу, а затем он выдает результат запроса.

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

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

Добавление прокси-сервера позволит системе абсолютно не зависеть от вида интернет-браузера, а так же от его обновлений. Однако браузер должен поддерживать работу в веб-соккетом, что накладывает на него некоторые ограничения. Проксирование клиентских запросов позволит из java-скрипта выполнить любой метод любого класса, который объявлен внутри этого прокси-сервера. Для реализации такой цепочки нужно будет во все веб-страницы, в которых будет необходимо взаимодействие с операционной системой, добавлять специальный java-скрипт. Это ограничение не очень серьезное, поэтому особых проблем не вызывает, но написание этих скриптов становится более трудоемким, так-как разработчик должен расписывать то, что классическому java-скрипту не свойственно.

Поддержка оптимальная, только одно приложение. Однако стоит отметить недостаток - прокси-сервер должен работать как служба. В таком случае он будет не визуален, а следовательно будет невозможно взаимодействовать с пользователем напрямую, а только через скрипты на веб-странице.

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

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

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

1.3 Создание собственного веб-клиента

Web-браузер - это прикладное программное обеспечение для просмотра веб-страниц; содержания веб-документов, компьютерных файлов и их каталогов; управления веб-приложениями; а также для решения других задач [2].

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

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

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

1.4 Анализ требований и выбор варианта решений

Разрабатываемая система предназначена для реализации клиентской части МИС (медицинской информационной системы) и обеспечивать взаимодействие МИС с операционной системой АРМа.

Целью разработки системы является обеспечение взаимодействия веб-приложений с операционной системой АРМ, сторонними программными продуктами и оборудованием.

Требования к структуре и функционированию системы:

.        Система должна функционировать под управлением ОС Windows, Linux, MacOS.

.        Система должна обрабатывать запросы пользователя и визуализировать результат (диалоговый режим).

.        Система должна обеспечивать защиту передаваемых по сети данных.

.        Система должны обеспечивать API для отображаемых веб-страниц по взаимодействию с клиенской ОС.

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

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

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

Библиотека QT предназначена для разработки GUI, разработанная компанией Trolltech AS. Qt была представлена в 1996 году, с тех пор, с помощью этой библиотеки было создано большое количество разнообразных приложений с графическим пользовательским интерфейсом [3].

Qt является кроссплатформенной, есть реализации библиотеки для MS/Windows, Unix/X11 (Linux, Sun Solaris, HP-UX, Digital Unix, IBM AIX, SGI IRIX и пр.), Macintosh ( Mac OS X ) и Embedded платформ. Библиотека является объектно-ориентированной, базирующейся на компонентах и имеет богатое разнообразие различных визуальных элементов - виджетов (widgets), предоставляемых в распоряжение программиста.

Эта библиотека является безусловным лидером среди имеющихся средств разработки межплатформенных программ на языке C++. Широко известная и часто используемая в мире Linux, она, благодаря распространению графической оболочки KDE, стала де-факто стандартом проектирования программного обеспечения на этой платформе.

Достоинства библиотеки QT:

.        Кроссплатформенная разработка приложений.

.        Удобная работа со строками.

.        Поддержка оконного интерфейса.

.        Возможность работы с сетевыми протоколами.

.        Поддержка разработки сложных графических объектов.

.        Модульность библиотеки.

.        Обновляемость.

Таким образом, данная библиотека в своем составе уже имеет модуль

для работы с веб-контентом, который в свою очередь построен на базе chromium. Это позволит иметь современный и обновляемый инструмент, а модульная структура поспособствует легкому обновлению приложения.

 

2. ПРОЕКТИРОВАНИЕ СТРУКТУРЫ СИСТЕМЫ


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

Программный модуль - это любой фрагмент описания процесса, оформляемый как самостоятельный программный продукт, пригодный для использования в описаниях процесса [4]. Это означает, что каждый программный модуль программируется, компилируется и отлаживается отдельно от других модулей программы, и тем самым, физически разделен с другими модулями программы. Более того, каждый разработанный программный модуль может включаться в состав разных программ, если выполнены условия его использования, декларированные в документации по этому модулю. Таким образом, программный модуль может рассматриваться и как средство борьбы со сложностью программ, и как средство борьбы с дублированием в программировании.

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

2.1 Основная идея модульного деления

Разрабатываемый клиент большой корпоративный системы использует множество технологии для реализации различных автоматизированных рабочих мест (АРМ). Он вынужден собирать различные информационные технологии для организации таких возможностей как:

.        Организация звонков с рабочих мест.

.        Просмотр файлов.

.        Мониторинг работы оборудования.

.        Работа с текстовыми документами.

Таким образом, получается, что для реализации всех возможностей

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

2.2 Межмодульное взаимодействие

Для обеспечения работоспособности всех компонентов системы необходимо организовать связь модулей друг другом. Для этого применяют такие технологии как AciveX, элементы NativeAPI, подключение динамических библиотек (DLL) и другие.

Одним из требований к системе является ее кроссплатформенность, однако элементы AciveX и NativeAPI предназначены для работы только в операционных системах семейства Windows. Однако разработка с использование DLL библиотек позволяет получить платфомонезависимое приложение [5].

Динамически подключаемые библиотеки (DLL)

Библиотека DLL представляет собой коллекцию подпрограмм, которые могут быть вызваны на выполнение приложениями или подпрограммами из других библиотек. Подобно модулям, библиотеки DLL содержат разделяемый (sharable) код или ресурсы. Однако, в отличие от модулей, библиотеки содержат отдельно откомпилированный исполняемый код, который подключается к приложению динамически на этапе его выполнения, а не компиляции.

Для того чтобы библиотеки можно было отличить от самостоятельно выполняемых приложений, они имеют расширение .dll.

Библиотеки могут содержать два вида подпрограмм: экспортируемые и внутренние. Экспортируемые подпрограммы могут вызываться процессом, подключающим библиотеку, а внутренние могут быть вызваны только внутри библиотеки.

Библиотека загружается в адресное пространство процесса, который ее вызвал. Подпрограммы библиотеки также используют стековое пространство процесса и его динамическую память.

Использование динамических библиотек предлагает следующие преимущества:

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

Экономия места на диске. В случае параллельного использования одной dll-библиотеки многими программами, на диске хранится только одна копия данной библиотеки. При использовании статических библиотек наоборот, для каждой программы создается отдельная копия данной библиотеки.

Удобство и простота изменения и обновления dll-библиотек. Если требуется провести изменение или корректировку dll-библиотеки, то после внесения изменений в библиотеку не придется проводить компиляцию или компоновку приложения, которое использует данную библиотеку, заново. Однако при использовании объектного кода статических библиотек придется проводить перекомпиляцию программы или снова проводить компоновку в случае даже небольших изменений.

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

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

Упрощение создания международных версий приложений. Легкость использования dll-библиотек позволяет облегчить процесс интернационализации программы, если, например, загрузить все строковые ресурсы, используемые программой, в dll-библиотеку. Соответственно версию для каждого языка размещаем в отдельной библиотеке, и подгружаем ресурсы той, язык которой необходим в данный момент.

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

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

)        соответствующая библиотека обновилась до новой версии;

)        внесены изменения в зависимую dll-библиотеку;

)        соответствующий dll-файл перезаписывался с более ранней версией;

)        соответствующий dll-файл не найден системой или удален с компьютера.

Обычно эти действия называются конфликтами dll-библиотек. Если не обеспечивается обратная совместимость, программа не может быть успешно запущена. Такие действия называют конфликтом dll-библиотек. Программа может быть успешно запушена и нормально использоваться только в случае обеспечения обратной совместимости [6].

Преимущества использования динамических библиотек (по сравнению со статическим подключением подпрограмм на этапе сборки приложения) следующие:

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

Модификация подпрограмм библиотек не требует повторной компиляции приложений до тех пор, пока остается неизменным формат их вызова.

Библиотеки обеспечивают также послепродажную поддержку (after-market support). Например, дисплейный драйвер, предоставляемый библиотекой, может быть обновлен для того, чтобы поддерживать дисплей, который не существовал в момент продажи приложения.

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

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

Реализация межмодульного взаимодействия

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

Такими функциями являются:

1.      Init.

2.      Start.

3.      Stop.

4.      About.

После загрузки каждого конкретного модуля в память, в функции Init размещается код для инициализации и создания используемых объектов, которые объявлены внутри DLL библиотеки. Функция About позволяет получить строку с описанием модуля и его функционала. Интерфейсная функция Start используется в тех случаях, когда загруженный модуль содержит исполняемый код, которому нужно передавать управление. Stop - это функция останавливает выполнение исполняемого кода. Если загруженная библиотека только предоставляет набор функций, то интерфейсные функции Start и Stop должны быть пустыми.

Поскольку разрабатываемая система отображает веб-страницы, то в подключаемых библиотеках могут быть как элементы управления (окна, виджеты, кнопки, и прочее), так и элементы меню, которые имеют собственные обработчики команд. Для этого в каждой библиотеке предусмотрено две структуры, которые инициализируются в методе Init, такие как: Get_actions и Get_widgets. Эти структуры возвращают список визуальных элементов управления и список обработчиков команд, которые присутствуют в библиотеке.

Для однозначного определения соответствия команды обработчика с функцией, которая его выполняет, нужно создать отдельную структуру. Эта структура содержит название всех команд и указатель на исполняемую функцию. Поскольку планируется, что систему будет расширяться, то и количество модулей будет только возрастать. Для их синхронизации необходимо создать отдельный модуль - ModuleManager, который будет хранить информацию по каждому модулю и указатели на исполняемые функции. Таким образом, когда к системе подключили модуль, затем дали ему команду Init, которая может, например, открыть вкладки или создать новое окно, то ModuleManager будет знать, как работать с обработчиками на новых компонентах.

Организация доступа к функциям

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

Абстрактный класс в объектно-ориентированном программировании - это базовый класс, который не предполагает создания экземпляров. Абстрактные классы реализуют на практике один из принципов ООП - полиморфизм. Абстрактный класс может содержать абстрактные методы и свойства. Абстрактный метод не реализуется для класса, в котором описан, однако должен быть реализован для его неабстрактных потомков. Абстрактные классы представляют собой наиболее общие абстракции, то есть имеющие наибольший объём и наименьшее содержание [7].

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

Основной причиной использования именно абстрактного класса является возможность объявления функций, реализация которых может находиться в а различных библиотеках. Если не использовать абстрактный функций, то, например, для реализации общесистемной возможности записи в лог, то придется добавить реализацию класса ЛОГ во все модули.

2.3 Проектирование ядра системы

Основным компонентов всей системы является ее ядро. Именно с этого модуля начинается логика работы программы. В реализации данной системы, ядро является не визуальным компонентом, поэтому его основные задачи:

.        Запуск ядра системы.

.        Проверка обновлений.

.        Получение обновлений.

.        Установка обновлений.

.        Инициализация библиотек.

.        Передача управления модулю главного окна.

Структурная схема ядра системы представлена на рисунке 2.1.

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

Рисунок 2.1 - Структурная схема ядра системы.

Менеджер модулей

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

Гланый цикл программы (Main Loop)

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

Подсистема работы с параметрами

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

Модуль логирования

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

2.4 Проектирование главного окна системы

В разрабатываемой системе модуль главного окна является основным визуальным модулем, он обеспечивает работу с веб-контентом. Разработка с нуля такого модуля большая и трудная задача, но кроссплатформеннная библиотека Qt в своем составе уже имеет похожее решение. Оно называется «Demo Browser», Снимок экрана работы которого представлен на рисунке 2.2.

Рисунок 2.2 - Снимок экрана работы проекта «Demo Browser»

Данное решение является как кроссплатформенным, так и многофункциональным, а за работу с веб-контентом отвечает ядро CEF (Chrome Embedded Framework). Оно работает со всеми актуальными веб-протоколами, взаимодействует с java-скриптами, а так же имеет в своем составе инструментарий для организации канала связи «Webchannel». Поддерживает как оконную, так и вкладочную модель навигации по веб-страницам. Таким образом «Demo Browser» является отличным модульным решением, которое можно использовать в качестве основы для реализации модуля главного окна системы [8].

Chrome Embedded Framework

Chromium Embedded Framework (CEF) - это проект с открытыми исходными кодами, созданный в 2008 году как элемент управления Web browser, работающий на базе Chromium от Google [9].

Основные возможности фреймворка:

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

.        CEF позволяет делать обертку над нативными функциями в пространстве объектов виртуальной машины Javascript. Ресурсоемкие операции по обработке больших массивов данных можно переложить на более строгие и быстрые языки программирования.

.        CEF позволяет обрабатывать события навигации, скачивания файлов и так далее.

2.5 Проектирования модуля печати

Модуль печати основан на стандартном классе QPrinter, позволяющем организовать обмен данными с принтером, и сторонней библиотеке Debenu PDF Library для работы с PDF файлами [10]. Над ней сделана надстройка, где все вызовы функции приведены к стандартному виду Qt.

Основные возможности Debenu PDF Library:

.        Обширный список функций.

.        Безопасность, подпись и защита PDF-файлов.

.        Создание, заполнение и редактирование PDF-формы.

.        Разделение, слияние, добавление и объединение PDF-файлов.

.        Преобразование EMF в PDF.

.        Извлечение текста и изображений из PDF-файлов.

.        Редактирование начального вида и свойств документов формате PDF.

.        Расширенная поддержка JavaScript, закладок.

.Функция прямого доступа (загружает файлы с диска, а не из памяти).

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

2.6 Допущения и ограничения

Ни при каких условиях несовместимость продукта и модуля не должна приводить к падению того или другого. Проверка версий и совместимости должна проводиться своевременно и безопасно. Разработчиком должны строго выполняться правила ведения версии модуля и обеспечения механизма проверки версии и совместимости.

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

Все методы, функциональность которых подразумевает возможность ошибок или отказа в выполнении запроса, должны возвращать численный результат операции типа CHAR.

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

2.7 Структурная схема системы

На рисунке 2.3 представлена структурная схема системы.

Рисунок 2.3 - Структурная схема системы

2.8 Функциональная схема системы

На рисунке 2.4 представлена функциональная схема системы.

Рисунок 2.4 - Функциональная схема системы

3. РАЗРАБОТКА ОСНОВНЫХ УЗЛОВ СИСТЕМЫ

3.1 Среда разработки Qt Creator

Введение

Qt Creator это полностью интегрированная среда разработки (IDE), которая предоставляет вам инструменты проектирования и разработки сложных приложений для множества настольных и мобильных платформ. На рисунке 3.1 представлен логотип среды разработки Qt Creator с указанием ее основных компонентов [11].

Рисунок 3.1 - Логотип Qt Creator с указанием основных компонентов

На рисунке 3.2 представлен снимок экрана главного окна среды разработки Qt Creator.

Проекты

Одним из главнейших достижений Qt Creator является то, что он позволяет команде разработчиков работать над проектом на различных платформах с использованием общих инструментов для разработки и отладки.

Рисунок 3.2 - Снимок экрана главного окна среды разработки Qt Creator

Но зачем вам нужны проекты? Чтобы быть в состоянии собирать и запускать приложения, Qt Creator нуждается в той же информации, которая потребуется компилятору. Эта информация указана в настройках сборки и запуска проекта.

Создание проекта позволяет:

.        Группировать файлы вместе.

.        Добавить собственные шаги сборки.

.        Включить формы и файлы ресурсов.

.        Указать настройки для запускаемых приложений.

Можно или создать проект с нуля, или импортировать существующий проект. Qt Creator генерирует все необходимые файлы в зависимости от типа создаваемого проекта. Например, если выбрать создание приложения с графическим интерфейсом пользователя (GUI), Qt Creator создаст пустой .ui файл, который вы можете изменить в интегрированном Qt Designer.интегрирован с кроссплатформенными системами автоматизации сборки: qmake и CMake. Также вы можно импортировать существующие проекты, которые не используют qmake или CMake, и указать Qt Creator просто проигнорировать систему сборки [12].

Редакторы

Qt Creator поставляется с редактором кода и Qt Designer для проектирования и сборки графических интерфейсов пользователя (GUI) из виджетов Qt.

Так как он является IDE, Qt Creator отличается от текстового редактора тем, что знает как собирать и запускать приложения. Он понимает языки C++ и QML как код, а не как простой текст. Это позволяет ему:

.        Дать возможность писать хорошо форматированный код.

.        Угадывать что разработчик хочет написать и дополнять код.

.        Отображать сообщения об ошибках и предупреждения.

.        Дать возможность перемещаться между классами, функциями и символами.

.        Предоставлять контекстно-зависимую справку по классам, функциям и символам.

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

.        Показывать место в коде где функция была описана или вызвана.

Так же можете использовать Qt Designer чтобы располагать и настраивать ваши виджеты или диалоги и тестировать их используя разные стили и разрешения экрана. Созданные с помощью Qt Designer виджеты и формы легко интегрируются в программный код с использованием механизма сигналов и слотов Qt, которые позволят легко определить поведение графических элементов. Все свойства, установленные в Qt Designer, могут быть динамически изменены в коде. Более того, такие особенности как продвижение виджетов и собственные модули позволят использовать собственные виджеты с Qt Designer.

Языки

Можно использовать редактор для написания кода на Qt C++ или на языке декларативного программирования QML.

Язык QML позволяет создавать очень гибкий интерфейс пользователя из обширного набора элементов QML. Он помогает разработчикам и дизайнерам работать вместе над созданием гибких пользовательских интерфейсов, которые получат распространение на портативных устройствах, таких как сотовые телефоны, медиаплееры, неттопы и нетбуки.это расширение JavaScript, которое предоставляет механизм декларативной сборки дерева объектов из элементов QML. QML улучшает интеграцию между JavaScript и существующей системой Qt, основанной на QObject, добавляет поддержку автоматического связывания свойств и обеспечивает сетевую прозрачность на уровне языка [13].

Цели

Qt Creator предоставляет поддержку для сборки и запуска приложений на Qt для настольных компьютеров (Windows, Linux и Mac OS) и мобильных устройств (Symbian, Maemo и MeeGo). Настройки сборки позволяют быстро переключаться между целями сборки.

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

3.2 Разработка ядра системы

Разработка главной функции ядра системы

Функция main вызывается при старте программы после инициализации нелокальных объектов со статической длительностью хранения. Это точка входа в программу, которая исполняется в гостевом окружении (то есть с операционной системой). Точки входа в автономные программы (boot loaders, OS kernels, и т.п.) зависят от реализации.

Параметры функции main в варианте с двумя параметрами позволяют передать произвольные многобайтовые строки из окружения выполнения (обычно это аргументы командной строки), указатели (argv[1] и. argv[argc-1]) ссылаются на первые символы этих строк. argv[0] - указатель на первый символ многобабайтовой строки с завершающим нулём, которая содержит имя, используемое при вызове программы. Эти строки изменяемые, хотя их изменения не распространяются назад в окружение выполнения: они могут использоваться, например, в std::strtok. Размер массива, на который указывает argv, равен по меньшей мере argc+1, и последний элемент массива argv[argc] гарантированно является null-указателем.

Функция main обладает следующими специальными свойствами:

.        Она нигде не может быть использована в программе.

.        Её нельзя объявлять и нельзя перегружать: фактически имя main зарезервировано в глобальном пространстве имён.

.        Её нельзя объявить как удалённую или определить со связыванием для C (начиная с C++17), inline, static или constexpr.

.        В теле функции main не обязателен оператор return: при завершении функции main без оператора return эффект будет тот же самый, как при выполнении return 0.

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

.        Если функция main определена как function-try-block, исключения, брошенные деструкторами статических объектов (которые уничтожаются при вызове std::exit), не отлавливаются функцией.

.        Тип возвращаемого значения функцией main не может быть выведен (auto main() {...} не разрешён) [14].

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

Рисунок 3.3 - Блок-схема алгоритма функции main ядра системы

Рисунок 3.4 - Блок-схема алгоритма функции main ядра системы

Рисунок 3.5 - Блок-схема алгоритма функции main ядра системы

На рисунке 3.6 изображен снимок экрана разработанной функции main, вынесенной в одноименный файл main.cpp.

Рисунок 3.6 - Снимок экрана разработанной функции main

Исходный код функции main представлен в приложении А.

Разработка менеджера модулей

Менеджер модулей реализован в виде двух классов GSModulesManager и GSModule. Далее перечислены открытые интерфейсы класса GSModulesManager:

1.      GSModulesManager(const QString &objectName, QObject *parent = Q_NULLPTR).

.        Virtual ~GSModulesManager().

.        GSModule *loadModule(const QString &moduleName="").

.        virtual void loadAllModules(void *abstractInterface=Q_NULLPTR, bool doInit=false).

.        Void unloadModule(GSModule* module).

.        Void unloadAllModules().

.        Void setLog(GSLog *log=Q_NULLPTR).

.        Virtual void init().

.        Virtual void onEvent(const QString &event, char param).

.        Void *getWidget(const QString &widgetID).

.        Char checkUpdates(const QString &listFileName, bool forceUpdate=false).

.        QString& getNewExeName().

.        QString& getInstName().

.        Void exeUpdated(const QString fileName).

.        Void instReceived(const QString fileName).

Открытые интерфейсы класса GSModule:

.        GSModule(const QString &moduleName, const QString &modulesSection,

.        QObject *parent = Q_NULLPTR).

.        Virtual ~GSModule().

.        Char init(void *abstractInterface).

.        Char start().

.        Char stop().

.        QString about(char content).

.        Bool isLoaded().

.        Bool load().

.        Bool unload().

.        Char onEvent(const QString &event, char param).

.        Void *getWidget(const QString &widgetId).

.        Vvoid setLog(GSLog *log=Q_NULLPTR).

На рисунке 3.7 изображен снимок экрана реализованного менеджера модулей, вынесенного в файл gsModule.cpp.

Рисунок 3.7 - Снимок экрана реализованного менеджера модулей

Исходный код менеджера модулей представлен в приложении Б.

Разработка подсистемы работы с параметрами

Подсистема работы с параметрами реализована в виде класса GSApplication. Далее перечислены его открытые интерфейсы:

1.      GSApplication(int &argc, char **argv).

.        Virtual ~GSApplication().

.        Static GSApplication *theApplication().

.        Virtual void saveParams().

.        Virtual void readParams().

.        GSLog *createAppLog(const QString &objectName="").

.        GSLog* getAppLog().

.        GSModulesManager *createModulesManager(const QString &objectName="").

.        QUuid getAppUID().

.        Void isWow64(QString *appDigits).

На рисунке 3.8 изображен снимок экрана реализованной подсистемы работы с параметрами, вынесенной в файл gsApplication.cpp.

Рисунок 3.8 - Снимок экрана реализованной подсистемы работы с параметрами

Исходный код подсистемы работы с параметрами представлен в приложении В.

Разработка подсистемы логирования

Подсистема логирования реализована в виде класса GSLog. Далее перечислены его открытые интерфейсы:

1)      GSLog(QObject *pobj=Q_NULLPTR,const QString &objectName="").

)        Virtual ~GSLog().

)        Bool init(const QString &paramsSection="").

)        Bool setLogFileName(QString &value=QString()).

)        QString getLogFileName().

)        Void setLogLevel(int value).

)        Int getLogLevel().

)        Void setlogMessagesLevel(int value=0).

)        GSLog& toLog(int level=0, QObject *pobj=Q_NULLPTR).

)        GSLog& operator <<(QString &value).

)        GSLog& operator <<(const QString &value).

)        GSLog& operator <<(char *value).

)        GSLog& operator <<(const char *value).

)        GSLog& operator <<(bool value).

)        GSLog& operator <<(int value).

)        GSLog& operator <<(QTextStream &(__cdecl *)(QTextStream &)).

)        GSLog& operator <<(QEvent::Type value.

На рисунке 3.9 изображен снимок экрана реализованной подсистемы логирования, вынесенной в файл gsLog.cpp.

Рисунок 3.9 - Снимок экрана реализованной подсистемы логирования

Исходный код подсистемы логирования представлен в приложении Г.

Разработка модуля обновлений

Модуль обновлений реализован виде класса gsUpdateFiles. Далее перечислены его открытые интерфейсы:

1)      Enum TUpdatePolicy {newOnly,fullUpdate}.

)        GSUpdateFiles(QWidget *parent = 0).

)        Virtual ~GSUpdateFiles().

)        Virtual void getUpdatesList(const QString &listFileName).

)        Virtual void buildUpdatesTree(const QString &listFileName.

)        Const QString &modulesSection.

)        TUpdatePolicy updatePolicy=newOnly).

)        Void setLog(GSLog *log=Q_NULLPTR).

)        Virtual void init(const QString &paramSectionName="").

На рисунке 3.10 изображен снимок экрана реализованного модуля обновления, вынесенного в файл gsUpdateFiles.cpp.

Рисунок 3.10 - Снимок экрана реализованного модуля обновления

Исходный код модуля обновления представлен в приложении Д.

3.3 Разработка модуля главного окна системы и модуля печати

В данной системе модуль главного окна является основным модулем, именно ему предается управление после запуска ядра. На данном этапе модули главного окна и модуль печати объединены в одну библиотеку под названием «InterinClntMainWnd.dll».

Стоит обратить внимание на подсистемы загрузки файлов, печати в файл формата pdf, а так же настройки системы.

Разработка подсистемы загрузки файлов

Подсистема загрузки файлов реализована в виде класса DownloadManager. Далее перечислены его открытые интерфейсы:

1)      void stop();

)        void open();

)        void print();

)        void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);

5)      void finished().

На рисунке 3.11 изображен снимок экрана реализованной подсистемы печати, вынесенной в файл gsUpdateFiles.cpp.

Рисунок 3.11 - Снимок экрана реализованной подсистемы загрузки файлов

Исходный код подсистемы загрузки файлов представлен в приложении Е.

Разработка подсистемы печати

Модуль печати состоит из сторонней библиотеки, над которой сделана надстройка, где прописаны все функции этой библиотеки, а так же вызовы этих функций приведены к стандартному виду qt. Таким образом скрыто низкоуровневое обращение к функциям сторонней библиотеки. Основной функцией модуля печати является получение файла, затем, в зависимости от параметра в адресе страницы, либо сразу отправляется на печать, либо открывается предустановленным в ОС программным обеспечением. Перед отправкой на печать (прозрачной), этот модуль может указать размер бумаги, ее ориентацию, дуплекс и количество копий.

На рисунке 3.12 изображен снимок экрана реализованного диалогового окна печати в файл формата pdf, вынесенного в файл printtopdfdialog.cpp.

Рисунок 3.12 - Снимок экрана реализованного диалогового окна печати в файл формата pdf

Исходный код реализованного диалогового окна печати в файл формата pdf представлен в приложении Ж.

Разработка подсистемы настроек

Настройки всей системы реализованы в реестре операционной системы. Они позволяют не просто настроить систему, но полностью изменить логику ее работы, путем изменения или добавления библиотек.

На рисунке 3.13 изображен снимок экрана реализованной подсистемы настроек вынесенной в файл settings.cpp.

Рисунок 3.13 - Снимок экрана реализованной подсистемы настроек системы

Создание инсталлятора программной системы

Для создания инсталлятора целесообразно воспользоваться готовыми программными средствами, например, Inno Setup. Для работы системы необходим следующий набор компонент:

.        Исполняемый файл ядра системы.

.        Модуль главного окна системы.

.        Функциональные библиотеки.

Скрипт Inno Setup , который содержит свойства дистрибутива и набор производимых действий при установке и удалении представлен в приложении З.

4. ТЕСТИРОВАНИЕ СИСТЕМЫ

.1 Инсталяция программы

Инстлятор клиента представляет собой исполняемый файл с раширением .exe. Его название имеет следующий вид: InterinClient_XX_setup, где XX - версия инсталятора. В процессе установки пользователю предлагается прочесть лицензионное соглашение, выбрать директурю для установки, а так же у пользователя есть возможность вобора название приложения в меню пуск, и необходимость создания ярлыка на рабочем столе. На рисунке 4.1 представлен снимок экрана процесса установки клиента.

Рисунок 4.1 - Снимок экрана процесса установки клиента.

В процессе установки клиента, инсталлятор создает ветвь (\HKEY_CURRENT_USER\Software\Interin) в реестре операционной систем, а в выбранную директорию распаковываются исполняемый файл системы (InterinClient.exe), а так же все необходимые для его работы библиотеки и модули.

4.2 Тестирование функциональной части программы

Функциональное тестирование - процесс проверки соответствия поведения системы первоначально заявленным функциональным требованиям [15].

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

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

Исходные данные и результаты тестирования приведены в Таблице 4.1. Графа оценка определяет соответствие результатов ожидаемым («+» - соответствуют, «-» - нет).

Таблица 4.1 - Исходные данные и результаты тестирования программной системы

Исходные данные

Результат

Оценка

Запуск инсталлятора

Запускается установка клиента,по результатам ее выполнения пользователю предлагается ознакомится с основными ее результатами

+

Запук исполняемого файла клиента

Открывается приложения и начинается проверка обновлений, по ее завершению запускается главное окно

+

Нажатие на пунк меню

Открывается выбранное подменю

+

Изменение параметро системы

После сохранения именений результат успешно применяется

+

Запуск обновления

Запускается подсистема обновлений

+

Измнение размеров окна программы

Изменения применяются, пропорции отображаемого материала сохраняются

+

Нажатие на попункт меню "Печать в PDF"

Открывается диалоговое окно печати с возможностью выбора параметров

+

Нажатие на понкт меню "Помощ"

Открывается информационное окно "О программе"

+

Изменение адреса получения обновлений на локальный

Программа меняет протокол обмена данными и получает обновления из локальной папки

+

Переход по ссылке в МИС на открытие тектового файла

Идет загрузка фала, затем его открытие в выбранном тектовом редакторе.

+

Переход по ссылке в МИС на прямую печать тектового файла

Идет загрузка фала, затем его преобразование в pdf формат и отправка на принтер. От пользователя скрыт весь процесс.


Нажание на подпунк меню "Выход"

Программа закрывается

+

 

4.3 Тестирование модульной расширяемости

Для изменения логики работы всей системы достаточно в ее параметрах указать название другого модуля главного окна. На рисунке 4.2 представлен пример работы главного модуля, которы имеет название "InterinClntMainWnd_2.0.10.dll ".

Рисунок 4.2 - Пример работы модуля "InterinClntMainWnd_2.0.10.dll"

На рисунке 4.3 представлен процесс изменения главного исполнемого модуля на "InterinClntMainWnd_2.0.4.dll".

Рисунок 4.3 - Пример изменения главного исполняемого модуля

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

На рисунке 4.4 представлен пример работы главного модуля, которы имеет название "InterinClntMainWnd_2.0.4.dll ".

Рисунок 4.4 - Пример работы модуля "InterinClntMainWnd_2.0.4.dll"

Для расширения функиционала системы путем добавления бибилотек, а не их изменения потребуется в реестре операционной системы, в ветви "ModulesManeger" создать раздел. В нем разделе необхоимо создать такие параметры как module_path,module_name и version. В module_path необходимо указать полный путь до ибилотеки, а в module_name её полное наименование. После перезапуска программы, функционал реалиованный в данной бибилотеке будет доступен.

 

5. ВНЕДРЕНИЕ


Для внедрения клиента корпоративной информационной системы необходимо происталлировать програмный продукт на все необходимые рабочие места и насроить адрес домашней страницы. Далее, при необходимости, нужно установить программы для работы с тектовыми документами, а так же выбрать нужную в настройках. Для этого в настройках системы, в пункте "Редактор выходных форм" необходимо выбрать исполняемый файл тектового редактора. Данный процесс представлен на рисунке 5.1.

Рисунок 5.1 - Пример выбора редактора выходны фаорм

Вторым этапом внедрения является создание общего ресурса, на котором будет рассположены файлы обновления с их описанием. Разработанная подсистема обновлений позволяет без изменений использовать раличные протоколы обмена данными, такие как ftp,smb,file. Таким образом, необходимо только указать в настройках путь до папки с обновлениями.

На рисунке 5.2 представлен пример отображения медицинской информационной системы Interin PROMIS ALPHA на реализованном в данной работе клиенском приложениии.

Рисунок 5.2 - Вид типовой МИС

 

ЗАКЛЮЧЕНИЕ


В данной магистерской диссертации была проведена работа по анализу методов реализации клиента информационной веб-системы, а так же выбран оптимальный способ его реализации. Была представлена и реализована идея интеграции информационных решений, для получения многофункционального, конкурентноспособного программного продукта. Разработана модульная структура, которая отражена на функциональной и структурной схемах. Она позволяет обеспечить легкое обновление, тиражируемость и масштабируемость всей системы без изменения основных компонент.

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

Полученный программный продукт полностью удовлетворяет всем исходным требования и используется в качестве клиента медицинской информационной системы «Interin PROMIS ALPHA» в двух медицинских организацияхж

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

ПРИЛОЖЕНИЕ


(Обязательное)

Исходный код функции main ядра системы

#include "../src/InterinClntApp/interinclntapp.h"main(int argc, char **argv)

{InterinClntApp *app=Q_NULLPTR;

QString execAfterExit="";

char initRez;

bool done=false;

app=new InterinClntApp(argc, argv);

while(!done){

initRez=app->init();

if(!initRez){

execAfterExit=" Не удалось выполнить инициализацию параметров приложения.\n"

" Работа будет завершена. Обратитесь в техподдержку!";

if(app->getAppLog())

app->getAppLog()->toLog(0)<<execAfterExit<<endl;

QMessageBox::critical(0, QString(" Клиент МИС \"Интерин\" версия ")+

app->applicationVersion(), execAfterExit,

QMessageBox::Ok);

delete app;

return 1;

}

if(initRez==0x01){

if(app->loadMainModule()){

app->exec();

execAfterExit=app->getUpdateCommand();

if(!execAfterExit.isEmpty()){

app->getAppLog()->toLog(9)<<" Перезапускаемся для проверки обновлений.\n "<<execAfterExit<<endl;

initRez=0x02;

}

done=true;

}

else{

execAfterExit=" Не удалось загрузить основной модуль программы.\n"

" Нажмите Ок для загрузки основного модуля с сервера обновлений.\n"

" Нажмите Cancel для завершения работы и обратитесь в техподдержку!.";

if(app->getAppLog())

app->getAppLog()->toLog(0)<<execAfterExit<<endl;

if(QMessageBox::critical(0, QString(" Клиент МИС \"Интерин\" версия ")+

app->applicationVersion(), execAfterExit,

QMessageBox::Ok|QMessageBox::Cancel)!=QMessageBox::Ok)

done=true;

}

}

else{ execAfterExit=app->getUpdateCommand();

app->getAppLog()->toLog(9)<<" Обновились. После завершения выполняем команду\n "<<execAfterExit<<endl;

done=true;

}

}app->saveParams();

delete app;

if(!execAfterExit.isEmpty() && initRez==0x02)

QProcess(0).startDetached(execAfterExit);

return 0;

}

Исходный код менеджера модулей

#include "gsmodule.h"

GSModule::GSModule(const QString &moduleName, const QString &modulesSection,

QObject *parent):

QObject(parent)

{

setObjectName(moduleName);

m_DllInit=Q_NULLPTR;

m_DllStart=Q_NULLPTR;

m_DllStop=Q_NULLPTR;

m_DllAbout=Q_NULLPTR;

m_HandlersArray=Q_NULLPTR;

m_WidgetsArray=Q_NULLPTR;

m_Log=Q_NULLPTR;

m_ModuleParamsSection=modulesSection+"/"+objectName()+"/";

// load();

}::~GSModule()

{

unload();

m_Log=Q_NULLPTR;

}GSModule::init(void *abstractInterface)

{TGetEventsHandlers getHandlers=Q_NULLPTR;

TGetWidgetsList getWidgets=Q_NULLPTR;

QSettings objectParams;

objectParams.setValue(m_ModuleParamsSection+"version", about(ABOUT_VER));

if(!m_DllInit)

return 0x00;

if(!m_DllInit(abstractInterface, &m_ModuleParamsSection))

return 0x00;

getHandlers=(TGetEventsHandlers) m_Library.resolve("getEventsHandlers");

if(getHandlers)

m_HandlersArray=(THandlersArray *)getHandlers();

getWidgets=(TGetWidgetsList) m_Library.resolve("getWidgetsList");

if(getWidgets)

m_WidgetsArray=(TWidgetsArray *)getWidgets();

return 0x01;

}GSModule::start()

{QSettings objectParams;

if(!m_DllStart)

return 0x00;

// objectParams.setValue(m_ModuleParamsSection+"version", about(ABOUT_VER));

return m_DllStart();

}GSModule::stop()

{

m_HandlersArray=Q_NULLPTR;

m_WidgetsArray=Q_NULLPTR;

if(!m_DllStop)

return 0x00;

return m_DllStop();

}GSModule::about(char content)

{QString mess;

if(!m_DllAbout)

return "";

m_DllAbout(content, &mess);

return mess;

}GSModule::isLoaded()

{

return m_Library.isLoaded();

}GSModule::load()

{QSettings objectParams;

if(m_Library.isLoaded())

unload();_Library.setFileName(objectParams.value(m_ModuleParamsSection+"module_path","").toString()+.value(m_ModuleParamsSection+"module_file","").toString());

gsInfo(m_Log,9)<<" Загружаем файл\n "<<m_Library.fileName()<<"\n из секции\n "<<m_ModuleParamsSection<<endl;

if(!m_Library.load()){

objectParams.setValue(m_ModuleParamsSection+"version","");

return false;

}

m_DllInit= (TModuleInit) m_Library.resolve("init");

m_DllStart= (TModuleStart) m_Library.resolve("start");

m_DllStop= (TModuleStop) m_Library.resolve("stop");

m_DllAbout= (TModuleAbout) m_Library.resolve("about");

return true;

}GSModule::unload()

{

if(!m_Library.isLoaded()){

stop();

m_DllInit=Q_NULLPTR;

m_DllStart=Q_NULLPTR;

m_DllStop=Q_NULLPTR;

m_DllAbout=Q_NULLPTR;

return m_Library.unload();

}

return true;

}GSModule::onEvent(const QString &event, char param)

{TEventHandler eventHandler;

if(m_HandlersArray){

eventHandler=m_HandlersArray->value(event,Q_NULLPTR);

if(eventHandler){

eventHandler(param);

return 0x01;

}

}

return 0x00;

}*GSModule::getWidget(const QString &widgetId)

{

if(m_WidgetsArray)

return m_WidgetsArray->value(widgetId,Q_NULLPTR);

return Q_NULLPTR;

}GSModule::setLog(GSLog *log)

{

m_Log=log;

}::GSModulesManager(const QString &objectName, QObject *parent)

: QObject(parent)

{

m_ModulesSection=objectName;

setObjectName(objectName);

m_Log=Q_NULLPTR;

m_newExeName="";

m_instReceivedName="";

}::~GSModulesManager()

{

unloadAllModules();

m_Log=Q_NULLPTR;

}* GSModulesManager::loadModule(const QString &moduleName)

{GSModule* module=Q_NULLPTR;

if(moduleName.isEmpty())

return loadModule(m_MainModuleName);

gsInfo(m_Log,9)<<" Загружаем модуль "<<moduleName<<endl;

for(int i=0; i<m_lModules.size(); i++){

module=m_lModules.at(i);

if(module->objectName() == moduleName){

if(module->isLoaded())

return module;

else{

module->load();

break;

}

}

module=Q_NULLPTR;

}

if(!module){

module=new GSModule(moduleName, m_ModulesSection, this);

module->setLog(m_Log);

module->load();

m_lModules.append(module);

}

return module;

}GSModulesManager::loadAllModules(void *abstractInterface, bool doInit)

{QSettings params;

GSModule* module=Q_NULLPTR;

QStringList modules;

params.beginGroup(m_ModulesSection);

modules=params.childGroups();

int index=modules.indexOf(m_MainModuleName);

if(index>0){

modules[index]=modules[0];

modules[0]=m_MainModuleName;

}

gsInfo(m_Log, 9)<<" Загружаем все модули."<<endl;

for(int i=0; i<modules.size(); ++i){

module=loadModule(modules.at(i));

if(!module->isLoaded()){

gsInfo(m_Log, 9)<<" Модуль "<<modules.at(i)<<" не загружен. Необходимо получить обновление."<<endl;

continue;

}

if(doInit)

if(!module->init((void *)this))

gsInfo(m_Log,0)<<" Не удалось проинициализировать модуль: "<<module->objectName()<<endl;

}

}GSModulesManager::unloadModule(GSModule *module)

{

for(int i = 0; i<m_lModules.size(); ++i){

if(module == m_lModules.at(i)){

module=m_lModules.takeAt(i);

delete module;

return;

}

}

}GSModulesManager::unloadAllModules()

{GSModule *module;

bool ok=false;

gsInfo(m_Log, 9)<<" Выгружаем все модули. Общее число загруженных модулей "<<m_lModules.size()<<"."<<endl;

while(!m_lModules.isEmpty())

delete m_lModules.takeFirst();

}GSModulesManager::setLog(GSLog *log)

{

m_Log=log;

}GSModulesManager::init()

{QSettings params;

params.beginGroup(m_ModulesSection);

m_MainModuleName=params.value("main_module", "MainWindow").toString();

m_AutoDownload=params.value("auto_download", true).toBool();

m_CheckUpdates=params.value("check_updates", true).toBool();

params.setValue("main_module", m_MainModuleName);

params.setValue("auto_download", m_AutoDownload);

params.setValue("check_updates", m_CheckUpdates);

params.endGroup();

}GSModulesManager::onEvent(const QString &event, char param)

{GSModule* module=Q_NULLPTR;

for(int i=0; i<m_lModules.size(); i++){

module=m_lModules.at(i);

if(module->onEvent(event, param))

gsInfo(m_Log, 9)<<" Событие "<<event<<" обработано в модуле "<<module->objectName()<<"."<<endl;

}

}* GSModulesManager::getWidget(const QString &widgetID)

{void *widget=Q_NULLPTR;

for(int i=0; i<m_lModules.size(); i++){

module=m_lModules.at(i);

widget=module->getWidget(widgetID);

if(widget)

return widget;

}

gsInfo(m_Log,9)<<" Элемент графического интерфейса: "<<widgetID<<" не найден!"<<endl;

return Q_NULLPTR;

}GSModulesManager::checkUpdates(const QString &listFileName, bool forceUpdate)

{

if(!m_CheckUpdates && !forceUpdate)

return 0x02;

GSUpdateFiles updateFiles;

updateFiles.setObjectName("updateFiles");

connect(&updateFiles, SIGNAL(exeUpdated(QString)), this,

SLOT(exeUpdated(QString)));

connect(&updateFiles, SIGNAL(instReceived(QString)), this,

SLOT(instReceived(QString)));

updateFiles.setLog(m_Log);

updateFiles.init(m_ModulesSection);

updateFiles.getUpdatesList(listFileName);

updateFiles.buildUpdatesTree(listFileName, m_ModulesSection);

return 0x01;

}&GSModulesManager::getNewExeName()

{

return m_newExeName;

}&GSModulesManager::getInstName()

{

return m_instReceivedName;

}GSModulesManager::exeUpdated(const QString fileName)

{

m_newExeName=fileName;

}GSModulesManager::instReceived(const QString fileName)

{

m_instReceivedName=fileName;

}

Исходный код подсистемы работы с параметрами

#include "GSApplication.h"* GSApplication::theApplication()

{ return (GSApplication *)qApp;}GSApplication::saveParams()

{QSettings params;

params.setValue("version",applicationVersion());

}GSApplication::readParams()

{}* GSApplication::createAppLog(const QString &objectName)

{

if(!m_AppLog)

m_AppLog=new GSLog(this, objectName);

return m_AppLog;

}*GSApplication::getAppLog()

{ return m_AppLog;}* GSApplication::createModulesManager(const QString &objectName)

{

if(!m_ModulesManager)

m_ModulesManager=new GSModulesManager(objectName,this);

return m_ModulesManager;

}GSApplication::getAppUID()

{ return m_Uuid;}::GSApplication(int &argc, char **argv)

: QApplication(argc, argv)

{

m_AppLog=Q_NULLPTR;

m_ModulesManager=Q_NULLPTR;

m_Uuid=QUuid::createUuid();

}::~GSApplication()

{

delete m_ModulesManager;

delete m_AppLog;

}GSApplication::isWow64(QString *appDigits)

{LPFN_ISWOW64PROCESS fnIsWow64Process=Q_NULLPTR;

BOOL bIsWow64=false, ok=false;

*appDigits="_Wx32";

fnIsWow64Process=(LPFN_ISWOW64PROCESS) GetProcAddress(

GetModuleHandle(TEXT("kernel32")),"IsWow64Process");

if(fnIsWow64Process){

if(fnIsWow64Process(GetCurrentProcess(),&bIsWow64)){

ok=true;

if(!bIsWow64)

*appDigits="_Wx64";

}

}

if(!ok)

gsInfo(m_AppLog,9)<<" Не удалось определить разрядность приложения!"<<endl;

}

Исходный код подсистемы логирования

#include "GSLog.h"::GSLog(QObject *pobj, const QString &objectName):QObject(pobj)

{

m_LogFile.setObjectName("LogFile");

m_LogFile.setParent(this);

setObjectName(objectName);

}::~GSLog()

{

m_LogFile.close();

}GSLog::init(const QString &paramsSection)

{QTextCodec *codec = QTextCodec::codecForName("UTF-8");

QTextCodec::setCodecForLocale(codec);

m_ParamsSection=paramsSection;

setLogFileName();

setLogLevel(getLogLevel());

m_OutStream.setCodec(codec);

m_currentLogMessagesLevel=0;

return true;

}GSLog::setLogFileName(QString &value)

{

if(value.isEmpty()){

value=m_Params.value(m_ParamsSection+"/log_path",::writableLocation(QStandardPaths::AppLocalDataLocation)+

QString("/")+qApp->applicationName()+".log").toString();

}

if(m_LogFile.fileName()==value)

return true;

m_LogFile.close();

m_LogFile.setFileName(value);

if(!m_LogFile.exists()){

QFileInfo fileInfo(m_LogFile);

fileInfo.absoluteDir().mkpath(fileInfo.absolutePath());

}

if(!m_LogFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))

return false;

m_OutStream.setDevice(&m_LogFile);

m_Params.setValue(m_ParamsSection+"/log_path",value);

return true;

}GSLog::getLogFileName()

{

return m_LogFile.fileName();

}GSLog::setLogLevel(int value)

{

m_Params.setValue(m_ParamsSection+"/log_level", value);

}GSLog::getLogLevel()

{

return m_Params.value(m_ParamsSection+"/log_level", -1).toInt();

}GSLog::setlogMessagesLevel(int value)

{

m_currentLogMessagesLevel=value;

}&GSLog::toLog(int level, QObject *pobj)

{

m_currentLogMessagesLevel=level;

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<level)

return *this;

m_OutStream<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ")

<<QString("%1 [").arg(level,2);

if(pobj){

m_OutStream<<pobj->objectName();

}

m_OutStream<<"] "<<endl;

// m_OutStream<<" ";

return *this;

}&GSLog::operator <<(QString &value)

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

m_OutStream<<value;

return *this;

}&GSLog::operator <<(const QString &value)

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

m_OutStream<<value;

return *this;

}&GSLog::operator <<(char *value)

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

m_OutStream<<QString(value).toLocal8Bit();

return *this;

}&GSLog::operator <<(const char *value)

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

m_OutStream<<QString(value).toLocal8Bit();

return *this;

}&GSLog::operator <<(bool value)

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

if(value)

m_OutStream<<"Yes";

else

m_OutStream<<"No";

return *this;

}&GSLog::operator <<(int value)

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

m_OutStream<<value;

return *this;

}&GSLog::operator <<(QTextStream &(__cdecl *)(QTextStream &))

{

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

m_OutStream<<endl;

return *this;

}&GSLog::operator <<(QEvent::Type value)

{QString event_type;

if(m_Params.value(m_ParamsSection+"/log_level", -1).toInt()<m_currentLogMessagesLevel)

return *this;

switch (value) {

case QEvent::ActionAdded:

event_type="A new action has been added";

break;

case QEvent::ApplicationActivate:

event_type="Application activated.";

break;

case QEvent::ApplicationDeactivate:

event_type="Application deactivated.";

break;

case QEvent::ApplicationStateChange:

event_type="The state of the application has changed.";

break;

case QEvent::MetaCall:

event_type="An asynchronous method invocation via QMetaObject::invokeMethod().";

break;

case QEvent::Timer:

event_type="Regular timer events.";

break;

default:

event_type=QString("Unknown(")+QString().number(value)+")";

break;

}

m_OutStream<<event_type;

return *this;

}

Исходный код подсистемы обновлений

#include "GSUpdateFiles.h"

GSUpdateFiles::GSUpdateFiles(QWidget *parent):QDialog(parent)

{QStringList headers;

// QPushButton* button;

m_UpdatesTree=new QTreeWidget(this);

headers<<tr("Модуль")<<tr("Версия")<<tr("Тип")

<<tr("Путь на сервере")<<tr("Локальный путь")<<tr("Локальное имя")

<<tr("Локальная версия")<<tr("Принудительно")<<tr("Обновить")

<<tr("Прогресс");

m_UpdatesTree->setColumnCount(headers.count());

m_UpdatesTree->setHeaderLabels(headers);

m_UpdatesTree->setColumnHidden(2, true);

m_UpdatesTree->setColumnHidden(3, true);

m_UpdatesTree->setColumnHidden(4, true);

m_UpdatesTree->setColumnHidden(5, true);

m_UpdatesTree->setColumnHidden(6, true);

m_UpdatesTree->setColumnHidden(7, true);

m_UpdatesTree->setItemDelegateForColumn(9,new ProgressBarDelegate(m_UpdatesTree));

m_UpdatesTree->setColumnWidth(0,300);

QVBoxLayout *vLayout= new QVBoxLayout(this);

vLayout->addWidget(m_UpdatesTree);

QHBoxLayout *hLayout= new QHBoxLayout(this);

hLayout->addStretch(10);

m_Button=new QPushButton(this);

m_Button->setText("Закрыть");

m_Button->setFocus();

connect(m_Button, SIGNAL(clicked(bool)), this, SLOT(close()));

hLayout->addWidget(m_Button);

m_Button->setVisible(false);

vLayout->addLayout(hLayout);

resize(700,300);

setWindowTitle("Доступные обновления");

m_Log=Q_NULLPTR;

}::~GSUpdateFiles()

{

delete m_UpdatesTree;

m_Log=Q_NULLPTR;

}GSUpdateFiles::getUpdatesList(const QString &listFileName)

{QUrl src, dst;

src.setUrl(m_UpdateConnectString+listFileName);

gsInfo(m_Log, 9)<<" Получаем файл списка обновлений "<<src.toDisplayString()<<endl;

src.setUserName(m_UpdateUser);

src.setPassword(m_UpdatePasswd);

dst.setUrl(QString("file:///")+QDir::current().absolutePath()+

"/"+listFileName);

m_ShowDialog="0";

loadUpdate(src, dst);

m_ShowDialog="1";

}GSUpdateFiles::buildUpdatesTree(const QString &listFileName,

const QString &modulesSection,

TUpdatePolicy updatePolicy)

{QFile file(QDir::current().absolutePath()+"/"+listFileName);

QDomDocument updatesDoc;

QString tmpStr;

int errorLine;

int errorColumn;

QTreeWidgetItem *rootItem, *currentItem, *childItem;

QSettings moduleParams;

if(m_NetworkError)

return;

m_ModulesSection=modulesSection;

gsInfo(m_Log, 9)<<" Заполняем список обновлений из файла "<<listFileName<<endl;

if(!file.open(QFile::ReadOnly | QFile::Text)){

tmpStr=" Не удалось открыть файл списка обновлений ";

gsInfo(m_Log, 9)<<tmpStr<<listFileName<<endl;

QMessageBox::critical(0, QString("Ошибка!"), tmpStr, QMessageBox::Ok);

return;

}

if(!updatesDoc.setContent(&file, true, &tmpStr, &errorLine, &errorColumn)){

tmpStr=QString(" Ошибка формата в строке %1, столбец %2:\n%3")

.arg(errorLine)

.arg(errorColumn)

.arg(tmpStr);

gsInfo(m_Log, 9)<<tmpStr<<endl;

QMessageBox::critical(0, QString("Ошибка!"), tmpStr, QMessageBox::Ok);

return;

}

QDomElement root=updatesDoc.documentElement();

if(root.tagName()!="updxml"){

tmpStr=" Формат файла обновлений неизвестен.";

gsInfo(m_Log, 9)<<tmpStr<<endl;

QMessageBox::critical(0, QString("Ошибка!"), tmpStr, QMessageBox::Ok);

return;

}

else

if(root.hasAttribute("version")

&& root.attribute("version") != "1.0"){

tmpStr=" Версия формата файла обновлений не поддерживается.";

gsInfo(m_Log, 9)<<tmpStr<<endl;

QMessageBox::critical(0, QString("Ошибка!"), tmpStr, QMessageBox::Ok);

return;

}

QDomElement moduleElement=root.firstChildElement("module");

rootItem=m_UpdatesTree->invisibleRootItem();

currentItem=Q_NULLPTR;

while(!moduleElement.isNull()){

currentItem=new QTreeWidgetItem(rootItem, currentItem);

currentItem->setText(0, moduleElement.attribute("name"));

currentItem->setText(1, moduleElement.attribute("version"));

currentItem->setText(2, moduleElement.attribute("type"));

currentItem->setText(7, moduleElement.attribute("force"));

currentItem->setText(8, "Да");

if(moduleElement.attribute("type")=="exe")

moduleParams.beginGroup("");

else.beginGroup(modulesSection+"/"+moduleElement.attribute("name"));

currentItem->setText(6, moduleParams.value("version", "").toString());

currentItem->setExpanded(true);

QDomElement child=moduleElement.firstChildElement();

childItem=Q_NULLPTR;

while(!child.isNull()){

childItem=new QTreeWidgetItem(currentItem, childItem);

childItem->setText(0, child.text());

childItem->setText(3, child.attribute("path"));

childItem->setText(4,"file:///"+moduleParams.value("module_path",

QDir::current().absolutePath()+"/").toString());

if(child.tagName()=="file"){

childItem->setText(5, moduleParams.value("module_file", child.text()).toString());

childItem->setText(2, "Основной");

}

else{

childItem->setText(5, child.text());

childItem->setText(2, "Необходимый");

}

child=child.nextSiblingElement();

}

moduleParams.endGroup();

moduleElement=moduleElement.nextSiblingElement("module");

}

checkItemForUpdate(updatePolicy);

}GSUpdateFiles::checkItemForUpdate(TUpdatePolicy updatePolicy)

{QTreeWidgetItem *rootItem, *currentItem;

int updateVersion, localVersion, modulesCount;

QString message;

rootItem=m_UpdatesTree->invisibleRootItem();

modulesCount=rootItem->childCount();

for(int index=0; index<rootItem->childCount(); index++){

currentItem=rootItem->child(index);

update=true;

if(!currentItem->text(6).isEmpty()){

updateVersion=currentItem->text(1).section(".",0,0).toInt()*10000;

localVersion=currentItem->text(6).section(".",0,0).toInt()*10000;

updateVersion=currentItem->text(1).section(".",1,1).toInt()*100;

localVersion=currentItem->text(6).section(".",1,1).toInt()*100;

updateVersion=currentItem->text(1).section(".",2,2).toInt();

localVersion=currentItem->text(6).section(".",2,2).toInt();

update=updateVersion>localVersion;

}

if(update){

currentItem->setText(8,"Да");

currentItem->setText(5, currentItem->text(0));

}

else{

message=" Модуль "+currentItem->text(0)+" "+currentItem->text(1)+

".\n Установлена версия "+currentItem->text(6)+

".\n В обновлении не нуждается.";

gsInfo(m_Log, 9)<<message<<endl;

currentItem->setText(8,message);

if(updatePolicy==newOnly){

currentItem->setHidden(true);

modulesCount--;

}

}

}

if(updatePolicy==newOnly)

m_UpdatesTree->setColumnHidden(8, true);

if(modulesCount){

if(QMessageBox::information(0, QString("Клиент МИС \"Интерин\"."), "Доступны обновления программы.\nОбновление может занять несколько минут. В процессе обновления программа может перезапуститься.\nОбновить сейчас?",

QMessageBox::Ok|QMessageBox::Cancel)==QMessageBox::Ok){

setModal(true);

show();

startTransfer(true);

}

}

}GSUpdateFiles::loadUpdate(const QUrl &src, const QUrl &dst, const QString &type)

{QNetworkReply* networkReply;

QNetworkAccessManager networkAccessManager;

QEventLoop loop;

QTimer timer;

timer.setSingleShot(true);

gsInfo(m_Log,9)<<" Обновляем файл\n src="<<src.toDisplayString()<<

"\n dst="<<dst.toDisplayString()<<"\n type="<<type<<endl;

networkReply=networkAccessManager.get(QNetworkRequest(src));

connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),

this, SLOT(loadError(QNetworkReply::NetworkError)));

if(m_ShowDialog=="1")

connect(networkReply, SIGNAL(downloadProgress(qint64,qint64)),

this, SLOT(transferProgress(qint64,qint64)));

connect(networkReply, SIGNAL(finished()), &loop, SLOT(quit()));

connect(&timer, SIGNAL(timeout()), networkReply, SLOT(abort()));

m_NetworkError=false;

timer.start(m_TimeOut);

loop.exec();

if(!m_NetworkError){

QFile file(dst.toLocalFile());

QFileInfo fileInfo(file);

if(!fileInfo.absoluteDir().exists())

fileInfo.absoluteDir().mkpath(fileInfo.absolutePath());

file.open(QIODevice::WriteOnly);

file.write(networkReply->readAll());

if(type=="exe/Основной")

emit exeUpdated(fileInfo.absoluteFilePath());

if(type=="inst/Основной")

emit instReceived(fileInfo.absoluteFilePath());

}

networkReply->deleteLater();

}GSUpdateFiles::showEvent(QShowEvent *event)

{}GSUpdateFiles::setLog(GSLog *log)

{

m_Log=log;

}GSUpdateFiles::init(const QString &paramSectionName)

{QSettings params;

params.beginGroup(paramSectionName);

m_UpdateConnectString=params.value("update_connect_str", "ftp://interin.rk35.ru/updates/").toString();

m_UpdateUser=params.value("update_user", "interin").toString();

m_UpdatePasswd=params.value("update_passwd", "www.gslobod.ru").toString();

m_BackupDir=params.value("backup_dir",

QDir::current().absolutePath()+"/backup/").toString();

m_ShowDialog=params.value("show_dialog", "1").toString();

m_MaxDownloads=params.value("max_downloads", "5").toString();

m_TimeOut=params.value("time_out", "60000").toLongLong();

params.setValue("update_connect_str", m_UpdateConnectString);

params.setValue("update_user", m_UpdateUser);

params.setValue("update_passwd", m_UpdatePasswd);

params.setValue("backup_dir", m_BackupDir);

params.setValue("show_dialog", m_ShowDialog);

params.setValue("max_downloads", m_MaxDownloads);

params.setValue("time_out", m_TimeOut);

params.endGroup();

}GSUpdateFiles::transferProgress(qint64 bytesReceived, qint64 bytesTotal)

{int childProgressData=0;

if(bytesTotal)

childProgressData=(float(bytesReceived)/bytesTotal*100);

m_DownloadingItem->setText(9, QString::number(childProgressData));

}GSUpdateFiles::startTransfer(bool value)

{QTreeWidgetItem *rootItem, *currentItem, *childItem;

QUrl src, dst;

QSettings moduleParams;

bool hasErrors=false;

QTime timer;

gsInfo(m_Log, 9)<<" Загружаем обновления."<<endl;

m_NetworkError=false;

rootItem=m_UpdatesTree->invisibleRootItem();

for(int index=0; index<rootItem->childCount(); index++){

currentItem=rootItem->child(index);

if(currentItem->isHidden())

continue;

for(int childIndex=0; childIndex<currentItem->childCount(); childIndex++){

childItem=currentItem->child(childIndex);

src.setUrl(childItem->text(3)+childItem->text(0));

src.setUserName(m_UpdateUser);

src.setPassword(m_UpdatePasswd);

dst.setUrl(childItem->text(4)+childItem->text(5));

m_DownloadingItem=childItem;

currentItem->setText(9,"Загружаем обновление.");

loadUpdate(src, dst, currentItem->text(2)+"/"+childItem->text(2));

hasErrors|=m_NetworkError;

if(!m_NetworkError)

currentItem->setText(9,"Обновление загружено.");

if(currentItem->text(2)+"/"+childItem->text(2)=="dll/Основной"){

moduleParams.beginGroup(m_ModulesSection+"/"+currentItem->text(0));

moduleParams.setValue("module_path",childItem->text(4).mid(8));

moduleParams.setValue("module_file",childItem->text(0));

moduleParams.endGroup();

}

}

}

if(!hasErrors){

timer.start();

while(timer.elapsed()<2000)

qApp->processEvents();

close();

}

else{

m_Button->setVisible(true);

exec();

}

}GSUpdateFiles::loadError(QNetworkReply::NetworkError error)

{

gsInfo(m_Log,0)<<" Превышен интервал выполнения запроса!"<<endl;

if(m_ShowDialog=="1")

m_DownloadingItem->parent()->setText(9, "Ошибка!");

m_NetworkError=true;

}GSUpdateFiles::loadTimeOut()

{}::ProgressBarDelegate(QObject *parent)

: QItemDelegate(parent)

{}ProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

{QStyleOptionProgressBar progressBarOption;

if(index.parent()==QModelIndex()){

QItemDelegate::paint(painter, option, index);

return;

}

progressBarOption.state = QStyle::State_Enabled;

progressBarOption.direction = QApplication::layoutDirection();

progressBarOption.rect = option.rect;

progressBarOption.fontMetrics = QApplication::fontMetrics();

progressBarOption.minimum = 0;

progressBarOption.maximum = 100;

progressBarOption.textAlignment = Qt::AlignCenter;

progressBarOption.textVisible = true;

int progress=index.data().toInt();

progressBarOption.progress = progress < 0 ? 0 : progress;

progressBarOption.text = index.data().toString()+" %";

QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);

}

Исходный код подсистемы загрузки файлов

#include "headers/downloadmanager.h"

#include "headers/autosaver.h"

#include "headers/browserapplication.h"

#include <math.h>

#include <QtCore/QMetaEnum>

#include <QtCore/QSettings>

#include <QtGui/QDesktopServices>

#include <QtWidgets/QFileDialog>

#include <QtWidgets/QHeaderView>

#include <QtWidgets/QFileIconProvider>

#include <QtCore/QDebug>

#include <QWebEngineSettings>

#include <QWebEngineDownloadItem>

#include <QMessageBox>

#include "../../Libs/src/WithQT/DebenuPdfLib/DebenuPDFLibraryDLLQt1113.h"::DownloadWidget(QWebEngineDownloadItem *download, QWidget *parent)

: QWidget(parent)

, m_bytesReceived(0)

, m_download(download)

{

setupUi(this);

QPalette p = downloadInfoLabel->palette();

p.setColor(QPalette::Text, Qt::darkGray);

downloadInfoLabel->setPalette(p);

progressBar->setMaximum(0);

connect(stopButton, SIGNAL(clicked()), this, SLOT(stop()));

connect(openButton, SIGNAL(clicked()), this, SLOT(open()));

connect(printButton, SIGNAL(clicked()), this, SLOT(print()));

if (download) {

m_file.setFile(download->path());

m_url = download->url();

}

init();

}DownloadWidget::init()

{

if (m_download) {

connect(m_download.data(), SIGNAL(downloadProgress(qint64,qint64)),

this, SLOT(downloadProgress(qint64,qint64)));

connect(m_download.data(), SIGNAL(finished()),

this, SLOT(finished()));

}

downloadInfoLabel->clear();

progressBar->setValue(0);

getFileName();

// start timer for the download estimation

m_downloadTime.start();

}DownloadWidget::getFileName(bool promptForFileName)

{

QSettings settings;

settings.beginGroup(QLatin1String("downloadmanager"));

QString defaultLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);

if (m_file.absoluteDir().exists())

defaultLocation = m_file.absolutePath();

QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString();

if (!downloadDirectory.isEmpty())

downloadDirectory += QLatin1Char('/');

QString defaultFileName = QFileInfo(downloadDirectory, m_file.fileName()).absoluteFilePath();

QString fileName = defaultFileName;

if (promptForFileName) {

fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);

if (fileName.isEmpty()) {

if (m_download)

m_download->cancel();

fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName()));

return false;

}

}

m_file.setFile(fileName);

if (m_download && m_download->state() == QWebEngineDownloadItem::DownloadRequested)

m_download->setPath(m_file.absoluteFilePath());

fileNameLabel->setText(m_file.fileName());

return true;

}DownloadWidget::stop()

{

setUpdatesEnabled(false);

stopButton->setEnabled(false);

stopButton->hide();

setUpdatesEnabled(true);

if (m_download)

m_download->cancel();

emit statusChanged();

}DownloadWidget::open()

{QString strCommand = "cmd /C ", default_editor="";

QSettings params;

QUrl url = QUrl::fromLocalFile(m_file.absoluteFilePath());

default_editor=params.value("default_editor", "").toString();

if(default_editor.isEmpty()){

QMessageBox::critical(0, "Внимание!", "Не настроен редактор документов.\n Редактирование невозможно.",

QMessageBox::Ok);

return;

}

strCommand+=QString("\"")+default_editor+QString("\" ")+url.toString();

m_process.start(strCommand);

}DownloadWidget::print()

{DebenuPDFLibraryDLLQt1113 pdfTest(QString("DebenuPDFLibraryDLL1113.dll"));

int iOptions;

QString pdfFileName, customPrinter;

QStringList printParams;

QSettings moduleParams;

if(!pdfTest.LibraryLoaded()){

QMessageBox::critical(0, "Печать PDF", "Библиотека не загружена!", QMessageBox::Ok);

return;

}

if(pdfTest.UnlockKey("j39163i38a653748u9f66rb5y")!=1){

QMessageBox::critical(0, "Печать PDF", "Ошибка инициализации!", QMessageBox::Ok);

return;

}

pdfFileName=m_file.absoluteFilePath();

printParams=m_download->url().toDisplayString().section("?",1,1).split("&", QString::SkipEmptyParts);

pdfTest.LoadFromFile(pdfFileName, QString(""));

iOptions=printParams.indexOf("printerName=*");

if(iOptions>-1)=moduleParams.value("Printers/"+printParams.at(iOptions).section("=",1,1),pdfTest.GetDefaultPrinterName()).toString();

else

customPrinter=pdfTest.GetDefaultPrinterName();

customPrinter=pdfTest.NewCustomPrinter(customPrinter);

pdfTest.SetupCustomPrinter(customPrinter,1,9);

pdfTest.SetupCustomPrinter(customPrinter,4,1);

pdfTest.SetupCustomPrinter(customPrinter,7,1);

pdfTest.SetupCustomPrinter(customPrinter,11,1);

for(int i=0; i<printParams.size(); i++){

if(printParams.at(i).contains("copies=")).SetupCustomPrinter(customPrinter,4,printParams.at(i).section("=",1,1).toInt());//Количество копий

if(printParams.at(i).contains("format=")){

if(printParams.at(i).section("=",1,1)=="A5")

pdfTest.SetupCustomPrinter(customPrinter,1,11);

}

if(printParams.at(i).contains("duplex=")){

if(printParams.at(i).section("=",1,1)=="vertical")

pdfTest.SetupCustomPrinter(customPrinter,7,2);

if(printParams.at(i).section("=",1,1)=="horizontal")

pdfTest.SetupCustomPrinter(customPrinter,7,3);

}

if(printParams.at(i).contains("orientation=")){

if(printParams.at(i).section("=",1,1)=="landscape")

pdfTest.SetupCustomPrinter(customPrinter,11,2);

}

}

iOptions=pdfTest.PrintOptions(0, 0, "PDF_MIS");

pdfTest.PrintDocument(customPrinter,1,pdfTest.PageCount(),iOptions);

}DownloadWidget::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)

{

m_bytesReceived = bytesReceived;

if (bytesTotal == -1) {

progressBar->setValue(0);

progressBar->setMaximum(0);

} else {

progressBar->setValue(bytesReceived);

progressBar->setMaximum(bytesTotal);

}

updateInfoLabel();

}DownloadWidget::updateInfoLabel()

{

qint64 bytesTotal = progressBar->maximum();

double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed();

double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed;

QString timeRemainingString = tr("seconds");

if (timeRemaining > 60) {

timeRemaining = timeRemaining / 60;

timeRemainingString = tr("minutes");

}

timeRemaining = floor(timeRemaining);

// When downloading the eta should never be 0

if (timeRemaining == 0)

timeRemaining = 1;

QString info;

if (!downloadedSuccessfully()) {

QString remaining;

if (bytesTotal != 0)

remaining = tr("- %4 %5 remaining")

.arg(timeRemaining)

.arg(timeRemainingString);

info = tr("%1 of %2 (%3/sec) %4")

.arg(dataString(m_bytesReceived))

.arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal))

.arg(dataString((int)speed))

.arg(remaining);

} else {

if (m_bytesReceived != bytesTotal) {

info = tr("%1 of %2 - Stopped")

.arg(dataString(bytesTotal));

} else

info = dataString(m_bytesReceived);

}

downloadInfoLabel->setText(info);

}DownloadWidget::dataString(int size) const

{

QString unit;

if (size < 1024) {

unit = tr("bytes");

} else if (size < 1024*1024) {

size /= 1024;

unit = tr("kB");

} else {

size /= 1024*1024;

unit = tr("MB");

}

return QString(QLatin1String("%1 %2")).arg(size).arg(unit);

}DownloadWidget::downloading() const

{

return (progressBar->isVisible());

}DownloadWidget::downloadedSuccessfully() const

{

bool completed = m_download

&& m_download->isFinished()

&& m_download->state() == QWebEngineDownloadItem::DownloadCompleted;

return completed || !stopButton->isVisible();

}DownloadWidget::finished()

{QString url;

if (m_download) {

QWebEngineDownloadItem::DownloadState state = m_download->state();

QString message;

bool interrupted = false;

switch (state) {

case QWebEngineDownloadItem::DownloadRequested: // Fall-through.

case QWebEngineDownloadItem::DownloadInProgress:

Q_UNREACHABLE();

break;

case QWebEngineDownloadItem::DownloadCompleted:

break;

case QWebEngineDownloadItem::DownloadCancelled:

message = QStringLiteral("Download cancelled");

interrupted = true;

break;

case QWebEngineDownloadItem::DownloadInterrupted:

message = QStringLiteral("Download interrupted");

interrupted = true;

break;

}

if (interrupted) {

downloadInfoLabel->setText(message);

return;

}

}

progressBar->hide();

stopButton->setEnabled(false);

stopButton->hide();

updateInfoLabel();

url=m_download->url().toDisplayString();

if(url.contains("fn=open"))

open();

if(url.contains("fn=print"))

print();

emit statusChanged();

}

/*!

DownloadManager is a Dialog that contains a list of DownloadWidgets

It is a basic download manager. It only downloads the file, doesn't do BitTorrent,

extract zipped files or anything fancy.

*/::DownloadManager(QWidget *parent)

: QDialog(parent)

, m_autoSaver(new AutoSaver(this))

, m_iconProvider(0)

, m_removePolicy(Never)

{

setupUi(this);

downloadsView->setShowGrid(false);

downloadsView->verticalHeader()->hide();

downloadsView->horizontalHeader()->hide();

downloadsView->setAlternatingRowColors(true);

downloadsView->horizontalHeader()->setStretchLastSection(true);

m_model = new DownloadModel(this);

downloadsView->setModel(m_model);

connect(cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup()));

load();

}::~DownloadManager()

{

m_autoSaver->changeOccurred();

m_autoSaver->saveIfNeccessary();

if (m_iconProvider)

delete m_iconProvider;

}DownloadManager::activeDownloads() const

{

int count = 0;

for (int i = 0; i < m_downloads.count(); ++i) {

if (m_downloads.at(i)->stopButton->isEnabled())

++count;

}

return count;

}DownloadManager::download(QWebEngineDownloadItem *download)

{

DownloadWidget *widget = new DownloadWidget(download, this);

addItem(widget);

}DownloadManager::addItem(DownloadWidget *widget)

{

connect(widget, SIGNAL(statusChanged()), this, SLOT(updateRow()));

int row = m_downloads.count();

m_model->beginInsertRows(QModelIndex(), row, row);

m_downloads.append(widget);

m_model->endInsertRows();

updateItemCount();

downloadsView->setIndexWidget(m_model->index(row, 0), widget);

QIcon icon = style()->standardIcon(QStyle::SP_FileIcon);

widget->fileIcon->setPixmap(icon.pixmap(48, 48));

downloadsView->setRowHeight(row, widget->sizeHint().height());

}DownloadManager::updateRow()

{

DownloadWidget *widget = qobject_cast<DownloadWidget*>(sender());

int row = m_downloads.indexOf(widget);

if (-1 == row)

return;

if (!m_iconProvider)

m_iconProvider = new QFileIconProvider();

QIcon icon = m_iconProvider->icon(widget->m_file);

if (icon.isNull())

icon = style()->standardIcon(QStyle::SP_FileIcon);

widget->fileIcon->setPixmap(icon.pixmap(48, 48));

downloadsView->setRowHeight(row, widget->minimumSizeHint().height());

bool remove = false;

if (!widget->downloading()

&& BrowserApplication::instance()->privateBrowsing())

remove = true;

if (widget->downloadedSuccessfully()

&& removePolicy() == DownloadManager::SuccessFullDownload) {

remove = true;

}

if (remove)

m_model->removeRow(row);

cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);

}::RemovePolicy DownloadManager::removePolicy() const

{

return m_removePolicy;

}DownloadManager::setRemovePolicy(RemovePolicy policy)

{

if (policy == m_removePolicy)

return;

m_removePolicy = policy;

m_autoSaver->changeOccurred();

}DownloadManager::save() const

{

QSettings settings;

settings.beginGroup(QLatin1String("downloadmanager"));

QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));

settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy)));

settings.setValue(QLatin1String("size"), size());

if (m_removePolicy == Exit)

return;

for (int i = 0; i < m_downloads.count(); ++i) {

QString key = QString(QLatin1String("download_%1_")).arg(i);

settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url);

settings.setValue(key + QLatin1String("location"), m_downloads[i]->m_file.filePath());

settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully());

}

int i = m_downloads.count();

QString key = QString(QLatin1String("download_%1_")).arg(i);

while (settings.contains(key + QLatin1String("url"))) {

settings.remove(key + QLatin1String("url"));

settings.remove(key + QLatin1String("location"));

settings.remove(key + QLatin1String("done"));

key = QString(QLatin1String("download_%1_")).arg(++i);

}

}DownloadManager::load()

{

QSettings settings;

settings.beginGroup(QLatin1String("downloadmanager"));

QSize size = settings.value(QLatin1String("size")).toSize();

if (size.isValid())

resize(size);

QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray();

QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));

m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ?

Never :_cast<RemovePolicy>(removePolicyEnum.keyToValue(value));

int i = 0;

QString key = QString(QLatin1String("download_%1_")).arg(i);

while (settings.contains(key + QLatin1String("url"))) {

QUrl url = settings.value(key + QLatin1String("url")).toUrl();

QString fileName = settings.value(key + QLatin1String("location")).toString();

bool done = settings.value(key + QLatin1String("done"), true).toBool();

if (done && !url.isEmpty() && !fileName.isEmpty()) {

DownloadWidget *widget = new DownloadWidget(0, this);

widget->m_file.setFile(fileName);

widget->fileNameLabel->setText(widget->m_file.fileName());

widget->m_url = url;

widget->stopButton->setVisible(false);

widget->stopButton->setEnabled(false);

widget->progressBar->hide();

addItem(widget);

}

key = QString(QLatin1String("download_%1_")).arg(++i);

}

cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);

}DownloadManager::cleanup()

{

if (m_downloads.isEmpty())

return;

m_model->removeRows(0, m_downloads.count());

updateItemCount();

if (m_downloads.isEmpty() && m_iconProvider) {

delete m_iconProvider;

m_iconProvider = 0;

}

m_autoSaver->changeOccurred();

}DownloadManager::updateItemCount()

{

int count = m_downloads.count();

itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count));

}::DownloadModel(DownloadManager *downloadManager, QObject *parent)

: QAbstractListModel(parent)

, m_downloadManager(downloadManager)

{}DownloadModel::data(const QModelIndex &index, int role) const

{

if (index.row() < 0 || index.row() >= rowCount(index.parent()))

return QVariant();

if (role == Qt::ToolTipRole)

if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully())

return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text();

return QVariant();

}DownloadModel::rowCount(const QModelIndex &parent) const

{ return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count();}DownloadModel::removeRows(int row, int count, const QModelIndex &parent)

{

if (parent.isValid())

return false;

int lastRow = row + count - 1;

for (int i = lastRow; i >= row; --i) {

if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully()) {

beginRemoveRows(parent, i, i);

m_downloadManager->m_downloads.takeAt(i)->deleteLater();

endRemoveRows();

}

}

m_downloadManager->m_autoSaver->changeOccurred();

return true;

}

Исходный код диалогового окона печати в pdf

#include "headers/printtopdfdialog.h"

#include "ui_printtopdfdialog.h"

#include <QtCore/QDir>

#ifndef QT_NO_PRINTER

#include <QtPrintSupport/QPageSetupDialog>

#include <QtPrintSupport/QPrinter>

#endif // QT_NO_PRINTER

#include <QtWidgets/QFileDialog>::PrintToPdfDialog(const QString &filePath, QWidget *parent) :

QDialog(parent),

currentPageLayout(QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0))),

ui(new Ui::PrintToPdfDialog)

{

ui->setupUi(this);

setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

connect(ui->chooseFilePathButton, &QToolButton::clicked, this, &PrintToPdfDialog::onChooseFilePathButtonClicked);

#ifndef QT_NO_PRINTER

connect(ui->choosePageLayoutButton, &QToolButton::clicked, this, &PrintToPdfDialog::onChoosePageLayoutButtonClicked);

#else

ui->choosePageLayoutButton->hide();

#endif // QT_NO_PRINTER

updatePageLayoutLabel();

setFilePath(filePath);

}::~PrintToPdfDialog()

{

delete ui;

}PrintToPdfDialog::onChoosePageLayoutButtonClicked()

{

#ifndef QT_NO_PRINTER

QPrinter printer;

printer.setPageLayout(currentPageLayout);

QPageSetupDialog dlg(&printer, this);

if (dlg.exec() != QDialog::Accepted)

return;

currentPageLayout.setPageSize(printer.pageLayout().pageSize());

currentPageLayout.setOrientation(printer.pageLayout().orientation());

updatePageLayoutLabel();

#endif // QT_NO_PRINTER

}PrintToPdfDialog::onChooseFilePathButtonClicked()

{

QFileInfo fi(filePath());

QFileDialog dlg(this, tr("Save PDF as"), fi.absolutePath());

dlg.setAcceptMode(QFileDialog::AcceptSave);

dlg.setDefaultSuffix(QStringLiteral(".pdf"));

dlg.selectFile(fi.absoluteFilePath());

if (dlg.exec() != QDialog::Accepted)

return;

setFilePath(dlg.selectedFiles().first());

}PrintToPdfDialog::filePath() const

{

return QDir::fromNativeSeparators(ui->filePathLineEdit->text());

}PrintToPdfDialog::setFilePath(const QString &filePath)

{

ui->filePathLineEdit->setText(QDir::toNativeSeparators(filePath));

}PrintToPdfDialog::pageLayout() const

{

return currentPageLayout;

}PrintToPdfDialog::updatePageLayoutLabel()

{

ui->pageLayoutLabel->setText(QString("%1, %2").arg(

currentPageLayout.pageSize().name()).arg(

currentPageLayout.orientation() == QPageLayout::Portrait

? tr("Portrait") : tr("Landscape")

));

}

Скрипт Inno Setup

#define Name "InterinClient"

#define Version "2.0.5"

#define Publisher "ООО Интерин технологии"

#define URL "http://www.interin.ru"

#define ExeName "InterinClient.exe"

[Setup]={{89ED2222-8C22-4F0B-A1CC-84770F5498F3}}={#Name}={#Version}={#Publisher}={#URL}={#URL}={#URL}={#Name} версия {#Version}=c:\Interin\InterinAlpha=Interin={app}\{#ExeName}=D:\repos\Development\Builds\Windows\x32\InterinClient\install={#Name}_{#Version}_setup=lzma=yes

;DiskSpanning=yes

;SlicesPerDisk=3

;DiskSliceSize=10000000

;ArchitecturesAllowed=x64

;ArchitecturesInstallIn64BitMode=x64

[Languages]: "english"; MessagesFile: "compiler:Default.isl"; LicenseFile: "License_ENG.txt": "russian"; MessagesFile: "compiler:Languages\Russian.isl"; LicenseFile: "License_RUS.txt"

[Tasks]: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; Flags: unchecked

[Files]: "D:\repos\Development\Builds\Windows\x32\InterinClient\release\{#ExeName}"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "D:\repos\Development\Projects\InterinClient\setup\qt.conf"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "D:\repos\Development\Projects\InterinClient\docs\changelog.txt"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "D:\repos\Development\Builds\Windows\x32\InterinClntMainWnd\release\InterinClntMainWnd.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs : "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\libEGL.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\libGLESv2.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\msvcp120.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\msvcr120.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\opengl32sw.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Core.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Gui.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Network.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Qml.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Quick.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5WebChannel.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5WebEngineCore.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5WebEngineWidgets.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Widgets.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5Xml.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\QtWebEngineProcess.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\bin\Qt5PrintSupport.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\plugins\platforms\qminimal.dll"; DestDir: "{app}\plugins\platforms"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\plugins\platforms\qoffscreen.dll"; DestDir: "{app}\plugins\platforms"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\plugins\platforms\qwindows.dll"; DestDir: "{app}\plugins\platforms"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\resources\*"; DestDir: "{app}\resources"; Flags: ignoreversion recursesubdirs createallsubdirs: "C:\Qt\Qt5.7.0\5.7\msvc2013\translations\qtwebengine_locales\*"; DestDir: "{app}\translations\qtwebengine_locales"; Flags: ignoreversion recursesubdirs createallsubdirs

;Source: "E:\install\dotNetFx40_Full_x86_x64.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall; Check: not IsRequiredDotNetDetected

[Registry]: HKCU; Subkey: Software\Interin; Flags: createvalueifdoesntexist uninsdeletekey;: HKCU; Subkey: Software\Interin\InterinClient; Flags: createvalueifdoesntexist;: HKCU; Subkey: Software\Interin\InterinClient\ModulesManager; Flags: createvalueifdoesntexist;: HKCU; Subkey: Software\Interin\InterinClient\ModulesManager; ValueType: string; ValueName: main_module; ValueData: MainWindow; Flags: createvalueifdoesntexist;: HKCU; Subkey: Software\Interin\InterinClient\ModulesManager\MainWindow; Flags: createvalueifdoesntexist;: HKCU; Subkey: Software\Interin\InterinClient\ModulesManager\MainWindow; ValueType: string; ValueName:module_file; ValueData: InterinClntMainWnd.dll; Flags: createvalueifdoesntexist;: HKCU; Subkey: Software\Interin\InterinClient\ModulesManager\MainWindow; ValueType: string; ValueName: module_path; ValueData: c:/Interin/InterinAlpha/; Flags: createvalueifdoesntexist;

[Icons]: "{group}\Interin"; Filename: "{app}\{#ExeName}": "{group}\{cm:UninstallProgram, {#Name}}"; Filename: "{uninstallexe}": "{userdesktop}\Интерин Alpha"; Filename: "{app}\{#ExeName}"; Tasks: desktopicon

;[Messages]

;#include "messages_rus.txt"

;[Code]

;#include "InterinClient.pas"

[Run]

;Filename: regedit.exe; Parameters: "/s {tmp}\registry.reg";

;Filename: {tmp}\dotNetFx40_Full_x86_x64.exe; Parameters: "/q:a /c:""install /l /q"""; Check: not IsRequiredDotNetDetected; StatusMsg: Microsoft Framework 4.0 is installed. Please wait...

[UninstallRun]: regedit.exe; Parameters: "/s -HKEY_CURRENT_USER\Software\Interin";

Похожие работы на - Разработка и реализация программных средств для работы с веб-контентом в рамках проекта INTERIN PROMIS

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!