Connecting selection of row in 2 QTableWidget - c++

I am trying to connect row selections from two QTableWidget.
I mean, when I select one row in Table 1, I want my program selects the same row in table 2. The two table dont have the same number of column so I cannot just select one item for the first and select the same item on the second able.
I have tried to use the following without success:
connect(ui->table1->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), ui->table2->selectionModel(), SLOT(setCurrentIndex(QModelIndex)));
It is written:
QObject::connect: No such slot QItemSelectionModel::setCurrentIndex(QModelIndex)
Do you know what is going wrong?

The problem is caused because setCurrentIndex() has two parameters, and not just one, plus the signatures do not match. So in these cases you should use a lambda and use selectRow():
#include <QApplication>
#include <QHBoxLayout>
#include <QTableWidget>
#include <QItemSelectionModel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto *table1 = new QTableWidget(4, 3);
table1->setSelectionBehavior(QAbstractItemView::SelectRows);
auto table2 = new QTableWidget(4, 4);
table2->setSelectionBehavior(QAbstractItemView::SelectRows);
QObject::connect(table1->selectionModel(), &QItemSelectionModel::currentRowChanged,
[table2](const QModelIndex &current, const QModelIndex & previous)
{
if(previous.isValid())
table2->selectRow(current.row());
});
QWidget w;
auto lay = new QHBoxLayout(&w);
lay->addWidget(table1);
lay->addWidget(table2);
w.show();
return a.exec();
}

Related

[Qt][C++] setMinimumWidth and setMaximumWidth not working on label

I have label created and want tooltip over it, I want to set tooltip's maximum and minimum width but somehow its not working.
I am not expert to QT, not able to figure out the reason.
Code:
#include "widget.h"
#include <QApplication>
#include <QListWidget>
#include <QListWidgetItem>
#include <QLabel>
#include <QHBoxLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget listWidget;
listWidget.setContentsMargins(QMargins(0, 0, 0, 0));
for (int i = 0; i < 5; ++i)
{
QListWidgetItem* item = new QListWidgetItem();
auto *itemWidget = new QWidget();
auto *textLabel = new QLabel(QString("Item %1").arg(i + 1), itemWidget);
textLabel->setMinimumWidth(100); //Not working whatever value I set
textLabel->setMaximumWidth(400); //Not working whatever value I set
textLabel->setToolTip("<p>This is the looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooonggggggggggggggg name </p>");
listWidget.addItem(item);
listWidget.setItemWidget(item, itemWidget);
}
listWidget.show();
return a.exec();
}
Tooltip:
can someone please help.
You cant directly set max and min on tooltip , hence you should indrectly do that(for this usecase):
static const QString FORMATTER = "<p>%1</p>";
QString tooltip =
"This is the "
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooonggggggggggggggg name.";
static constexpr auto maximum = 10;
textLabel->setToolTip(FORMATTER.arg(tooltip.mid(0, maximum)));
Update:
if you want exactly have your widget with any properties for tooltip , you can override your events and show the ToolTipWidget that reimplemented.
i.e: Qt WIdget inside ToolTip
textLabel->setMinimumWidth(100);
textLabel->setMaximumWidth(400);
This will set the min/max width of the label itself not the tooltip.
If you want to customize tooltip behavior, you'll have to override the event() function to catch QEvent::ToolTip (and probably QEvent::ToolTipChange) and draw it yourself using QToolTip::showText()
please use :textLabel->setToolTip

How to connect two QComboBox so that the second shows only certain items?

