Как определить правильный размер QTableWidget?

Есть ли способ установить "правильный" размер QTableWidget? (Я новичок). Этот тестовый код длиной всего 25 строк, в двух файлах, с файлом Test.h:

#include <QtGui>
class Test : public QMainWindow {
   Q_OBJECT
public:
   Test();
};

и файл Test.cpp:

#include "Test.h"
Test::Test() : QMainWindow() {
   QVBoxLayout *vbox = new QVBoxLayout;
   QPushButton  *btn = new QPushButton("Hello World etc etc etc etc etc");
   QTableWidget *tbl = new QTableWidget(2, 2);
   vbox->addWidget(btn);
   vbox->addWidget(tbl);
   QWidget *w = new QWidget;
   setCentralWidget(w);
   w->setLayout(vbox);
   resize(1, 1);
}

int main(int argc, char *argv[]) {
   QApplication app(argc, argv);
   Test test;
   test.show();
   app.exec();
}

Затем команда:

   qmake -project && qmake && make && ./Test

дает окно:

Unwanted window

Но мы хотим, конечно, нечто большее:

Wanted window

Использование tbl->width() кажется бесполезным, поскольку оно дает значение по умолчанию 640 перед test.show() и нежелательное значение 195 после. Я просмотрел подсказки и политики размера Qt до тех пор, пока голова не закружилась, и я попробовал setResizeMode(QHeaderView::Fixed) и setStretchLastSection(false). Может, мне что-то не хватает? Это с Qt 4.7.4 на CentOS 5, если это имеет значение. Спасибо за любую помощь.

Изменить: в ответ на DK, если resize(1, 1); линии resize(1, 1); нет, есть равная и противоположная проблема: окно слишком велико.

И в ответ на Донотало, добавив:

tbl->setMaximumWidth(222);
tbl->setMinimumWidth(222);
tbl->setMaximumHeight(88);
tbl->setMinimumHeight(88); 

даст желаемый размер окна (по крайней мере, на моей машине), но не по желанию. Как мы должны вычислять "константы" 222 и 88?

И Тон ответит на Qt: как заставить скрытый виджет рассчитать его макет? похоже, не работает здесь: добавление tbl->setAttribute(Qt::WA_DontShowOnScreen); tbl->show(); tbl->setAttribute(Qt::WA_DontShowOnScreen); tbl->show(); осталось значение tbl->width() без изменений на 640.

Ответ 1

Поток Как установить точный размер QTableWidget, чтобы предотвратить наличие полос прокрутки? (Qt-interest Archive, June 2007) между Лингфой Ян и Сьюзан Маккья, похоже, решает мой вопрос. В ближайшее время я опубликую более подробную информацию, если мое тестирование будет работать.

Обновление №1: Мой тест теперь создает красивое окно:

successful test window

Полный тестовый код для этого с Test.h без изменений:

#include "Test.h"

static QSize myGetQTableWidgetSize(QTableWidget *t) {
   int w = t->verticalHeader()->width() + 4; // +4 seems to be needed
   for (int i = 0; i < t->columnCount(); i++)
      w += t->columnWidth(i); // seems to include gridline (on my machine)
   int h = t->horizontalHeader()->height() + 4;
   for (int i = 0; i < t->rowCount(); i++)
      h += t->rowHeight(i);
   return QSize(w, h);
}

static void myRedoGeometry(QWidget *w) {
   const bool vis = w->isVisible();
   const QPoint pos = w->pos();
   w->hide();
   w->show();
   w->setVisible(vis);
   if (vis && !pos.isNull())
      w->move(pos);
}

Test::Test() : QMainWindow() {
   QVBoxLayout *vbox = new QVBoxLayout;
   QPushButton *btn  = new QPushButton("Hello World etc etc etc etc etc");
   QTableWidget *tbl = new QTableWidget(2, 2);
   vbox->addWidget(btn);
   vbox->addWidget(tbl);
   setCentralWidget(new QWidget);
   centralWidget()->setLayout(vbox);
   layout()->setSizeConstraint(QLayout::SetMinimumSize); // or SetFixedSize

   tbl->setVerticalHeaderItem(1, new QTableWidgetItem("two")); // change size
   myRedoGeometry(this);
   tbl->setMaximumSize(myGetQTableWidgetSize(tbl));
   tbl->setMinimumSize(tbl->maximumSize()); // optional
}

int main(int argc, char *argv[]) {
   QApplication app(argc, argv);
   Test test;
   test.show();
   app.exec();
}

