пятница, 30 сентября 2011 г.

Вставка QWidget в QTableWidget

Надо бы рассказать о способе вставки QWidget в QTableWidget.
Вообще, я не очень люблю QTableWidget, т.к. больше привык к QTableView и QAbstractItemModel, QStyledItemDelegate. Но в случае если быстро нужна таблица, связанная с одной готовой моделью, то можно воспользоваться QTableWidget. В принципе, QTableWidget порожден от QTableView и умеет всё то, что умеет и QTableView.

Перейдем к делу. Точнее к постановке задачи.
Надо вставить в ячейки QTableWidget виджеты. Пример на картинке.


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

Как это сделать?
В коде я приведу работу с QTreeWidget, а не с QTableWidget, но разницы особой нет.

Создаем QTreeWidget:
treeWidget_ = new QTreeWidget(this);
treeWidget_->setRootIsDecorated(false);
treeWidget_->setSelectionBehavior(QAbstractItemView::SelectRows);
treeWidget_->setAlternatingRowColors(false);
treeWidget_->header()->setDefaultAlignment(Qt::AlignCenter);
treeWidget_->setEditTriggers(QAbstractItemView::NoEditTriggers);
treeWidget_->setFocusPolicy(Qt::NoFocus);
treeWidget_->header()->setStretchLastSection(false);
Метод для добавления строки виджетов:
void SomeClass::appendRow(const QWidgetList & widgets)
{
  //Создаем Item
  QTreeWidgetItem * treeItem = new QTreeWidgetItem(treeWidget_);
  int col = 0;
  //Добавляем виджеты в строку
  foreach (QWidget * widget, widgets)
  {
     //Создаем новый виджет, на котором разместим исходный виджет
     //Разместим исходный виджет в Layout нового
     QWidget * newWidget = new QWidget(treeWidget_);
     QVBoxLayout * vb = new QVBoxLayout;
     vb->setMargin(3);
     vb->addWidget(widget);
     newWidget->setLayout(vb);
     //Добавляем виджет и увеличиваем счетчик столбцов
     treeWidget_->setItemWidget(treeItem, col++, newWidget);
  }

  treeWidget_->scrollTo(treeWidget_->model()->index(
               treeWidget_->model()->rowCount() - 1, 0));

  treeWidget_->setCurrentItem(treeItem);
}

 

пятница, 17 июня 2011 г.

Добавление своего типа в QVariant

Хороший тип QVariant. Это может быть int, double, QDate, QMap, и еще полсотни типов.
Добавим к этим типам собственный класс или структуру, чтобы с ней можно было бы общаться как с другими типами - помещать и извлекать из QVariant.

class SomeClass
{
//всё что нужно
};
Q_DECLARE_METATYPE(SomeClass)


Итак, достаточно после определения класса вызвать макрос Q_DECLARE_METATYPE()
Что это дает?

Помещаем экземпляр класса в QVariant.

SomeClass scl;
QVariant vclass = QVariant::fromValue(scl);


Всё, теперь экземпляр класса SomeClass, хранится как QVariant и с ним можно делать всё, что и с объектами типа QVariant.

Обратное преобразование.

scl = qvariant_cast(vclass);

Вообще говоря, в классе SomeClass крайне желательно определить оператор присваивания и конструктор копирования, на случай если вдруг возникнет желание помещать его в контейнер типа QList. Если это обычная структура, то ничего страшного, конечно, не произойдет, элементы структуры скопируются компилятором.

Теперь об одной особенности макроса Q_DECLARE_METATYPE(). Он не приемлет запятых в определении.
Написать что-то типа
Q_DECLARE_METATYPE(QVector<QPair<QString, QString> >)
не получится, из-за запятой в определении шаблона.
Лечится это хитрым патчем.

#define Q_DECLARE_METATYPE_COMMA(...) \
QT_BEGIN_NAMESPACE \
template <> \
struct QMetaTypeId< __VA_ARGS__ > \
{ \
    enum { Defined = 1 }; \
  static int qt_metatype_id() \
  { \
     static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
     if (!metatype_id) \
        metatype_id = qRegisterMetaType< __VA_ARGS__ >( #__VA_ARGS__, \
          reinterpret_cast< __VA_ARGS__ *>(quintptr(0))); \
     return metatype_id; \
  } \
}; \
QT_END_NAMESPACE \

Я даже не буду рассказывать, что здесь делается т.к. сам не понимаю и хитрый патч вытащен откуда-то из просторов интернета.
Главное, что с его помощью мы можем написать
Q_DECLARE_METATYPE_COMMA(QVector<QPair<QString, QString> >)