I have two QComboBoxes to connect with each other.
In particular, I'd like that if a scaling is made in the first QComboBox, this is not also shown in the second QComboBox and vice versa..
This is my code:
auto lingua_originaleT = new QComboBox();
lingua_originaleT->addItems({"Italiano", "Inglese", "Francese", "Spagnolo", "Portoghese", "Tedesco", "Cinese"});
auto lingua_targetT = new QComboBox();
lingua_targetT->addItems({"Italiano", "Inglese", "Francese", "Spagnolo", "Portoghese", "Tedesco", "Cinese"});
The result should be like this:
The same language should not appear in the second drop-down menu as well
One possible solution is to use QSortFilterProxyModel to do the filtering:
#include <QApplication>
#include <QComboBox>
#include <QHBoxLayout>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QWidget>
class FilterProxyModel: public QSortFilterProxyModel{
public:
using QSortFilterProxyModel::QSortFilterProxyModel;
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const{
if(filterRegExp().isEmpty())
return true;
return !QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.resize(640, 480);
widget.show();
QStandardItemModel *model = new QStandardItemModel(&widget);
const QStringList values{"Italiano", "Inglese", "Francese", "Spagnolo", "Portoghese", "Tedesco", "Cinese"};
for(const QString & value: values){
model->appendRow(new QStandardItem(value));
}
QComboBox *lingua_originaleT = new QComboBox;
FilterProxyModel *proxy_originalT = new FilterProxyModel(&widget);
proxy_originalT->setSourceModel(model);
lingua_originaleT->setModel(proxy_originalT);
QComboBox *lingua_targetT = new QComboBox;
FilterProxyModel *proxy_targetT = new FilterProxyModel(&widget);
proxy_targetT->setSourceModel(model);
lingua_targetT->setModel(proxy_targetT);
QHBoxLayout *lay = new QHBoxLayout(&widget);
lay->addWidget(lingua_originaleT);
lay->addWidget(lingua_targetT);
QObject::connect(lingua_originaleT, &QComboBox::currentTextChanged, proxy_targetT, &FilterProxyModel::setFilterFixedString);
QObject::connect(lingua_targetT, &QComboBox::currentTextChanged, proxy_originalT, &FilterProxyModel::setFilterFixedString);
lingua_originaleT->setCurrentIndex(0);
lingua_targetT->setCurrentIndex(1);
return a.exec();
}
Internally, QCombobox uses QStandardItemModel - unless you provided a custom one using setModel().
That means you can do things like:
// Just some setup
auto combo = new QComboBox(this);
combo->addItems({ "Item0", "Item1", "Item2" });
// Here is the interesting bit
auto model = qobject_cast<QStandardItemModel*>(combo->model());
auto item = model->item(0); // <-- index in the combobox
item->setEnabled(false); // <-- You can't select it anymore
if(combo->currentIndex() == 0) // Choose another one if it's already selected
combo->setCurrentIndex(1);
// From now, Item 0 will be visible in the dropdown but not selectable by the user.
I leave you find a way to get the 2 boxes disabling each other items (And importantly, enabling them back once if the selection change). This is a matter of listening the index changed signals of each boxes and and updating the other model accordingly.
I solved and the solution works perfectly!
Here's how I did it:
lingua_originaleT = new QComboBox();
lingua_targetT = new QComboBox();
QStringList traduzioneLangList = {"Italiano", "Inglese", "Francese", "Spagnolo", "Portoghese", "Tedesco", "Cinese"};
lingua_originaleT->clear();
lingua_originaleT->addItems(traduzioneLangList);
lingua_originaleT->setCurrentIndex(-1);
lingua_targetT->clear();
lingua_targetT->addItems(traduzioneLangList);
lingua_targetT->setCurrentIndex(-1);
connect(lingua_originaleT, &QComboBox::currentTextChanged, lingua_targetT, [=](const QString &selection) {
auto tarLangList = traduzioneLangList;
lingua_targetT->blockSignals(true);
tarLangList.removeOne(selection);
lingua_targetT->clear();
lingua_targetT->addItems(traduzioneLangList);
lingua_targetT->blockSignals(false);
});
connect(lingua_targetT, &QComboBox::currentTextChanged, lingua_originaleT, [=](const QString &selection) {
auto tarLangList = traduzioneLangList;
lingua_originaleT->blockSignals(true);
tarLangList.removeOne(selection);
lingua_originaleT->clear();
lingua_originaleT->addItems(traduzioneLangList);
lingua_originaleT->blockSignals(false);
});
lingua_originaleT->setCurrentIndex(0);
Many thanks to #lifof on reddit!

Fitting rows in QTableView, getting rid of extra space

I'm trying to generate a simple table (2 rows and 2 columns) and write it to a pdf file, using Qt 4.8.0.
So far, I generate the pdf but there is extra space at the bottom of the "printed" table:
I got the same problem with the right side of the table but I managed to get rid of it. But in this case I am clueless.
Here's the code I have now (all of this code is located in main.cpp):
Main
#include <QtGui/QApplication>
#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtGui/QPrinter>
#include <QtGui/QHeaderView>
#include <QtGui/QPainter>
#include <QtGui/QTableWidget>
#include <QtGui/QTableWidgetItem>
/**/
/* Here are the functions.
/**/
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMap<QString,int> values;
values.insert("X",7);
values.insert("Y",13);
bool status = TableWidgetToPdf("FromWidget.pdf",values);
return a.exec();
}
TableWidgetToPdf
bool TableWidgetToPdf(const QString& title, const QMap<QString, int>& values) {
QTableWidget* table = GenerateTable(values);
QPrinter printer;
printer.setOutputFileName(title);
printer.setOutputFormat(QPrinter::PdfFormat);
QPainter painter(&printer);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
printer.setPaperSize(QPrinter::A4);
table->render(&painter);
painter.end();
printer.newPage();
delete table;
return true;
};
GenerateTable
QTableWidget* GenerateTable(const QMap<QString,int>& values) {
QTableWidget* table = new QTableWidget;
table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table->setRowCount(2);
table->setColumnCount(2);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setShowGrid(false);
table->verticalHeader()->hide();
QStringList h_labels;
h_labels << "X" << "Y";
table->setHorizontalHeaderLabels(h_labels);
table->horizontalHeader()->setFont( QFont("Times", 10, QFont::Bold) );
table->horizontalHeader()->setStretchLastSection(true);
QTableWidgetItem* item00 = new QTableWidgetItem( QString::number(values["X"]));
item00->setTextAlignment(Qt::AlignCenter);
table->setItem(0,0, item00 );
QTableWidgetItem* item01 = new QTableWidgetItem( QString::number(values["Y"]) );
item01->setTextAlignment(Qt::AlignCenter);
table->setItem(0,1,item01);
table->setItem(1,0,new QTableWidgetItem("ABCD"));
return table;
};
NOTE:
Putting
table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
table->verticalHeader()->setResizeMode(QHeaderView::Stretch);
in GenerateTable the space disappears, but the cells are resized and consume too much space than needed for their contents. I would like to avoid that if possible:
EDIT:
OK.
In the end I achieved what I wanted by getting rid of the QTableWidget. I had to create the table using html and feeding it to a QTextEditor. Isn't any way to achieve this with a QTableWidget?
Have you tried the flags for resize content?
Try the following code, I don't have access to Qt right now.
table->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
table->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
Hope that works!
I realise that this is an old post but it seems fairly often read.
I tried the same methods that you tried and none worked. Eventually I used a QTableView and added an extra method called by adding/removing rows.
void
TitleView::verticalResizeTableViewToContents()
{
auto count = m_model->rowCount(QModelIndex());
auto scrollBarHeight = horizontalScrollBar()->height();
auto horizontalHeaderHeight = horizontalHeader()->height();
auto rowTotalHeight = scrollBarHeight + (horizontalHeaderHeight * count);
setMinimumHeight(rowTotalHeight);
setMaximumHeight(rowTotalHeight);
}