Некоторые примечания:

  • Вышеупомянутое включение verticalScrollBar()->width() кажется неправильным. И в моем тестировании это всегда было либо значением по умолчанию 100, либо значением 15, если была отображена полоса прокрутки.

  • Применение show(); hide(); show(); hide(); последовательность только для QTableWidget была недостаточной, чтобы заставить Qt пересчитать геометрию в этом тесте. Мне нужно было применить его ко всему окну.

  • Любые предложения по улучшению были бы приветствующими. (Я немного подожду, прежде чем принять свой собственный ответ, если будут лучшие решения.)

Обновление № 2:

  • Поток Qt-интереса может быть неправильным (или, по крайней мере, он не согласен с моей версией Qt, работающим на моей машине) относительно деталей о том, как рассчитать размер: +1 для каждой линии сетки не требуется, но общий +4 необходим.

  • Я все еще работаю через layout()->invalidate() сравнению с QT: как просмотреть размеры виджетов в макете перед шоу().

Обновление № 3:

  • Это моя последняя версия, но я не очень доволен ею. Любые улучшения были бы очень желанными.

  • Такие вещи, как adjustSize() и layout()->invalidate() и Qt::WA_DontShowOnScreen, похоже, не помогают.

  • Блог Shrinking Qt widgets до минимально необходимого размера интересен, но использование setSizeConstraint() так же хорошо.

  • setSizeConstraint() и move() необходимы для последующих изменений в таблице, не показаны здесь.

  • В моем тестировании была странная вещь, сделанная на CentOS 5 с Qt 4.6.3 и 4.7.4. Для hide(); show(); hide(); show(); последовательность, положение окна сохраняется/восстанавливается. Но примерно в 25% случаев (паттерн был нерегулярным) восстановленное положение было бы на 24 пикселя выше на экране, предположительно на высоту заголовка окна. И примерно в 10% случаев значение, возвращаемое pos() будет равно нулю. Сайт Qt говорит, что для X11 ему нужна nifty heuristics and clever code для этого, но что-то кажется сломанным.

Ответ 2

Вот мое (питоновское) решение этой проблемы:

table.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)

table.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff)
table.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff)

table.resizeColumnsToContents()
table.setFixedSize(table.horizontalHeader()->length()+table.verticalHeader()->width(), table.verticalHeader()->length()+table.horizontalHeader()->height())

Это дает мне таблицу с размером, прекрасно обертывающим все строки и столбцы.

Ответ 3

Недавно я нашел ту же проблему. Я тестировал десятки функций, найденных Google в Qt Center, Qt Project, но ни один из них не дал мне правильный размер. Я сделал эту функцию, которая, по моему мнению, вычисляет ближайший размер таблицы. Функция дает правильную вертикаль, но немного больший горизонтальный размер - похоже, она по-прежнему покрывает вертикальную полосу прокрутки. Кроме того, я проверил, превышают ли размеры стола размер рабочего стола:

const QRect MyApp::adjustTableSize(
    QTableView* tv,
    const QVBoxLayout* lay,
    const int& maxRows,
    const int& maxCols) {
// get Desktop size
using std::size_t;
QDesktopWidget desktop;
size_t desW = desktop.screen()->width();
size_t desH = desktop.screen()->height();

int leftM,rightM,topM,bottomM;
lay->getContentsMargins(&leftM,&topM,&rightM,&bottomM);

size_t extraTopHeight = topM + tv->frameWidth();
size_t extraBottomHeight = bottomM + tv->frameWidth();
size_t extraLeftWidth = leftM + tv->frameWidth();
size_t extraRightWidth = rightM + tv->frameWidth();
size_t w = tv->verticalHeader()->width() + extraLeftWidth + extraRightWidth;
size_t h = tv->horizontalHeader()->height() + extraTopHeight + extraBottomHeight;
for(size_t col = 0; col < maxCols; ++col) {
    w += tv->columnWidth(col);
}
for(size_t row = 0; row < maxRows; ++row ) {
    h += tv->rowHeight(row);
}

std::size_t x,y;
if((w - extraLeftWidth - extraRightWidth) > desW) {
    x = 0;
    w = desW - extraLeftWidth - extraRightWidth;
} else
    x = (desW - w)/2;
if(h - extraTopHeight - extraBottomHeight - QStyle::PM_TitleBarHeight > desH) {
    y = extraTopHeight + QStyle::PM_TitleBarHeight;
    h = desH - (extraTopHeight + QStyle::PM_TitleBarHeight + extraBottomHeight);
} else
    y = (desH - h)/2;
return QRect(x,y,w,h);
}

Несколько примечаний стороны:

QTableView.
Я использовал QTableView, но я думаю, что он также может работать с QTableWidget.

QVBoxLayout.
Я довольно уверен, что любой из макетов, доступных в Qt, можно использовать вместо этой функции.

Надеюсь, эта функция может помочь кому угодно.

Ответ 4

