Posts Tagged Java
Сравнение Qt и Java
В этой статье сравнивается эффективность использования C++/Qt и Java/AWT/Swing для разработки программного обеспечения с пользовательским графическим интерфейсом.
1. Что мы сравниваем?
При выборе средств для разработки крупного программного проекта необходимо учесть множество различных аспектов, наиболее важнейшим из которых является язык программирования, потому что он в значительной степени определяет другие доступные средства. Например, для разработки пользовательского графического интерфейса разработчикам необходима GUI-библиотека, предоставляющая готовые элементы интерфейса, такие, как кнопки и меню. Так как выбор GUI-библиотеки оказывает большое влияние на разработку проекта, часто ее выбор осуществляется первым, а язык программирования определяется из числа доступных для этой библиотеки языков. Обычно, язык программирования определяется библиотекой однозначно.
Другие компоненты средств разработки, такие, как библиотеки доступа к базам данных или библиотеки коммуникаций, также должны быть приняты во внимание, но они не оказывают такого влияния на разработку проекта, как библиотеки GUI.
Целью этой статьи является сравнение C++/Qt и Java/AWT/Swing. Чтобы это сделать наиболее точно, мы сначала сравним языки программирования, то есть C++ и Java, а потом две GUI-библиотеки: Qt для C++ и AWT/Swing для Java.
2. Сравнение C++ и Java
Часто при обсуждении преимуществ и недостатков различных языков программирования дебаты сводятся к аргументам, основанным скорее на личном опыте и предпочтениях, чем на объективных критериях. Конечно же, при выборе языка программирования личные предпочтения и опыт разработчика должны быть учтены, но так как эти критерии субъективны, они здесь не принимаются во внимание. Вместо этого мы будем рассматривать продуктивность программирования, производительность работы приложения и эффективность использования памяти, потому что эти критерии могут быть определены количественно и могут быть исследованы с научной точки зрения, хотя мы также учтем информацию, полученную из опыта разработки проектов в нашей компании.
2.1. Продуктивность программирования
Продуктивность программирования определяет, насколько эффективно (т.е. быстро и точно) программист с определенным опытом и знаниями может решить поставленную перед ним задачу, используя заданный язык программирования. Так как оклад разработчика является главной составляющей стоимости разработки любого программного проекта, продуктивность программирования имеет большое значение. Также в определенной степени продуктивность программирования определяется доступными инструментальными средствами.
Отличительной особенностью Java в сравнении с другими языками программирования общего назначения является обеспечение высокой продуктивности программирования, нежели производительность работы приложения или эффективность использования им памяти.
Для этого Java наделена некоторыми дополнительными возможностями. Например, в отличие от C++ (или C), программист не должен в явном виде «освобождать» (возвращать) выделенную память операционной системе. Освобождение неиспользуемой памяти (сборка «мусора») автоматически обеспечивается средой выполнения Java в ущерб производительности и эффективности использования памяти (см. далее). Это освобождает программиста от утомительной задачи по слежению за освобождением памяти – главного источника ошибок в приложениях. Одна эта возможность языка должна значительно увеличить продуктивность программирования в сравнении с C++ (или C).
Однако проведенное исследование показывает, что на практике сборка «мусора» и другие возможности Java не оказывают большого влияния на продуктивность программирования. Одна из классических моделей оценки программного обеспечения CoCoMo, предложенная Barry Boehm, предопределяет стоимость и сроки разработки программного продукта на основе стоимостных коэффициентов, которые учитывают такие факторы, как суммарный опыт программирования разработчика, опыт программирования на заданном языке, желаемая надежность программы и т.д. Boehm пишет, что независимо от уровня используемого языка, начальные трудозатраты всегда высокие. Подобная методика подсчета использовалась в другом исследовании, проведенном C.E.Walston и C.P.Felix, IBM, Метод измерения и оценки программирования (A method of programming measurement and estimation) .
Оба приведенных здесь исследования появились задолго до создания Java, но несмотря на это, они демонстрируют общий принцип: сложность языка программирования общего назначения по сравнению с другими аспектами, такими как квалификация разработчика, не оказывает существенного влияния на полную стоимость разработки проекта.
Существует более позднее исследование, которое явно включает Java и которое подтверждает эту гипотезу. В Эмпирическом сравнении C, C++, Java, Perl, Python, Rexx и Tcl (An empirical comparison of C, C++, Java, Perl, Python, Rexx, and Tcl) Lutz Prechelt из университета Karlsruhe описывает проведенный им эксперимент, в котором студентам информатики поручили выполнить определенный проект и выбрать для его реализации, руководствуясь личными предпочтениями, один из языков программирования: C, C++ или Java (остальные языки были рассмотрены в другой части исследования). Собранные данные показали почти одинаковые результаты для C++ и Java (C был на третьем месте по многим параметрам). Эти результаты подтверждаются нашим собственным опытом: если программисты вольны в самостоятельном выборе языка программирования (чаще руководствуясь при этом своим опытом), программисты с равным опытом работы (например, измеренным в годах) достигают одной и той же продуктивности. Второй интересный аспект, который бы мы хотели отметить (но который не имеет формального экспериментального подтверждения), заключается в том, что менее опытные разработчики достигают лучших результатов с Java, разработчики со средним опытом разработки достигают одинаковых результатов с обоими языками программирования, опытные разработчики достигают лучших результатов с C++. Эти наблюдения могут быть объяснены тем, что для C++ доступны более совершенные средства разработки; и этот факт тоже должен быть принят во внимание.
Интересный способ определения продуктивности программирования предлагает метод функциональных единиц (Function Point), разработанный Capers Jones. Функциональная единица – это метрика программного обеспечения, которая зависит лишь от его функциональности, а не от конкретной реализации. Эта метрика позволяет использовать в качестве критерия оценки продуктивности программирования число строк кода, необходимых для обеспечения одной функциональной единицы, в свою очередь, уровень языка определяется числом функциональных единиц, которые могут быть созданы за определенное время. Интересно, что обе величины: число строк кода на единицу функциональности и уровень языка одинаковы для обоих языков (уровень языка: C++ и Java – 6, C – 3.5, Tcl – 5; число строк кода на единицу функциональности: C++ и Java – 53, C – 91, Tcl – 64).
Подводя итог: оба исследования и практика опровергают утверждение, что Java обеспечивает программистам лучшую продуктивность программирования, нежели C++.
2.2. Производительность работы приложений
Мы увидели, что преимущества продуктивности программирования на Java оказались иллюзорными. Теперь мы исследуем производительность работы приложений.
И снова Prechelt предоставляет интересные сведения. Объем предлагаемой им информации огромен, но в конечном итоге он приходит к заключению, что «Java-программы выполняются по крайней мере в 1.22 раза медленнее C/C++ программ». Заметьте, что он сказал по крайней мере; средняя же скорость работы Java-программ гораздо меньше. Наш собственный опыт показывает, что Java-программы выполняются приблизительно в 2-3 раза медленнее своих C/C++ аналогов. На задачах, ориентированных на интенсивное использование процессора, Java-программы проигрывают еще сильнее.
В случае программ с пользовательским графическим интерфейсом увеличение времени отклика интерфейса является более критичным, чем низкая производительность программы. Проведенные исследования показывают, что пользователи более терпимы к задачам, выполняющимся в течение двух или трех минут, чем к программам, которые не реагируют мгновенно на их воздействия, например, на нажатия кнопок. Эти исследования показывают, что если время отклика программы больше, чем 0,7 секунды, пользователи считают ее медленной. Мы вернемся к этой проблеме, когда будем сравнивать пользовательский графический интерфейс в программах Java и C++.
Объяснение того, почему Java-программы медленнее C++ проограмм, заключается в следующем. C++ программы компилируются компилятором C++ в двоичный формат, который затем исполняется непосредственно процессором; таким образом, выполнение программы осуществляется аппаратными средствами. (Это несколько упрощенно, так как большинство современных процессоров выполняют микрокод, но это не принципиально при обсуждении данного вопроса.) С другой стороны, компилятор Java компилирует исходный код в «байт-код», который непосредственно исполняется не процессором, а с помощью другого программного обеспечения, виртуальной машины Java (Java Virtual Machine, JVM). В свою очередь, JVM исполняется процессором. Таким образом, выполнение байт-кода Java-программ осуществляется не быстрыми аппаратными средствами, а с помощью более медленной программной эмуляции.
Для повышения производительности работы Java-программ были разработаны «Just in Time» (JIT) компиляторы, но универсального решения этой проблемы не существует.
На первый взгляд, полуинтерпретируемая природа Java-программ обеспечивает выполнение принципа «скомпилированный однажды код выполняется везде». Однажды скомпилированная в байт-код Java-программа может выполняться на любой платформе, для которой доступна JVM. На практике же, это не всегда так из-за отличий в реализациях разных JVM и из-за необходимости иногда наряду с Java-программами использовать родной, не-Java код, обычно написанный на C или C++.
Но разве использование платформенно-независимого байт-кода является верным подходом в создании кросс-платформенных приложений? С хорошим кросс-платформенным инструментарием, наподобие Qt, и хорошими компиляторами для различных платформ программисты могут достичь почти той же цели компиляцией своего исходного кода один раз для каждой из платформ: «написанный однажды код компилируется везде». Можно возразить, что для этого разработчикам потребуется доступ ко всем поддерживаемым платформам, в то время, как с Java, теоретически, разработчикам необходим доступ только к одной из платформ, имеющей средства разработки для Java и JVM. На практике же ни один из ответственных производителей программного обеспечения не будет сертифицировать свои программные продукты для платформ без предварительного их тестирования, поэтому в любом случае производителям будет необходим доступ ко всем поддерживаемым платформам.
Возникает вопрос, зачем использовать программную реализацию виртуальной машины Java, если такую же функциональность можно получить с помощью аппаратной реализации? Именно так рассуждали разработчики при создании языка Java; они предполагали, что вопрос низкой производительности будет решен, когда станет доступной аппаратная реализация JVM в виде Java-процессоров. Однако даже по прошествии пяти лет Java-процессоры не получили широкого распространения. Существуют проектные экземпляры и даже работающие прототипы Java-процессоров, однако понадобится еще немало времени, чтобы стало возможным их приобрести.
2.3. Эффективность использования памяти
Java и C++ используют различные подходы в управлении памятью. В C++ управление памятью полностью осуществляется программистом, т.е. по мере необходимости распределение и освобождение памяти должно выполняться программистом. Если программист забывает освободить ранее полученную память, возникает «утечка памяти». Если во время работы приложения произойдет лишь одна такая утечка, проблем не возникнет, так как после завершения работы приложения операционная система освободит всю ранее использованную им память. Но если утечки памяти будут происходить постоянно (например, если пользователь будет периодически выполнять определенные действия), использование памяти приложением будет расти вплоть до полного ее расхода с последующим возможным отказом системы.
Java обеспечивает автоматическое освобождение неиспользуемой памяти. Наряду с распределением памяти программистом JVM ведет учет всех используемых блоков памяти и указателей на них. Если блок памяти больше не используется, он может быть освобожден. Это обеспечивает процесс, который называется «сборкой мусора». Он периодически вызывается JVM, проверяет все используемые блоки памяти и освобождает те из них, на которые отсутствуют указатели.
Сборка мусора очень удобна, но за ее использование приходится расплачиваться большим потреблением памяти и низкой произодительностью… Программисты C++ могут (и должны) освобождать блоки памяти сразу после того, как они перестали быть нужны. С Java блоки не освобождаются до очередного вызова сборщика мусора, периодичность работы которого зависит от использумой реализации JVM. Prechtelt предоставляет цифровые данные, утверждая, что в среднем, (…) и с вероятностью 80% Java-программы используют на 32 MB (или 297%) памяти больше, чем C/C++ программы (…). Вдобавок к большому расходу памяти процесс сборки мусора требует дополнительной процессорной мощности, которая в результате становится недоступной приложению, и это приводит к замедлению его работы. Поэтому периодическая работа сборщика мусора может приводить к «замораживанию» Java-программы на несколько секунд. Лучшие реализации JVM минимизируют такие замораживания, но не устраняют их полностью.
При работе с внешними программами и устройствами, например, во время ввода/вывода или при взаимодействии с базой данных, желательно закрыть файл или соединение с базой данных сразу же после того, как они перестали быть нужны. Благодаря деструкторам C++ это происходит сразу после вызова delete. В Java закрытие произойдет лишь во время следующего цикла работы сборщика мусора. В лучшем случае это может привести к излишней блокировке ресурсов, в худшем – к нарушению целостности открытых ресурсов.
Тот факт, что Java-программы используют блоки памяти большие, чем необходимо, является особенно критичным для встраиваемых устройств, объемы памяти которых невелики. Неслучайно, что до сих пор (на время написания этой статьи) не существует полной реализации Java-платформы для встраиваемых устройств, а лишь ее частичные реализации.
Главная причина, по которой сборка мусора является более дорогостоящей, чем непосредственное управление памятью программистом, – это утрата информации. В C++ программе программист знает и местонахождение своих блоков памяти (сохраняя указатели на них), и когда они перестанут быть ему нужными. В Java-программе последняя информация недоступна для JVM (даже если она известна программисту), поэтому JVM должна перебирать все блоки на предмет отсутствующих указателей. Для того, чтобы вызвать сборку мусора вручную, Java-программист может удалить все указатели на больше ненужные ему блоки памяти. Но со стороны программиста это потребует больше усилий, чем непосредственное управление памятью в C++; и тем не менее, во время сборки мусора JVM все равно придется проверить все блоки памяти, чтобы освободить неиспользуемые.
С технической точки зрения, нет ничего такого, что бы мешало реализовать сборку мусора в C++ программах. Существуют обеспечивающие это коммерческие программы и библиотеки. Но из-за перечисленных выше недостатков немногие C++ программисты используют их. Инструментарий Qt использует более эффективный подход для упрощения задачи управления памятью: при удалении объекта все зависящие от него объекты также автоматически удаляются. Подход Qt не мешает программистам по желанию самостоятельно удалять объекты.
Так как управление памятью в C и C++ обременительно для программиста, созданное с помощью этих языков программное обеспечение обвиняется в нестабильной работе и подверженности ошибкам. Хотя некорректная работа с памятью в C и C++ может привести к более критичным ошибкам (обычно приводящим к аварийному завершению программы), хорошие знания, инструментарий и опыт могут значительно уменьшить связанный с этим риск. Изучению управления памятью должно уделяться достаточно внимания. Также существует большое число коммерческих и свободных инструментов, позволяющих программистам обеспечить отсутствие в программах ошибок при работе с памятью; например, Parasoft Insure++, Rational Purify и Electric Fence. Гибкая система управления памятью в C++ делает возможным создавать адаптированные для любого типа приложений профилировщики памяти.
В результате этого обсуждения мы убедились в том, что при сравнимой продуктивности программирования C++ обеспечивает приложениям гораздо лучшие, чем Java, производительность работы и эффективность использования памяти.
2.4. Доступные библиотеки и инструментарий
Java-платформа предлагает внушительное число пакетов, насчитывающих сотни классов для любых задач, включая пользовательский графический интерфейс, безопасность, поддержку сети и прочие. Это несомненное преимущество Java-платформы. Любому Java-пакету соответствует, как минимум, одна C++ библиотека, хотя иногда бывает очень трудно собрать в одном C++ проекте множество библиотек и заставить их вместе правильно работать.
Однако это преимущество Java является также ее недостатком. Разобраться в огромном API программисту становится все сложнее. Можете быть наверняка уверены, что для любой задачи всегда найдется уже готовое решение или, по крайней мере, решение, облегчающее выполнение этой задачи. Но найти пригодный для этого пакет и класс становится все труднее. Также с увеличением числа пакетов стремительно растет размер Java-платформы. В результате стали возникать ее «урезанные» версии, утратившие преимущества готовых решений. Таким образом, размер Java-платформы делает практически невозможным создание Java-систем небольшими производителями независимо от создателя Java Sun Microsystems, что ослабляет конкуренцию.
Если преимущества Java заключаются в доступных библиотеках, то явные преимущества C++ – в имеющихся средствах разработки. За все время существования семейства языков C и C++ было создано огромное количество самых разнообразных средств разработки, включая инструменты для дизайна, отладки и профилирования. Имеющиеся средства разработки для Java часто уступают по возможностям своим C++ -аналогам. Это справедливо даже для инструментов от одного производителя; например, сравните Java и C/C++ -версии профилировщика Rational Quantify.
Самым важным инструментом для любого разработчика, использующего компилируемые языки, является компилятор. Основным достоинством компиляторов C++ является скорость работы. Для обеспечения кросс-платформенности своих компиляторов (и других средств разработки) производители Java-инструментов часто сами используют Java, со всеми вытекающими отсюда проблемами производительности и эффективности использования памяти. Иногда встречаются Java-компиляторы, написанные на C/C++ (например, IBM Jikes), но это редкость.
3. Сравнение AWT/Swing и Qt
До сих пор мы сравнивали лишь языки программирования Java и C++. Но, как мы упомянули в начале этой статьи, язык программирования является лишь одним из аспектов, принимаемых во внимание при разработке GUI. Теперь мы сравним пакеты для разработки GUI, которые поставляются вместе с Java, т.е. AWT и Swing, и кросс-платформенный инструментарий Qt от норвежского производителя Trolltech. В сравнении мы ограничились лишь одним инструментарием C++, потому что в отличие от MFC (Microsoft Foundation Classes) и других подобных библиотек, Qt поддерживает все 32-битные Windows-платформы (кроме NT 3.5x), большинство разновидностей Unix, включая Linux, Solaris, AIX и Mac OS X, и встраиваемые платформы. Это позволяет максимально близко сопоставить платформы Java/AWT/Swing и C++/Qt.
3.1. Описание AWT, Swing и Qt
Инструментарий AWT (Abstract Windowing Toolkit) начал поставляться с самой первой версией Java. Он использует родные для платформ компоненты GUI (т.е. Win32 API для Windows и библиотеку Motif для Unix), обеспечивая таким образом переносную обертку. Это значит, что внешний вид и поведение AWT-программ будет отличаться на различных платформах, потому что именно они занимаются отрисовкой и управлением компонентов GUI. Это противоречит кросс-платформенной философии Java и может быть объяснено тем, что первая версия AWT была разработана за четырнадцать дней.
По этой и другим причинам AWT был дополнен инструментарием Swing. Swing использует AWT (и, следовательно, низкоуровневые библиотеки) только лишь для базовых операций: создания прямоугольных окон, управления событиями и отрисовки графических примитивов. Всем остальным, включая отрисовку компонентов GUI, занимается Swing. Это решает проблему отличающегося внешнего вида и поведения приложений на различных платформах. Но из-за реализации Swing-инструментария средствами Java его производительность оставляет желать лучшего. В результате Swing-программы медлительны не только во время интенсивных вычислений, но и при отрисовке элементов пользовательского интерфейса. Как уже говорилось, ничто не вызывает у пользователей такого раздражения, как большое время отклика интерфейса программ. Странно наблюдать за медлительностью перерисовки Swing -кнопки на современном оборудовании. Хотя с ростом производительности оборудования эта ситуация будет постепенно улучшаться, сложным пользовательским интерфейсам, созданным с помощью Swing, всегда будет свойственна медлительность.
При разработке инструментария Qt был использован тот же самый подход: низкоуровневые библиотеки используются только лишь для базовых операций, а отрисовкой элементов GUI занимается непосредственно Qt. Благодаря этому инструментарий Qt приобретает все преимущества Swing (например, схожесть поведения и внешнего вида приложений на различных платформах), и не имеет проблем, связанных с низкой производительностью, так как разработан на C++ и откомпилирован в машинный код. Интерфейс, созданный с помощью Qt, отличается быстрой работой, и, благодаря использованию кеширования, может быть быстрее интерфейса, разработанного стандартными средствами. Теоретически, оптимизированная не-Qt программа должна быть быстрее аналогичной Qt-программы; но на практике для оптимизации не-Qt программы потребуется больше усилий и мастерства, чем для создания оптимизированной Qt-программы.
И Qt, и Swing поддерживают технику стилей, которая позволяет программам независимо от платформы использовать один из стилей интерфейса. Это становится возможным благодаря тому, что отрисовкой элементов GUI занимаются непосредственно Qt и Swing. Вместе с Qt поставляются стили, которые эмулируют внешний вид Win32, Motif, MacOS X Aqua (в Macintosh-версии), и даже стиль, эмулирующий внешний вид Swing-программ.
3.2. Парадигмы программирования в Qt и Swing
Несмотря на то, что оценка API в определенной степени является делом личных предпочтений программиста, среди API-интерфейсов можно выделить такие, которые сделают ваш код более простым, кратким, элегантным и читаемым, чем другие. Ниже мы приводим два примера кода: первый с использованием Java/Swing, а второй с использованием C++/Qt, в которых реализуется вставка нескольких элементов в иерархическое дерево. Swing-код:
...
DefaultMutableTreeNode root = new DefaultMutableTreeNode( "Root" );
DefaultMutableTreeNode child1 = new DefaultMutableTreeNode( "Child 1" );
DefaultMutableTreeNode child2 = new DefaultMutableTreeNode( "Child 2" );
DefaultTreeModel model = new DefaultTreeModel( root );
JTree tree = new JTree( model );
model.insertNodeInto( child1, root, 0 );
model.insertNodeInto( child2, root, 1 );
...
Этот же код с использованием Qt:
...
QListView* tree = new QListView;
QListViewItem* root = new QListViewItem( tree, "Root" );
QListViewItem* child1 = new QListViewItem( root, "Child 1" );
QListViewItem* child2 = new QListViewItem( root, "Child 2" );
...
Как видите, Swing использует архитектуру Model-View-Controller (MVC), в то время как Qt ее поддерживает, но не навязывает использовать. Поэтому Qt-код более интуитивен. К такому же результату приводит сравнение кода для создания заполненной таблицы или других сложных компонентов GUI.
Вторым интересным моментом является то, как различные инструментарии связывают воздействие пользователя (например, выбор элемента в выше созданном дереве) с определенной функциональностью (вызовом функции или метода). Синтаксически в Java/Swing и C++/Qt это выглядит по-разному, но основной принцип общий. Трудно сказать, какой код является более ясным и элегантным, Swing-код:
...
tree.addTreeSelectionListener( handler );
...
или Qt-код:
...
connect( tree, SIGNAL( itemSelected( QListViewItem* ) ),
handler, SLOT( handlerMethod( QListViewItem* ) ) );
...
С одной стороны, Swing-код выглядит проще, а с другой – Qt-код более гибок. Qt позволяет программисту использовать для управляющей функции любое имя, в то время, как Swing обязывает использовать в качестве имени valueChanged() (вот почему в приведенном выше Swing-примере оно не было указано явно). Также Qt позволяет связывать событие (сигнал в терминологии Qt) с любым числом управляющих функций (слотов).
Таким образом, и Java/AWT/Swing, и C++/Qt одинаково хорошо подходят для разработки сложного пользовательского интерфейса. Главным недостатком Swing-интерфейса является низкая производительность Java.
4. Заключение
Мы сравнили две платформы: Java/AWT/Swing и C++/Qt, оценив их пригодность для эффективной разработки высокопроизводительных приложений с пользовательским графическим интерфейсом. В то время, как Java-платформа обеспечивает разработчикам сравнимую продуктивность программирования, платформа C++/Qt обеспечивает приложениям лучшую производительность и эффективность использования памяти. Также C++ выигрывает за счет более лучших средств разработки.
Что касается сравнения GUI-библиотек, Swing и Qt, то видно, что более худшая производительность Java-программ делает платформу Java/Swing менее подходящей для разработки GUI-приложений, даже при сравнимом опыте программирования. В отличие от Swing, Qt не навязывает программисту парадигму программирования Model-View-Controller, поэтому в результате Qt-программы получаются более краткими.
Независимое научное исследование и полученный практический опыт эксплуатации показывают, что предпочитаемость использования Java во многих случаях чаще всего неоправданна, а комбинация C++/Qt является более лучшей. Главными причинами этого являются более низкие производительность и эффективность использования памяти в Java (особенно при использовании инструментария Swing) при такой же обеспечиваемой продуктивности программирования. Во многих выполненных нами проектах начинающие программисты осваивали быстрее Java, более опытные и профессиональные разработчики (занимающиеся проектированием приложений и реализацией критичных участков программ) достигали быстрее лучших результатов с помощью C++.
Java/Swing может подойти для разработки некоторых программ, особенно если они без GUI-интерфейса или с ограниченной GUI-функциональностью. В целом, C++/Qt является более лучшим решением, в особенности для разработки GUI-приложений.
Источник: Сравнение Qt и Java
Qt: интерфейс, и не только…
7 декабря 2004 г
Если вы связаны с программированием, то наверняка уже встречали сочетание Qt. Многие (0,1%) из вас даже использовали Qt в своих разработках, пусть даже как следствие инсталляции Borland Kylix. В общем, самое время посмотреть на Qt, что называется, «в упор, двумя глазами»…
Этот продукт на слуху у всех разработчиков, которые программируют для Linux. И не только у них — например, самый модный и удобный многоплатформенный клиент для сетей AIM/ICQ (точнее, ICQ/AIM), sim, написанный питерцем Володей Шутовым, создан именно с использованием Qt. Не говоря уже о Borland Kylix — там вообще все работает поверх Qt.
Такие совпадения — там вилочка, тут ножичек — поневоле задумаешься…
Идея
Норвежская компания Trolltech AG (изначально называвшаяся Quasar Technologies, откуда, собственно, и пошла аббревиатура Qt) образовалась из двух (хм…) норвежцев, Эйрика Енга (Eirik Eng) и Хаварда Норта (Haavard Nort) с целью… короче, как всегда, с целью подрубить бабла. Но, кроме того, еще и создать пару нетленок из разряда универсальных библиотек, которые завоюют весь мир. Уж такой народ норвежцы — не могут без славы и денег.
Дело было в 1994 г. в городе Осло. Само сочетание «Qt» некоторые произносят как «кьют» ака cute — то есть «мило, прикольно». В общем-то, так оно и есть, если, конечно, вы способны оценить красоту, выраженную в терминах С++.
Говоря более конкретно, во главу угла были поставлены две вещи. Первое: библиотеки создавались для языка С++. Второе: основой всему был именно интерфейс пользователя, поскольку это самый несовместимый и позорный момент во всем софтвере — полностью разные парадигмы графического пользовательского интерфейса.
Как известно, MS Windows использует «локальный» GUI API, который, однако, недавно был транспонирован на сетевые подключения в XP Remoting. Все UNIX-like, и Linux в том числе, были изначально (ну, как изначально — как только рабочие станции доросли до графики, естественно) завязаны на X Windows, который, хоть и работает от рождения по сети, но локально показывает худшие характеристики быстродействия и реактивности из всех рассматриваемых интерфейсов. Наконец, графика Mac OS, зашитая полуаппаратно в фирменных BIOS-»тулбоксах», всегда показывала скорость и красоту, даже когда Windows еще под стол пешком ходила. Но это было: а) проприетарно, б) ни с чем не совместимо — даже приблизительно.
Короче — бардак и брожение умов. Вот прибалты и задумались…
Точнее задумались-то не только они. В качестве переносимого графического интерфейса можно использовать хоть Java, хоть Tk/Tcl, а хоть и Macromedia Flash — на последних версиях этого Flash можно писать вполне приличные приложения. Это только те «задумавшиеся», у кого получилось,— об остальных мы просто не знаем.
Да, так вот, у Trolltech тоже неплохо получилось, как именно — сейчас посмотрим.
Кю-тэшное семейство, обзор
На самом деле существует несколько продуктов Qt, хотя все они «завязаны» на одном и том же коде. Собственно Qt — это межплатформенная библиотека для С++. Более конкретно — библиотека классов, «цешники»-натуралы отдыхают. То есть это даже и библиотекой-то не назовешь — сами создатели именуют это как framework, то есть программная оболочка. Но, конечно, от этого она не перестает быть обычной библиотекой.