* This source code was highlighted with Source Code Highlighter.
 И тогда мы сможем назначить typedef
typedef QVector<QPair<QString, QString> > VectorPair;
Q_DECLARE_METATYPE_COMMA(VectorPair)


* This source code was highlighted with Source Code Highlighter.

и использовать тип VectorPair как QVariant.

среда, 15 июня 2011 г.

Обновилась PoDoFo

Обновилась библиотека podofo для работы с PDF до версии 0.9.1.
Последнюю версию я использовал 0.8.2.
Проблем со сборкой не было, но чтобы узнать список изменений пришлось сливать исходники из SVN и читать SVN ChangeLog, ибо в архиве никаких Changes'ов не было.
Итак, what news.
В основном добавились патчи, исправляющие баги, либо ускоряющие работу библиотеки с большими pdf-файлами.
Изменен принцип сбоки библиотек, вместо двух сделали одну. Но это так, всё по мелочи.

Главное, что библиотека хорошо работает как и прежде.

вторник, 14 июня 2011 г.

QProgressIndicator

На qtapps.org есть много интересных виджетов, написанных обычными пользователями. Распространяется свободно под LGPL.
Например, QProgressIndicator - интересная крутилка прогресса в стиле FireFox.
Пример использования:
progress_ = new QProgressIndicator(this);
progress_->setAnimationDelay(50);

progress_->startAnimation();

* This source code was highlighted with Source Code Highlighter.


В итоге колесико будет крутиться пока ему не скажут stopAnimation()

четверг, 19 мая 2011 г.

Поломать комп

Поломать компьютер? Легко: грохни MBR. Вряд ли дитё знает, что это лечится одной консольной командой. Чтобы было совсем страшно, объясни, что восстановление займёт пару недель, так как переустановка системы снесёт все данные. Можно ещё в биосе температуру критического режима на минимум выставить, тогда машина регулярно в ребут будет уходить. Битую планку памяти воткнуть, видюху не до конца в разъём вставить, питание с проца убрать, винт с мастера на слейв джапмером переключить, — мало ли вариантов? Экстремальный метод — внутренние USB-разъёмы закольцевать.

Совсем злодейская штука — с разъёма для лампочки HDD кинуть мост через микрореле или транзистор на разъём Reset, тогда машина будет в постоянном ребуте. Я сам такое делал для родителей одного школяра. Главное в этом деле — всё спрятать под пластиковую «морду» корпуса. Пару дней выпускник сам пытался найти неисправность, потом родители, изобразив массовый поиск спеца, вызвали меня, и я забрал ящик в ремонт. Экзамены школяр сдал, ящик отремонтированный получил назад.

А проще всего пойти к провайдеру и договориться о «неполадках» на линии на месяц-другой. К чему мудрить?

вторник, 19 октября 2010 г.

Интеграция Silverlight в Qt приложения

Хотелось бы опубликовать статью на русском языке об интеграции роликов Silverlight в Qt приложения.
Для этого используется библиотека Qtitan, она коммерческая, увы, но может быть кого-то заинтересует. К тому же ее вполне можно использовать в демо-режиме в некоммерческих целях.
Статья публикуется с негласного соглашения авторов фреймворка Qtitan Multimedia.

Интеграция Silverlight в Qt приложения


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

Мы тоже любим Qt, и мы захотели расширить наш любимый фреймворк возможностью работы с современной векторной графикой, например, мы захотели интегрировать в приложение ролики Adobe Flash или Microsoft Silverlight.

среда, 13 октября 2010 г.

QScintilla: определяем styleText

Вот мы и добрались до самой важной функции лексера void styleText(int start, int end)

Займемся ее реализацией.

void LexerZ::styleText(int start, int end)
{
  //editor() - функция из родительского класса.
  //Возвращает указатель на QsciScintilla
  if (!editor())
   return;

  //получить исходник
  //выделим кусок памяти
  // +1 - под \0
  char * chars = new char[end - start + 1];
  //заполним кусок обратившись сцинтилле
  editor()->SendScintilla(QsciScintilla::SCI_GETTEXTRANGE,
              start, end, chars);
  //преобразуем в строку
  QString source(chars);
  delete [] chars;
 
  //Оптимизация - кэшируем уже ранее отрисованную строку
  if (source == cache)
   return;
  else
   cache = source;
  //Не работаем с пустой строкой
  if (source.isEmpty())
   return;
  //вызываем функцию отрисовки комментария
  //передаем исходную строку, начало и конец
  commentStyling(source, start, end);
}

