Пример Calendar
Файлы:
Пример Calendar показывает как создать содержимое в виде форматированного текста и отобразить его с помощью редактора форматированного текста.
В частности, пример демонстрирует следующее:
- Использование текстового редактора с текстовым документом
- Вставка таблиц и фреймов в документ
- Перемещение внутри таблицы
- Вставка текста в различных стилях
Редактор форматированного текста, использованный для отображения документа, использован внутри главного окна приложения.
Определение класса MainWindow
Класс MainWindow предоставляет виджет редактора текста и некоторые контролы которые позволяют пользователю изменять отображаемый месяц и год. Размер шрифта, использованного для текста, может быть также настроен.
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
public slots:
void setFontSize(int size);
void setMonth(int month);
void setYear(QDate date);
private:
void insertCalendar();
int fontSize;
QDate selectedDate;
QTextBrowser *editor;
};
Приватная функция insertCalendar() выполняет большинство работы полагаясь на переменные fontSize и selectedDate для записи полезной информации в редактор.
Реализация класса MainWindow
Конструктор MainWindow устанавливает интерфейс пользователя и инициализирует переменные используемые для генерации календаря для каждого месяца.
MainWindow::MainWindow()
{
selectedDate = QDate::currentDate();
fontSize = 10;
QWidget *centralWidget = new QWidget;
Мы начинаем установкой значений по умолчанию для выбранной даты которая будет подсвечена в календаре и размера шрифта который будет использован. Так как мы используем QMainWindow для интерфейса пользователя, мы конструируем виджет для использования в качестве центрального.
Интерфейс пользователя будет включать линию контролов над сгенерированным календарём; мы конструируем метку и выпадающий список что бы позволить выбирать месяцы и счётчик для года. Эти виджеты настраиваются чтобы предоставить разумный диапазон значений для пользователя:
QLabel *dateLabel = new QLabel(tr("Date:"));
QComboBox *monthCombo = new QComboBox;
for (int month = 1; month <= 12; ++month)
monthCombo->addItem(QDate::longMonthName(month));
QDateTimeEdit *yearEdit = new QDateTimeEdit;
yearEdit->setDisplayFormat("yyyy");
yearEdit->setDateRange(QDate(1753, 1, 1), QDate(8000, 1, 1));
Мы используем объект selectedDate чтобы получить текущий месяц и год, и мы устанавливаем их в выпадающий список и счетчик:
Размер шрифта отображён в счетчике который мы ограничиваем разумным диапазоном значений:
QLabel *fontSizeLabel = new QLabel(tr("Font size:"));
QSpinBox *fontSizeSpinBox = new QSpinBox;
fontSizeSpinBox->setRange(1, 64);
fontSizeSpinBox->setValue(10);
editor = new QTextBrowser;
insertCalendar();
Мы конструируем редактор и используем функцию insertCalendar() что бы создать для него календарь. Каждый календарь отображается в том же редакторе текста; в данном примере мы используем QTextBrowser так как мы не позволяем редактировать календарь.
Контролы, используемые для установки месяца, года и размера шрифта не будут оказывать никакого эффекта на внешний вид календаря пока мы не соединим некторые сигналы и слоты:
connect(monthCombo, SIGNAL(activated(int)), this, SLOT(setMonth(int)));
connect(yearEdit, SIGNAL(dateChanged(QDate)), this, SLOT(setYear(QDate)));
connect(fontSizeSpinBox, SIGNAL(valueChanged(int)),
this, SLOT(setFontSize(int)));
Сигналы подключены к некоторым простым слотам в классе MainWindow который мы опишем позже.
Мы создаем компоновку для управления виджетами, которые мы сделали:
QHBoxLayout *controlsLayout = new QHBoxLayout;
controlsLayout->addWidget(dateLabel);
controlsLayout->addWidget(monthCombo);
controlsLayout->addWidget(yearEdit);
controlsLayout->addSpacing(24);
controlsLayout->addWidget(fontSizeLabel);
controlsLayout->addWidget(fontSizeSpinBox);
controlsLayout->addStretch(1);
QVBoxLayout *centralLayout = new QVBoxLayout;
centralLayout->addLayout(controlsLayout);
centralLayout->addWidget(editor, 1);
centralWidget->setLayout(centralLayout);
setCentralWidget(centralWidget);
Наконец, центральный виджет установлен для окна.
Каждый календарь создается для редактора функцией insertCalendar() которая использует дату и размер шрифта, определенными приватными переменными selectedDate и fontSize, для производства удобной схемы для указанного месяца и года.
void MainWindow::insertCalendar()
{
editor->clear();
QTextCursor cursor = editor->textCursor();
cursor.beginEditBlock();
QDate date(selectedDate.year(), selectedDate.month(), 1);
Мы начинаем с очистки документа форматированного текста в редакторе и получим текстовый курсов из редактора который мы будем использовать для добавления содержимого. Мы также создаём объект QDate основанный на текущей выбранной дате.
Календарь создается из таблицы с серым цветом фона которая содержит семь столбцов: по одному на каждый день недели. Она помещается в центр таблицы с равными отступами слева и справа от неё. Все эти свойства установлены в объекте QTextTableFormat:
QTextTableFormat tableFormat;
tableFormat.setAlignment(Qt::AlignHCenter);
tableFormat.setBackground(QColor("#e0e0e0"));
tableFormat.setCellPadding(2);
tableFormat.setCellSpacing(4);
Каждая ячейка таблицы будет иметь отступ что бы сделать текст более удобным для чтения.
Мы хотим что бы у столбцов была равная ширина, поэтому мы предоставляем вектор, содержащий ширину в процентах для каждого из них и устанавливаем ограничения в QTextTableFormat:
QVector<QTextLength> constraints;
constraints << QTextLength(QTextLength::PercentageLength, 14)
<< QTextLength(QTextLength::PercentageLength, 14)
<< QTextLength(QTextLength::PercentageLength, 14)
<< QTextLength(QTextLength::PercentageLength, 14)
<< QTextLength(QTextLength::PercentageLength, 14)
<< QTextLength(QTextLength::PercentageLength, 14)
<< QTextLength(QTextLength::PercentageLength, 14);
tableFormat.setColumnWidthConstraints(constraints);
Ограничения, использованные для ширины столбцов полезны только в случае когда таблица имеет подходящее число столбцов. С определенным форматом для таблицы мы создаём новую таблицу с одной строкой и семью столбцами в текущей позиции курсора:
QTextTable *table = cursor.insertTable(1, 7, tableFormat);
Нам необходим только один столбец для начала; остальные могут быть добавлены когда они нам понадобятся. Использование такого подхода означает, что нам не нужно выполнять расчеты даты пока мы добавляем ячейки в таблицу.
Когда вставляем объекты в документ с функцией вставки курсора, курсор автоматически перемещается внутри вставленного объекта. Это означает, что мы можем незамедлительно начать модифицировать таблицу изнутри:
QTextFrame *frame = cursor.currentFrame();
QTextFrameFormat frameFormat = frame->frameFormat();
frameFormat.setBorder(1);
frame->setFrameFormat(frameFormat);
Так как таблица имеет внешний фрейм, нам необходимо получить внешний фрейм и его формат что бы мы могли настроить его. После совершения изменений которых мы хотим, мы устанавливаем формат фрейма используя модифицированный формат объекта. Мы должны предоставить таблице внешнюю рамку толщиной один пиксел.
QTextCharFormat format = cursor.charFormat();
format.setFontPointSize(fontSize);
QTextCharFormat boldFormat = format;
boldFormat.setFontWeight(QFont::Bold);
QTextCharFormat highlightedFormat = boldFormat;
highlightedFormat.setBackground(Qt::yellow);
Похожим способом мы получаем текущий формат символов курсора и создаем основанный на нём настроенный формат.
Мы не устанавливаем формат для курсора так как это изменит текущий формат символов; вместо этого мы используем настроенный формат только когда мы вставляем текст. Следующий цикл вставляет дни недели в таблицу как полужирный текст:
for (int weekDay = 1; weekDay <= 7; ++weekDay) {
QTextTableCell cell = table->cellAt(0, weekDay-1);
Для каждого дня недели мы получаем существующую ячейку таблицы в первой строке (row 0) используя функцию таблицы cellAt(). Так как мы начинаем считать дни недели с первого дня (понедельник), мы вычитаем 1 из weekDay что бы гарантировать что мы получим ячейку для правильного столбца таблицы.
Прежде чем текст может быть вставлен в ячейку мы должны получить курсор с правильной позицией в документе. Ячейка предоставляет функцию для этой цели и мы используем этот курсор для вставки теста используя формат символов boldFormat который мы создали ранее:
QTextCursor cellCursor = cell.firstCursorPosition();
cellCursor.insertText(QString("%1").arg(QDate::longDayName(weekDay)),
boldFormat);
}
Вставка текста в объекты документа обычно следует аналогичному шаблону. Каждый объект может предоставить новый курсор который соответствует первой позиции внутРи него и это может быть использовано для вставки нового содержимого. Мы продолжаем использовать этот шаблон так как мы вставляем дни месяца в таблицу.
Так как каждый месяц имеет более семи дней мы вставляем одну строку в начале и добавляем дни пока не достигнем конец месяца. Если встречается текущая дата, она вставляется со специальным форматом (созданным ранее) что её выделяет:
table->insertRows(table->rows(), 1);
Мы добавляем новую строку в таблицу в конце каждой неделе только если следующая неделя попадает в выделенный на данный момент месяц.
Для каждого создаваемого календаря мы меняем заголовок окна для отражения выделенного в данный момент месяца и года:
setWindowTitle(tr("Calendar for %1 %2"
).arg(QDate::longMonthName(selectedDate.month())
).arg(selectedDate.year()));
}
Функция insertCalendar() полагается на актуальные значения месяца, года и размера шрифта. Они установлены в следующих слотах:
void MainWindow::setFontSize(int size)
{
fontSize = size;
insertCalendar();
}
Функция setFontSize() просто меняет приватную переменную fontSize перед обновлением календаря.
void MainWindow::setMonth(int month)
{
selectedDate = QDate(selectedDate.year(), month + 1, selectedDate.day());
insertCalendar();
}
Слот setMonth вызывается когда QComboBox используемый для выбора месяца обновляется. Поставляемое значение это выбранная в настоящий момент строка с выпадающем списке. Мы добавляем 1 к этому значению что бы получить правильный номер месяца и создаем новый QDate который основан на уже существующем. После этого календарь обновляется для использования этой новой даты.
void MainWindow::setYear(QDate date)
{
selectedDate = QDate(date.year(), selectedDate.month(), selectedDate.day());
insertCalendar();
}
Слот setYear() вызывается когда QDateTimeEdit используемый для выбора года обновляется. Поставляемое значение это объект QDate; это делает конструктор нового значения для selectedDate проще. Мы обновляем календарь перед использованием новой даты.
|