Применение нового «стиля» в Qt может мгновенно придать вашему приложению новый вид
Помимо переносимого интерфейса библиотека также занимается интернационализацией, то есть располагает инструментом для перевода интерфейса на ваш родной китайский язык (сделано это, кстати, очень прикольно). Менеджер релизов выкусывает из кода все строки, подлежащие переводу. Они должны быть, правда, особого формата, чтоб не было накладок с непереводимыми фразами. Короче, полученные таблицы фраз поступают в средство перевода — Linguist. Сам он ничего не переводит (это делаете вы) — но зато помогает сохранять уже переведенные фразы в словарях. Так что вы можете менять программу как вам угодно, и то, что уже переведено, уже никогда не будет требовать перевода.
Третье, что было сделано (кстати, уже по ходу разработки — по просьбе многих клиентов), это системно-независимые механизмы доступа к базам данных. Конечно, я уже не говорю о таких вещах, как унифицированный сетевой доступ, XML, трехмерная графика и так далее — просто различия в этих сферах не так разительны, как в перечисленных выше.
Выходит, что Qt — это как бы «мегапатч» к стареньким си-либам, позволяющий:
- работать в графике;
- переводить программы;
- выполнять запросы к БД;
- унифицированно реализовывать все остальное, вроде мультимедиа.
Помимо основной библиотеки, существует основанный на том же коде framework для всякой мобильной живности вроде смарт-фонов и PDA под управлением Embedded Linux (особенно засветился в этом секторе Sharp). Называется эта штуковина Qtopia. Инструмент для разработки встроенных систем с ограниченной функциональностью называется Qt/Embedded.
Далее, QSA — скриптовый язык, позволяющий управлять готовыми приложениями на Qt/C и расширять их возможности в скриптовом контексте.
Teambuilder — средство компиляции, которое собирает статистику утилизации CPU по целой рабочей группе и позволяет оптимизировать процесс сборки на менее загруженных станциях.
Ко всему, существует несколько «плугов» для известных сред разработки (говоря проще — для Visual Studio и Borland Builder), упрощающих разработку под Qt,— Qt Solutions.
В качестве визуального инструмента разработки используется Qt Designer — это «малевалка» интерфейса, подобная всем «клик-энд-го». Конечно, еще полно полезного хелпняка: просто тысячи артиклов, плюс девяносто шесть (проверьте) примеров — и это есть гуд.
Однако сосредоточимся на Qt — все остальное вторично.
Инсталл
Конечно же, сначала следует проинсталлировать систему. Под Windows нужно обратить внимание на три вещи. Во-первых, потребуется ввести имя пользователя, компанию и сериал. Вводите их точно в таком же виде, как указано в письме, которое вам придет (сериал является хеш-фанкшином от даты активации и двух остальных полей) — иначе ничего работать не будет.
Во-вторых, при инсталляции предпоследним пунктом идет установка примеров. Они приходят в виде исходных текстов, но в процессе установки будут компилироваться. Компиляция происходит в командной строке, так что без установленных переменных окружения это не сработает, получите ошибку «где линкер дел, гад?». Пофиксить проблему просто: создайте и запустите bat-файл типа:
call "C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat" qt-win-eval-msvc-3.3.3.exe
Третья заморочка: примеры и туторы тоже так просто работать не станут. Они не могут найти свою библиотеку, хотя в переменной пути к ней все вроде и указано. На самом деле для правильной работы нужно установить переменные окружения — а они, в свою очередь, устанавливаются при загрузке профиля пользователя. Даже и перезагружаться не нужно, просто отлогоньтесь-прилогоньтесь. Кстати, о необходимости такой операции пишется в конце инсталляции — но кто же такое читает?
Если вы инсталлируетесь под Linux и BSD, то я вам ничего не стану рассказывать — сами с ушами. Скажу только, что уровень проблем при инсталляции под этой ОС может колебаться в очень больших пределах, в зависимости от поддержки вашего дистрибутива и его «комплектации» на момент установки. Я ставил на Slakware, RH и Mandrake без особых проблем. В любом случае Qt нужно ставить на KDE.
Библиотека, learning curve
Как я уже говорил, Qt — это библиотека классов. Классов там более четырехсот, так что, если начальник требует, чтобы вы начали кодить на Qt с завтрашнего дня, то лучше послать его к ежам. Придется, как минимум, пару недель изучать доки, а лучше прочитать книжку. Я за целую ночь не смог воткнуть даже простецкие вещи. Вам тоже торопиться не советую, лучше делать все правильно — от простого к сложному.
Может быть, не первая по важности, но несомненно нужная после инсталляции вещь — просмотр, распечатка, (а возможно, и татуировка на груди — вверх ногами, чтобы читать было удобно) иерархии классов Qt.
Это, в общем-то, относится к любой библиотеке. Но в некоторых «системах ценностей» вы можете прожить три жизни, и проходить при этом «в лаптях», то есть использовать 3% от всех возможностей, обходя стороной то, что, собственно, и составляет ценность продукта. Пример: множество малопонятных функций в MS Exсel, масса никому не нужных ActiveX-компонент (помните, как они продавались почти по одной штучке — во цирк!) и т.д. В Qt все несколько иначе — вы в конце концов будете использовать 50-80% всех классов, так что «отбыть номер» не удастся.
На рисунке приведен сильно уменьшенный вариант этой самой схемы — но вы можете распечатать и большой вариант. Он расположен по адресу ftp://ftp.trolltech.com/qt/pdf/qt33-class-chart.pdf и, кроме того, лежит прямо здесь.
Непременно прочитайте whitepapers — в отличие от других «белок», это не просто набор рекламных лозунгов. После чтения приведенных там примеров вы даже сможете построить несколько приложений.
Как маршрут для вечерних прогулок вы должны избрать посещение сайта http://doc.trolltech.com/3.3/ — вас там многое заинтересует, начиная, конечно, с How To Learn Qt. Собственно, всегда лучше начинать с голого кодирования, не пользуясь автогенераторами — или пользуясь, но при этом тщательно разбирая код. В противном случае вы напишете что-то, что потом не сможете ни объяснить, ни поддержать, ни модифицировать.
Как оно работает
Говоря в общем, все вращается вокруг событий, называемых сигналами, и слотов — то есть регистрируемых реакций на эти события. В отличие от других «закрытых» событийных множеств (вроде тех, что встречаются в Active Script) система сигналов Qt полностью динамическая — каждый участок кода может регистрировать или обрабатывать сигналы. Это напоминает механизм, условно известный как WM_USER+1.
Сигналы имеют отдаленное отношение к системным событиям и являются их произвольной трансляцией — так же как это реализовано в Delphi. Естественно, что ни одна система не должна порождать таких шквалов нотификаций, как Windows. Обеспечивать приложению доступ к событиям неклиентской области — по-моему, большая ошибка архитекторов этой системы, породившая немало «смешных» интерфейсов.
Что касается графического интерфейса, то он более всего похож на Java Swing — тут тоже существуют схемы стилей, например Windows, CDE, Motif, копирующие известные оболочки. Присутствуют также layout’ы, автоматически размещающие элементы управления; кроме того, есть такие «пружинки», или «спейсеры», которые расталкивают сопредельные компоненты.
Есть также немало вещей, напоминающих Delphi: хинты размеров (то есть «как было бы лучше»), масштабирование компонент и прочие вещи, претендующие на красивость.
Как и в Swing, все элементы перерисованы от руки, то есть стандартные механизмы рисования элементов управления не применяются — вместо этого используется, например, GDI WinAPI. Автоматически определяется версия ОС и, соответственно, реализуются или игнорируются те или иные свойства, вроде прозрачности или XP-прибамбасов. В качестве небольшого попуска Qt использует стандартные диалоговые окна Windows, в частности диалоги открытия файла и настойки печати.
Под X11 Qt не использует тулкиты вроде Motif или Xt — ну, Qt вроде и сам такой же тулкит (то есть — зачем же?). Вместо этого напрямую используется Xlib — с расширениями вроде RENDER, если они доступны.
Схема «генеральной помпы», то есть основного цикла событий, не очень отличается от всех подобных во всех ОС — от MS Windows до PalmOS. Приложение строится просто, если не сказать примитивно: создается главное окно, устанавливаются его параметры, после чего приложение непосредственно «ранится». Вот как выглядит известный всем «Привет, мир!»:
#include < qapplication.h >
#include < qlabel.h >
int main( int argc, char **argv )
{
QApplication app( argc, argv );
QLabel *hello = new QLabel( "Привет, мир!", 0 );
app.setMainWidget( hello );
hello->show();
return app.exec();
}
Естественно, это вам не Visual Basic: вы можете создавать собственные элементы управления — это, как и в Delphi, поощряется, а не наоборот. Писать их не сложнее, чем дельфийские, а то и проще. Вот, к примеру, как реализуются LCD-часы на основе класса, отрисовывающего LCD-строку:
#include < qdatetime.h >
#include "clock.h"
Clock::Clock( QWidget *parent, const char *name )
: QLCDNumber( parent, name ), showingColon( true ) {
showTime();
startTimer( 1000 );
}
void Clock::timerEvent( QTimerEvent * ) {
showTime();
}
void Clock::showTime() {
QString time = QTime::currentTime().toString().left( 5 );
if ( !showingColon )
time[2] = ’ ’;
display( time );
showingColon = !showingColon;
}
Не сложно — а если б выключить мигание двух точек, так и вовсе тривиально. Кстати, мне показалось что знак «!» перед showingColon можно «сэкономить» — ну, конечно, заменив для мнемоничности showcolon на hidecolon. Да и showTime() в конструкторе можно бы убрать…
Короче, в полном ходу лозунг Trolltech: code_less && create_more — только не стоит вычислять эту выражение по короткой схеме.
Интеграция
Это, конечно, не бог весть что, но, тем не менее, приятно и весьма полезно — Qt во время инсталляции интегрируется в Visual Studio, так что вы сможете добавлять потом новые проекты, формы и т.д. прямо из дополнительной панельки. Кроме того, там доступно быстрое переключение на подсказку в QtAssistant — раз уж вся среда не зависит от платформы, было бы просто позором сделать справку по ее системе. В результате html’ные «хелпы» от Microsoft тут нет — вместо них используются похожие, но собственного формата и, естественно, с оболочкой на самом Qt.

Qt также хорошо интегрируется в Visual Studio, хотя это и не совсем то, для чего он создавался
Аллилуя, то есть Славься!
Я не преследовал цели посвятить вас в самые сложные вещи Qt — точнее, таких вещей я там и не заметил. Всё ровно, логично, слаженно, сбито и задокументированно. Конечно, были и будут баги, возникают новые возможности — но для этого есть огромное сообщество разработчиков, да и конфы в Сети просто ужасно большое количество. В общем, с проблемами не «подвиснете». Кстати, и исходный код можно посмотреть, если уж что-то совсем непонятно, а при желании можно даже частично перекомпилировать. Этого я, правда, делать не советую — расстанетесь с совместимостью. Лучше станьте контрибьютором — а там, чего доброго, дорастете до должности «платного друга Trolltech», я бы гордился.
В заключение хочу сказать, что в процессе «кьютизаци» вы можете круто сэкономить на интернете — на КП-диске лежат отличные фрагменты видео, поясняющие концепции Qt, что называется, устами создателей. Там же — whitepapers. Там же — триальные инсталляшки Qt и QSA для Windows и X11. Для регистрации триала зайдите на сайт www.trolltech.com/download/index.html?cid=20 и зарегистрируйтесь без закачки, ключи придут по почте. Бесплатные версии для Линукс лучше всего закачать с сайта дистрибутива в виде пакета, чтобы не морочиться с компиляций — в последние дистро все и так наверняка включено, просмотрите список доступных пакетов, если что.
Короче: надумаете начинать новый портабельный «высокооктановый» проект… ну вы уже поняли, что делать. После «пингвинизации» Китая компанией Sun (контракт на поставку 200 миллионов пингвинов…) вам будет просто скучно писать для MS Windows. Хотя, с другой стороны, Билла Гейтса тоже еще никто не отменял, так что не без этого — лучше сохранять совместимость.
Кстати, кто первый создаст аутсорсинг «QTWorks.ua» — тот может реально подрубить не только у нас на районе, но и в окружающих поселениях, типа России, Германии и Кореи, поскольку в ближайшее время значение Linux-приложений во «взрослых» странах будет только расти.
В статье использованы материалы и примеры с сайта trolltech.com.
Источник: Qt: интерфейс, и не только…
Свежие комментарии