* This source code was highlighted with Source Code Highlighter.


В комментариях описано, что мы делаем в этой функции. Здесь мы получаем строку, которую требуется отрисовать (это может быть не только текущая строка, а в том числе и соседние строки, сцинтилла в этом плане довольно умна).
Дальше вызываем функцию commentStyling(source, start, end);

Ее реализация приведена ниже:

понедельник, 11 октября 2010 г.

QScintilla: раскрашиваем комментарии

Сегодня мы будем раскрашивать комментарии для некого гипотетического языка Z.
Допустим в этом языке комментарии строк принято обозначать символом **, аналогично // в C++. Давайте предположим, что цвет шрифта комментария - синий, цвет фона - светло-зеленый, а сам шрифт - Comic Sans.
Начнем изменения в нашем коде Lexer.cpp и Lexer.h. (Можно переименовать эти файлы в LexerZ.h и LexerZ.cpp).

пятница, 8 октября 2010 г.

QScintilla: пишем Lexer

Перед дальнейшим написанием лексера нужно уяснить парадигму паттерна "Виртуальный метод", который использует QScintilla.
Лескер состоит из набора виртуальный функций, которые нужно переопределить. Класс QsciScintilla при необходимости вызывает виртуальные методы базового класса, однако в соответствии с полиморфизмом, будут вызываться методы нашего лексера.
Вот перечень виртуальных функций, необходимых для переопределения:
  //! Возвращает название языка
  virtual const char * language() const;
  //! Цвета для стилей
  virtual QColor defaultColor(int style) const;
  //! Шрифты для стилей
  virtual QFont defaultFont(int style) const;
  //! Бакгроунд для стилей
  virtual QColor defaultPaper(int style) const;
  //! Разбор текста на стили
  virtual void styleText (int start, int end);
  //! Описание
  virtual QString description (int style) const;

* This source code was highlighted with Source Code Highlighter.
Переменная style - это просто идентификатор стиля. Мы сами ее определяем. Для этого в лексере мы определяем некий enum для наших стилей.
  enum
  {
   Default = 0,
   Comment = 1,
   Binary  = 2,
   String  = 3,
   Keyword1 = 4,
   Keyword2 = 5,
   Keyword3 = 6,
   Keyword4 = 7,
   Const  = 8
  };

* This source code was highlighted with Source Code Highlighter.
Имена стилей и их числовые обозначения значения не имеют, далее станет ясно почему.
Самой важной виртуальной функцией является   virtual void styleText (int start, int end);

Именно она отвечает за отрисовку. К этой функции QScintilla обращается всякий раз, когда ей надо перерисовать кусок текста начиная с позиции start до позиции end.

четверг, 7 октября 2010 г.

QScintilla: создаем свой лексер

Как уже было сказано ранее, QScintilla поддерживает около 80 языков, раскрашивая каждый в соответствии с их синтаксисом. Т.е. для C++ комментарии задаются символом //, а для языка R символом #. Соответственно, раскрашивать в каждом случае надо по своему.

Трудно представить, что кроме этих 80 языков вам может понадобиться что-то свое, но тем не менее такая ситуация возникнуть может и надо знать как тут поступать.

Вообще, есть два способа добавить свой язык для раскраски.
Первый - это правка собственно исходного кода библиотеки qscintilla2.dll.
Достоинства этого метода - простота добавления. Достаточно взять за пример уже какой-нибудь существующий лексер, скопировать его в новый файл, исправить несколько строк, пересобрать библиотеку и, вуаля, новый лексер готов.
Недостаток этого метода очевиден - необходимость пересобирать библиотеку. Хотелось бы обойтись без этого.
Для этого в QScintilla есть путь истинного воина - наследование от QsciLexerCustom и самостоятельное написание алгоритмов раскраски. Это более сложный путь, на мой взгляд, но зато более правильный с концептуальной точки зрения, т.к. не нужно пересобирать библиотеку.

Итак, первое что мы должны сделать - это отнаследоваться от QsciLexerCustom:

#include <qsci/qscilexercustom.h>

class Lexer : public QsciLexerCustom
{
  Q_OBJECT
public:
  //! Конструктор
  Lexer(QObject * parent = 0);
  //! Деструктор
  virtual ~Lexer();
private:
  Lexer(const Lexer &);
  Lexer &operator=(const Lexer &);
};

* This source code was highlighted with Source Code Highlighter.
В следующих выпусках мы продолжим написание своего лексера.