QTreeView / QFileSystemModel set header labels

Pretty simple task but I didn't manage to find anything useful in documentation. I want a QTreeView to contain a single column called "Files" with data from QFileSystemView. Here's what I've got:
QFileSystemModel *projectFiles = new QFileSystemModel();
projectFiles->setRootPath(QDir::currentPath());
ui->filesTree->setModel(projectFiles);
ui->filesTree->setRootIndex(projectFiles->index(QDir::currentPath()));
// hide all but first column
for (int i = 3; i > 0; --i)
{
ui->filesTree->hideColumn(i);
}
That gives me a single column with "Name" header. How do I rename this header?
QAbstractItemModel::setHeaderData() should work. If not, you can always inherit from QFileSystemModel and override headerData().
Quick but a little dirty trick (please note w.hideColumn()):
#include <QApplication>
#include <QFileSystemModel>
#include <QTreeView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView w;
QFileSystemModel m;
m.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
m.setRootPath("C:\\");
w.setModel(&m);
w.setRootIndex(m.index(m.rootPath()));
w.hideColumn(3);
w.hideColumn(2);
w.hideColumn(1);
w.show();
return a.exec();
}
You can subclass QFileSystemModel and overide method headerData(). For example, if you want only to change first header label and leave the rest with their original values, you can do:
QVariant MyFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const {
if ((section == 0) && (role == Qt::DisplayRole)) {
return "Folder";
} else {
return QFileSystemModel::headerData(section,orientation,role);
}
}

What is wrong with this Qt code?

I was reading MVC tutorial and wanted to try out the code, but for some reason (which I'm not able to figure out) it is not working.
This code is suppose to show contents of current directory in QListWidget.
#include <QApplication>
#include <QFileSystemModel>
#include <QModelIndex>
#include <QListWidget>
#include <QListView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileSystemModel *model = new QFileSystemModel;
QString dir = QDir::currentPath();
model->setRootPath(dir);
QModelIndex parentIndex = model->index(dir);
int numRows = model->rowCount(parentIndex);
QListWidget *list = new QListWidget;
QListWidgetItem *newItem = new QListWidgetItem;
for(int row = 0; row < numRows; ++row) {
QModelIndex index = model->index(row, 0, parentIndex);
QString text = model->data(index, Qt::DisplayRole).toString();
newItem->setText(text);
list->insertItem(row, newItem);
}
list->show();
return a.exec();
}
There are 2 problems.
The first described by Frank Osterfeld's answer. Move:
QListWidgetItem *newItem = new QListWidgetItem;
into your loop.
The second has to do with QFileSystemModel's threading model. from the docs for QFileSystemModel:
Unlike the QDirModel, QFileSystemModel uses a separate thread to populate itself so it will not cause the main thread to hang as the file system is being queried. Calls to rowCount() will return 0 until the model populates a directory.
and
Note: QFileSystemModel requires an instance of a GUI application.
I don't think QFileSystemModel() will work properly until after the Qt event loop is running (which is started by a.exec() in your example).
In your case, model->rowCount(parentIndex) returns 0, even though there are items in the directory (at least that's what it's doing on my test).
Replacing QFileSystemModel with QDirModel (and removing the model->setRootPath(dir) call, which QDirModel` doesn't support) populates the list.
You must create a new item for each row. Move
QListWidgetItem *newItem = new QListWidgetItem;
into the for loop.