Я имел дело с одной и той же проблемой, и я изменил предыдущие ответы, чтобы получить правильный рабочий код.

Во-первых, мы получаем поля содержимого из QTableWidget. Если вы просто хотите настроить его ширину, вам нужно отключить горизонтальную полосу прокрутки.

int w = 0, h = 0;
ui->tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

w += ui->tableWidget->contentsMargins().left()
        + ui->tableWidget->contentsMargins().right();
h += ui->tableWidget->contentsMargins().top()
        + ui->tableWidget->contentsMargins().bottom();

Затем мы добавляем ширину вертикального заголовка в w. Вертикальный заголовок представляет собой специальный столбец, содержащий индексы строк и по умолчанию включен. мы также добавляем высоту горизонтального заголовка в h. Горизонтальный заголовок представляет собой специальную строку, содержащую заголовки столбцов.

w += ui->tableWidget->verticalHeader()->width();
h += ui->tableWidget->horizontalHeader()->height();

Затем мы добавляем ширину каждого столбца в w, а высота каждой строки - h.

for (int i=0; i<ui->tableWidget->columnCount(); ++i)
    w += ui->tableWidget->columnWidth(i);
for (int i=0; i<ui->tableWidget->rowCount(); ++i)
    h += ui->tableWidget->rowHeight(i);

Теперь, если w или h слишком велики, чтобы поместиться в окне, мы уменьшаем их, чтобы они соответствовали ui->centralWidget->contentsRect().

if (w > ui->centralWidget->contentsRect().width())
    w = ui->centralWidget->contentsRect().width();
if (h > ui->centralWidget->contentsRect().height())
    h = ui->centralWidget->contentsRect().height();

К сожалению, я не знаю лучшего способа применения w и h для установки ширины и высоты QTableWidget. Если я использую:

ui->tableWidget->setMinimumWidth(w);
ui->tableWidget->setMaximumWidth(w);
ui->tableWidget->setMinimumHeight(h);
ui->tableWidget->setMaximumHeight(h);

Тогда пользователь не сможет изменять размер виджета вообще. Я думаю, нам нужно использовать какую-то ручную компоновку.

Это может показаться немного не по теме, но если вы хотите, чтобы ваша таблица имела более привлекательный вид, используйте эту команду для изменения размеров столбцов, чтобы они соответствовали их данным:

ui->tableWidget->resizeColumnsToContents();

Ответ 5

Я не могу комментировать сообщение Джозефа Кунейя /Celdor, поэтому я пишу его здесь: Чтобы получить истинное значение QStyle::PM_TitleBarHeight, вы должны сделать это следующим образом:

QStyle *style;
if(tv->style())
{
    style = tv->style();
}
else
{
    style = QApplication::style();
}
int titleBarHeight = style->pixelMetric(QStyle::PM_TitleBarHeight));

Это связано с тем, что перечисление не задало точных значений. Я думаю, ваше непонимание того, как это работает, было связано с вводящей в заблуждение Qt- документацией, где похоже, что перечисление непосредственно представляет значения, в то время как, конечно, нет (поскольку это зависит от стиля).

Ответ 6

видимо, есть ошибка --some, возможно, - в функции horizontalHeader() → width(); это решение сработало для меня:

    //given a QtableWidget* called x
    int previous_width= x->horizontalHeader()->width();//get the width
    //the width is relating to all the columns including the vertical headers 
    int previous_length=x->horizontalHeader()->length();//and the length
    // the length is relating to all the columns excluding the size of the vertical headers
    int vertical_headers_width=(previous_width-previous_length);
    x->resizeColumnsToContents();//now we resize
    x->resizeRowsToContents();
    //if we call again x->horizontalHeader()->width();
    // we get a number that looks to me not correct (maybe is my ignorance here)
    x->setFixedWidth(x->horizontalHeader()->length()+vertical_headers_width);

   //by adding to the length of the data columns the old width_of_verticla_headers_column we get the correct size;

Ответ 7

Чтобы ширина таблицы была равна ширине вертикальных и горизонтальных заголовков и в два раза больше ширины кадра. То же самое касается высоты:

w = verticalHeader()->width() + horizontalHeader()->length() + frameWidth()*2
h = horizontalHeader()->height() + verticalHeader()->length() + frameWidth()*2

Существует несколько способов установить их в зависимости от ваших обстоятельств. Предпочтительным, вероятно, было бы переопределить sizeHint(), чтобы вернуть ваши значения. Таким образом, таблица остается изменчивой, но отсутствующие другие требования будут вашими размерами.

Ключом к этой работе является использование метода length() в правильных местах, а не просто width() или height(). Я не совсем уверен, почему, но здесь он делает правильные вещи ;-)