Пример "Character Map"
Файлы:
Пример "Character Map" показывает, как создать пользовательский виджет, который может выводить на экран и собственный вывод, и реакцию на пользовательский ввод. Пример выводит на экран массив символов, которые пользователь может щелкнуть для ввода в однострочный редактор. Содержимое однострочного редактора можно затем скопировать в буфер обмена и вставить в другие приложения. Назначение такого вида инструмента заключается, в том чтобы позволить пользователю ввести символы, которые могут быть недоступны или затруднена локализация на клавиатуре. Пример состоит из следующих классов:
Определение класса CharacterWidgetКласс CharacterWidget используется для вывода на экран массива символов в с заданным пользователем шрифтом и стилем. Для большей гибкости, мы создаём подкласс QWidget и переопределим только те функции, которые нужны чтобы предоставить возможности базовой отрисовки и взаимодействия. Определение класса выглядит примерно так: class CharacterWidget : public QWidget { Q_OBJECT public: CharacterWidget(QWidget *parent = 0); QSize sizeHint() const; public slots: void updateFont(const QFont &font); void updateSize(const QString &fontSize); void updateStyle(const QString &fontStyle); void updateFontMerging(bool enable); signals: void characterSelected(const QString &character); protected: void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); private: QFont displayFont; int columns; int lastKey; int squareSize; }; Виджет не содержит каких-либо других виджетов, поэтому он должен предоставить свои собственные подсказки размера чтобы позволить своему содержимому отображаться корректно. Мы переопределили QWidget::paintEvent() для отрисовки пользовательского содержимого. Также переопределили QWidget::mousePressEvent(), чтобы позволить пользователю взаимодействовать с виджетом. Слоты updateFont() и updateStyle() используются для обновления шрифта и стиля символов в виджете всякий раз, когда пользователь меняет настройки приложения. Класс определяет сигнал characterSelected() для того, чтобы сообщать другим частям приложения всякий раз, когда пользователь выберет символ в виджете. В качестве вежливости, виджет предоставляет всплывающую подсказку, которая показывает значение текущего символа. Мы переопределили обработчик событий QWidget::mouseMoveEvent() и объявили showToolTip(), чтобы разрешить эту возможность. Закрытые члены данных columns, displayFont и currentKey используются для записи количества отображаемых столбцов, текущего шрифта и текущего выделенного символа в виджете. Реализация класса CharacterWidgetПоскольку виджет используется как простой холст, то конструктор вызывает только конструктор базового класса и определяет некоторые значения по умолчанию закрытых членов данных. CharacterWidget::CharacterWidget(QWidget *parent) : QWidget(parent) { squareSize = 24; columns = 16; lastKey = -1; setMouseTracking(true); } Инициализируем currentKey значением -1 чтобы показать, что первоначально никакой символ не выбран. Разрешаем отслеживание мыши, чтобы позволить нам двигаться по виджету вслед за курсором. Класс предоставляет две функции, позволяющие настроить шрифт и стиль шрифта. Обе они модифицируют экранный шрифт виджета и вызывают update(): void CharacterWidget::updateFont(const QFont &font) { displayFont.setFamily(font.family()); squareSize = qMax(24, QFontMetrics(displayFont).xHeight() * 3); adjustSize(); update(); } void CharacterWidget::updateSize(const QString &fontSize) { displayFont.setPointSize(fontSize.toInt()); squareSize = qMax(24, QFontMetrics(displayFont).xHeight() * 3); adjustSize(); update(); } Для вывода на экран используем шрифт фиксированного размера. Аналогичным образом, подсказка фиксированного размера предоставляется функцией sizeHint(): QSize CharacterWidget::sizeHint() const { return QSize(columns*squareSize, (65536/columns)*squareSize); } Три функции стандартных событий реализованы так, чтобы виджет мог реагировать на щелчки, предоставлять всплывающие подсказки и отрисовывать доступные символы. Функция paintEvent() показывает, как содержимое виджета размещается и выводится на экран: void CharacterWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.fillRect(event->rect(), QBrush(Qt::white)); painter.setFont(displayFont); QPainter создаётся для виджета и в любом случае мы гарантируем, что фон виджета отрисовывается. Шрифт рисовальщика устанавливается таким же, что и определённый пользователем экранный шрифт. Область виджета, которую нужно перерисовать, используется для определения, какие символы нужно вывести на экран: QRect redrawRect = event->rect(); int beginRow = redrawRect.top()/squareSize; int endRow = redrawRect.bottom()/squareSize; int beginColumn = redrawRect.left()/squareSize; int endColumn = redrawRect.right()/squareSize; Используя целочисленное деление, мы получаем номера строк и столбцов каждого символа, который будет выводиться на экран, и рисуем квадрат на виджете для всех выводимых символов. painter.setPen(QPen(Qt::gray)); for (int row = beginRow; row <= endRow; ++row) { for (int column = beginColumn; column <= endColumn; ++column) { painter.drawRect(column*squareSize, row*squareSize, squareSize, squareSize); } } Значки каждого символа массива рисуются внутри квадрата, значок наиболее часто выбираемого символа выводится красным цветом: QFontMetrics fontMetrics(displayFont); painter.setPen(QPen(Qt::black)); for (int row = beginRow; row <= endRow; ++row) { for (int column = beginColumn; column <= endColumn; ++column) { int key = row*columns + column; painter.setClipRect(column*squareSize, row*squareSize, squareSize, squareSize); if (key == lastKey) painter.fillRect(column*squareSize + 1, row*squareSize + 1, squareSize, squareSize, QBrush(Qt::red)); painter.drawText(column*squareSize + (squareSize / 2) - fontMetrics.width(QChar(key))/2, row*squareSize + 4 + fontMetrics.ascent(), QString(QChar(key))); } } } Нам не нужно учитывать разницу между выводимой на экран областью в окно просмотра и областью, рисуемой поскольку всё за пределами видимой области будет отсечено. mousePressEvent() определяет, как виджет реагирует на щелчки кнопок мыши. void CharacterWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { lastKey = (event->y()/squareSize)*columns + event->x()/squareSize; if (QChar(lastKey).category() != QChar::NoCategory) emit characterSelected(QString(QChar(lastKey))); update(); } else QWidget::mousePressEvent(event); } Нам интересно только когда пользователь щёлкнет левой кнопкой по виджету. Когда это происходит, мы вычисляем, какой символ был выбран и отправляем сигнал characterSelected(). Номер символа находим делением x- и y-координат щелчка на размер квадратной ячейки сетки символа. Поскольку количество столбцов в виджете определяется переменной columns, мы просто умножаем индекс строки на её значение и прибавляем номер столбца чтобы получить номер символа. Если были нажаты другие кнопки мыши, то событие передаётся в базовый класс QWidget. Этим гарантируется, что событие будет правильно обработано другими заинтересованными виджетами. mouseMoveEvent() отображает позицию курсора мыши в глобальных координатах на координаты виджета, а также определяет символ, по которому щелкнули выполняя вычисление. void CharacterWidget::mouseMoveEvent(QMouseEvent *event) { QPoint widgetPosition = mapFromGlobal(event->globalPos()); uint key = (widgetPosition.y()/squareSize)*columns + widgetPosition.x()/squareSize; QString text = QString::fromLatin1("<p>Character: <span style=\"font-size: 24pt; font-family: %1\">").arg(displayFont.family()) + QChar(key) + QString::fromLatin1("</span><p>Value: 0x") + QString::number(key, 16); QToolTip::showText(event->globalPos(), text, this); } Всплывающая подсказка предоставляет поцицию, определённую в глобальных координатах. Определение класса MainWindowКласс MainWindow предоставляет минимальный интерфейс для примера, только с конструктором, слотами, которые реагируют на сигналы отправленные стандартными виджетами, и несколькими вспомогательными функциями, которые используются для настройки пользовательского интерфейса. Определение класса выглядит примерно так: class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); public slots: void findStyles(const QFont &font); void findSizes(const QFont &font); void insertCharacter(const QString &character); void updateClipboard(); private: CharacterWidget *characterWidget; QClipboard *clipboard; QComboBox *styleCombo; QComboBox *sizeCombo; QFontComboBox *fontCombo; QLineEdit *lineEdit; QScrollArea *scrollArea; QCheckBox *fontMerging; }; Главное окно содержит различные виджеты, которые используются для управления тем, как символы будут выводиться на экран, а также определяет функцию findFonts() для ясности и удобства. Слот findStyles() используется виджетами для определения доступных стилей, insertCharacter() - вставляет выбранный пользователем символ в однострочный редактор окна, а updateClipboard() - синхронизирует буфер обмена с содержимым однострочного редактора. Реализация класса MainWindowВ конструкторе мы устанавливаем центральный виджет и заполняем его несколькими стандартными виджетами (два выпадающих списка, однострочный редактор и кнопка). Также мы создаём пользовательский виджет CharacterWidget, и добавляем QScrollArea для того, чтобы можно просматривать его содержимое: MainWindow::MainWindow() { QWidget *centralWidget = new QWidget; QLabel *fontLabel = new QLabel(tr("Font:")); fontCombo = new QFontComboBox; QLabel *sizeLabel = new QLabel(tr("Size:")); sizeCombo = new QComboBox; QLabel *styleLabel = new QLabel(tr("Style:")); styleCombo = new QComboBox; QLabel *fontMergingLabel = new QLabel(tr("Automatic Font Merging:")); fontMerging = new QCheckBox; fontMerging->setChecked(true); scrollArea = new QScrollArea; characterWidget = new CharacterWidget; scrollArea->setWidget(characterWidget); QScrollArea предоставляет область просмотра на CharacterWidget, если мы установим его виджет и обработаем большую часть работы, необходимой для предоставления прокручиваемой области просмотра. Выпадающий список выбора шрифта автоматически заполняется списком доступных шрифтов. Перечисляем доступные для текущего шрифта стили в выпадающем списке используя следующую функцию: findStyles(fontCombo->currentFont()); Однострочный редактор и кнопка используются для отправки текста в буфер обмена: lineEdit = new QLineEdit; QPushButton *clipboardButton = new QPushButton(tr("&To clipboard")); Также мы получаем объект буфера обмена, чтобы можно было отправить введённый пользователем текст в другие приложения. Большая часть отправляемых в примере сигналов приходят от стандартных виджетов. Мы соединяем эти сигналы со слотами в данном классе и со слотами, предоставляемым другими виджетами. connect(fontCombo, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(findStyles(const QFont &))); connect(fontCombo, SIGNAL(currentFontChanged(const QFont &)), this, SLOT(findSizes(const QFont &))); connect(fontCombo, SIGNAL(currentFontChanged(const QFont &)), characterWidget, SLOT(updateFont(const QFont &))); connect(sizeCombo, SIGNAL(currentIndexChanged(const QString &)), characterWidget, SLOT(updateSize(const QString &))); connect(styleCombo, SIGNAL(currentIndexChanged(const QString &)), characterWidget, SLOT(updateStyle(const QString &))); Сигнал выпадающего списка выбора шрифта currentFontChanged() соединяется с функцией findStyles(), чтобы можно было показать список доступных стилей для всех используемых шрифтов. Поскольку и шрифт, и стиль может изменить пользователь, сигнал выпадающего списка выбора шрифта currentFontChanged() и выпадающего списка стиля currentIndexChanged() соединяются непосредственно с виджетом символа. Последние два соединения позволяют выбирать символы в виджете символов, а также вставлять текст в буфер обмена: connect(characterWidget, SIGNAL(characterSelected(const QString &)), this, SLOT(insertCharacter(const QString &))); connect(clipboardButton, SIGNAL(clicked()), this, SLOT(updateClipboard())); Виджет символов отправляет пользовательский сигнал characterSelected() когда пользователь щёлкает по символу, а обрабатывается функцией insertCharacter() данного класса. Буфер обмена изменяется когда кнопка отправляет сигнал clicked(), а обрабатываем этот сигнал с помощью функции updateClipboard(). Оставшийся код конструктора настраивает компоновку центрального виджета и предоставляет заголовок окна: QHBoxLayout *controlsLayout = new QHBoxLayout; controlsLayout->addWidget(fontLabel); controlsLayout->addWidget(fontCombo, 1); controlsLayout->addWidget(sizeLabel); controlsLayout->addWidget(sizeCombo, 1); controlsLayout->addWidget(styleLabel); controlsLayout->addWidget(styleCombo, 1); controlsLayout->addWidget(fontMergingLabel); controlsLayout->addWidget(fontMerging, 1); controlsLayout->addStretch(1); QHBoxLayout *lineLayout = new QHBoxLayout; lineLayout->addWidget(lineEdit, 1); lineLayout->addSpacing(12); lineLayout->addWidget(clipboardButton); QVBoxLayout *centralLayout = new QVBoxLayout; centralLayout->addLayout(controlsLayout); centralLayout->addWidget(scrollArea, 1); centralLayout->addSpacing(4); centralLayout->addLayout(lineLayout); centralWidget->setLayout(centralLayout); setCentralWidget(centralWidget); setWindowTitle(tr("Character Map")); } Выпадающий список выбора шрифта автоматически заполняется списком доступных семейств шрифтов. Стили, которые можно использовать с каждым шрифтом, находит функция findStyles(). Эта функция вызывается всякий раз, когда пользователь выбирает другой шрифт в выпадающем списке выбора шрифта. void MainWindow::findStyles(const QFont &font) { QFontDatabase fontDatabase; QString currentItem = styleCombo->currentText(); styleCombo->clear(); Начинаем с записи текущего выделенного стиля, а также очищаем выпадающий список стиля, чтобы можно было вставить стили, связанные с текущим семейством шрифта. QString style; foreach (style, fontDatabase.styles(font.family())) styleCombo->addItem(style); int styleIndex = styleCombo->findText(currentItem); if (styleIndex == -1) styleCombo->setCurrentIndex(0); else styleCombo->setCurrentIndex(styleIndex); } Мы используем базу данных шрифтов для сбора стилей, которые доступны для текущего шрифта, и вставляем их в выпадающий список стилей. Текущий элемент сбрасывается, если исходный стиль не доступен для этого шрифта. Последние две функции - это слоты, которые реагируют на сигналы от виджета символов и кнопки главного окна. Функция insertCharacter() используется для вставки символов из виджета символов когда пользователь щёлкнет по символу: void MainWindow::insertCharacter(const QString &character) { lineEdit->insert(character); } Символ вставляется в однострочный редактор в текущую позицию курсора. Кнопка "To clipboard" главного окна соединена с функцией updateClipboard() чтобы при нажатии буфер обмена был обновлён содержимым однострочного редактора: void MainWindow::updateClipboard() { clipboard->setText(lineEdit->text(), QClipboard::Clipboard); clipboard->setText(lineEdit->text(), QClipboard::Selection); } Мы скопировали в буфер обмена весь текст из однострочного редактора, но не очистили однострочный редактор.
|
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |