Шаг 178.
Библиотека Qt.
Реализация пунктов меню Edit

    На этом шаге рассмотрим реализацию пунктов меню Edit в приложении Электронная таблица.

    Вид меню Edit (Правка), приведен на рисунке 1.


Рис.1. Меню Edit приложения Электронная таблица

    Слот cut() соответствует пункту меню Edit | Cut (Правка | Вырезать). Он реализуется просто, поскольку операция Cut выполняется с помощью операции Сору, за которой следует операция Delete.

void Spreadsheet::cut()
{
    copy();
    del();
}

    Слот сору() соответствует пункту меню Edit | Copy (Правка | Копировать).

void Spreadsheet::copy()
{
    /*обрабатываем всю выделенную область ячеек (если нет явно выделенной
    области, то ею будет просто текущая ячейка).
    Иллюстрация приводится на рис. 2.*/
    QTableWidgetSelectionRange range = selectedRange();
    QString str;

    for (int i = 0; i < range.rowCount(); ++i) {
        if (i > 0)
            //строки отделяются символом новой строки
            str += "\n";
        for (int j = 0; j < range.columnCount(); ++j) {
            if (j > 0)
                //столбцы разделяются символом табуляции
                str += "\t";
            //формула каждой выделенной ячейки добавляется в QString
            str += formula(range.topRow() + i, range.leftColumn() + j);
        }
    }
    QApplication::clipboard()->setText(str);
}


Рис.2. Копирование выделенных ячеек в буфер обмена

    Функция QTableWidget::selectedRange() возвращает список выделенных диапазонов. Мы знаем, что может быть не более одного диапазона, потому что мы задали в конструкторе режим выделения QAbstractltemView::ContiguousSelection.

    Определим функцию selectedRange(), которая возвращает выделенный диапазон:

QTableWidgetSelectionRange Spreadsheet::selectedRange() const
{
    /*какая-нибудь область всегда должна быть выбрана, поскольку
    в режиме ContiguousSelection текущая ячейка рассматривается как выделенная*/
    QList<QTableWidgetSelectionRange> ranges = selectedRanges();
    if (ranges.isEmpty())
        /*обрабатываем ситуацию, чтобы защититься от ошибки в
        нашей программе, приводящей к отсутствию текущей ячейки*/
        return QTableWidgetSelectionRange();
    /возвращаем первую (и единственную) выделенную область
    return ranges.first();
}

    Слот paste() соответствует пункту меню Edit | Paste (Правка | Вставить).

void Spreadsheet::paste()
{
    QTableWidgetSelectionRange range = selectedRange();
    //считываем текст из буфера обмена
    QString str = QApplication::clipboard()->text();
    /*и вызываем статическую функцию QString::split() для разбиения
    строки и представления ее в виде списка QStringList. Каждая строка таблицы
    представлена в этом списке одной строкой*/
    QStringList rows = str.split('\n');
    /*определяем размеры области копирования.
    Номер строки в таблице является номером строки в QStringList*/
    int numRows = rows.count();
    /*номер столбца является номером символа табуляции в
    первой строке плюс 1*/
    int numColumns = rows.first().count('\t') + 1;
    /*если выделена только одна ячейка, мы используем ее в качестве
    верхнего левого угла области вставки; в противном случае мы используем
    текущую выделенную область для вставки*/
    if (range.rowCount() * range.columnCount() != 1
            && (range.rowCount() != numRows
                || range.columnCount() != numColumns)) {
        QMessageBox::information(this, tr("Электронная таблица"),
                tr("Информация не может быть вставлена, т.к. размеры"
                   "скопированной области отличаются от размеров"
                   "области, предназначенной для вставки."));
        return;
    }
    /*при выполнении операции вставки в цикле проходим по строкам и
    разбиваем каждую строку на значения ячеек, снова используя функцию
    QString::split(), но теперь в качестве разделителя применяется знак табуляции*/
    for (int i = 0; i < numRows; ++i) {
        QStringList columns = rows[i].split('\t');
        for (int j = 0; j < numColumns; ++j) {
            int row = range.topRow() + i;
            int column = range.leftColumn() + j;
            if (row < RowCount && column < ColumnCount)
                setFormula(row, column, columns[j]);
        }
    }
    somethingChanged();
}

    Слот del() соответствует пункту меню Edit | Delete (Правка | Удалить).

void Spreadsheet::del()
{


    QList<QTableWidgetItem *> items = selectedItems();
    /*если есть выделенные ячейки, они удаляются,
    и вызывается функция somethingChanged()*/
    if (!items.isEmpty()) {
        foreach (QTableWidgetItem *item, items)
            /*для очистки ячеек достаточно использовать оператор
            delete для каждого объекта Cell. Объект QTableWidget замечает, когда
            удаляются его элементы QTableWidgetltem, и автоматически перерисовывает
            себя, если какой-нибудь из элементов оказывается видимым.
            Если мы вызываем функцию cell(), указывая координаты удаленной ячейки,
            то она возвратит нулевой указатель*/
            delete item;
        somethingChanged();
    }
} 

    Функции selectCurrentRow() и selectCurrentColumn() соответствуют пунктам меню Edit | Select | Row и Edit | Select | Column (Правка | Выделить | Строка и Правка | Выделить | Столбец).

/*используется реализация функций selectRow() и selectColumn( )
класса QTableWidget*/
void Spreadsheet::selectCurrentRow()
{
    selectRow(currentRow());
}

void Spreadsheet::selectCurrentColumn()
{
    selectColumn(currentColumn());
}

    Нам не требуется реализовывать функциональность пункта меню Edit | Select | All (Правка | Выделить | Все), поскольку она обеспечивается в QTableWidget унаследованной функцией QAbstractltemView::selectAll().

    Рассмотрим реализацию слота findNext():

void Spreadsheet::findNext(const QString &str, Qt::CaseSensitivity cs)
{
    int row = currentRow();
    int column = currentColumn() + 1;
    /*в цикле просматривает ячейки, начиная с ячейки,
    расположенной правее курсора, и двигается вправо до достижения
    последнего столбца; затем процесс идет с первого столбца строки,
    расположенной ниже, и так продолжается, пока не будет найден требуемый
    текст или пока не будет достигнута самая последняя ячейка. Например,
    если текущей является ячейка С24, поиск будет продолжаться по ячейкам
    D24, Е24,..., Z24, затем по А25, В25, С25,..., Z25 и т. д., пока не
    будет достигнута ячейка Z999*/
    while (row < RowCount) {
        while (column < ColumnCount) {
            //если соответствующее значение найдено
            if (text(row, column).contains(str, cs)) {
                //сбрасываем текущее выделение
                clearSelection();
                /*перемещаем курсор на ячейку,
                в которой оно находится*/
                setCurrentCell(row, column);
                /*делаем активным окно,
                содержащее эту электронную  таблицу Spreadsheet*/
                activateWindow();
                return;
            }
            ++column;
        }
        column = 0;
        ++row;
    }
    /*при неудачном завершении поиска мы заставляем
    приложение выдать соответствующий звуковой сигнал*/
    QApplication::beep();
}

    Слот findPrevious() похож на findNext(), но здесь цикл выполняется в обратном направлении и заканчивается в ячейке А1.

void Spreadsheet::findPrevious(const QString &str,
                               Qt::CaseSensitivity cs)
{
    int row = currentRow();
    int column = currentColumn() - 1;

    while (row >= 0) {
        while (column >= 0) {
            if (text(row, column).contains(str, cs)) {
                clearSelection();
                setCurrentCell(row, column);
                activateWindow();
                return;
            }
            --column;
        }
        column = ColumnCount - 1;
        --row;
    }
    QApplication::beep();
}

    На следующем шаге рассмотрим реализацию других меню (Инструменты и Настройки).




Предыдущий шаг Содержание Следующий шаг