Qt ComboBox 2 columns or horizontal columns? - c++

I have a QComboBox with a rather long drop down. With style sheets qss I can reduce the length, but I wonder if I can display the items horizontally or in 2 columns?
As my values are just keys (1 character) I could use 2,3,4 columns or use something which expands horizontally instead of vertically. Any chance to do so?

It must be replaced with a QListView with a flow QListView::LeftToRight and set an appropriate size of the view and popup:
#include <QApplication>
#include <QBoxLayout>
#include <QComboBox>
#include <QListView>
class HorizontalComboBox: public QComboBox
{
public:
HorizontalComboBox(QWidget *parent = nullptr):
QComboBox(parent)
{
QListView *m_view = new QListView(this);
m_view->setFlow(QListView::LeftToRight);
setView(m_view);
for(QWidget* o: findChildren<QWidget *>()){
if(o->inherits("QComboBoxPrivateContainer")) {
//popup
o->setFixedHeight(view()->height());
break;
}
}
}
virtual void showPopup() override {
QComboBox::showPopup();
int w = 0;
for(int i=0; i<count(); i++){
QModelIndex ix= model()->index(i, modelColumn(), rootModelIndex());
w += view()->visualRect(ix).width();
}
view()->setFixedWidth(w);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
HorizontalComboBox w;
w.addItems(QString("ABCDEFGHIJKLMNOPQRSTUVWXYZ").split("", QString::SkipEmptyParts));
w.show();
return a.exec();
}

Related

Coloring a specific line (indicated by the cursor) in textedit for QT , but the cursor's position changes with a function every time button is pressed

I edited it like this. But all text is not printed in textEdit even though the word is added to the correct position and colored.
ui->textEdit->setText(display_text);
QTextcursor cursor=ui->textEdit->textCursor();
cursor.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor,cursor_position);
cursor.insertHtml("<span style=color:red;>"+coloring_string+"</span>");
ui->textEdit->setTextCursor(cursor);
You have to use setExtraSelections:
#include <QApplication>
#include <QTextEdit>
class Editor: public QTextEdit {
public:
Editor(QWidget *parent=nullptr): QTextEdit(parent){
connect(this, &QTextEdit::cursorPositionChanged, this, &Editor::highlightCurrentLine);
highlightCurrentLine();
}
private:
void highlightCurrentLine(){
QList<QTextEdit::ExtraSelection> extraSelections;
if (!isReadOnly()) {
QTextEdit::ExtraSelection selection;
QColor lineColor("red");
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
extraSelections.append(selection);
}
setExtraSelections(extraSelections);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle("fusion");
Editor editor;
editor.resize(640, 480);
editor.show();
return a.exec();
}

Adding horizontal slider to QTableWidget

I am trying to design something like a timeline view for my video player. I decided to use QTableWidget as the timeline since it suits my purpose. My widget looks like this:
I want the green line to run through the widget when i click on play. Here is my MVCE example:
//View.cpp
View::View(QWidget* parent) : QGraphicsView(parent)
{
QGraphicsScene* scene = new QGraphicsScene(this);
TableWidget* wgt = new TableWidget;
scene->addWidget(wgt);
QGraphicsLineItem* item = new QGraphicsLineItem(30, 12, 30, wgt->height() - 9);
item->setPen(QPen(QBrush(Qt::green), 3));
item->setFlags(QGraphicsItem::ItemIsMovable);
scene->addItem(item);
setScene(scene);
}
Here is TableWidget
TableWidget::TableWidget(QWidget* parent) : QTableWidget(parent)
{
setColumnCount(10);
setRowCount(10);
//Hides the numbers on the left side of the table
verticalHeader()->hide();
//Prevents top header from highlighting on selection
horizontalHeader()->setHighlightSections(false);
//Makes the cells un-editable
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionMode(QAbstractItemView::MultiSelection);
}
Problem:
Moving the line item reflects changes to the scene it has been added to i.e. when i drag the line using mouse, the line moves in the scene but not inside the TableWidget.
What do i want
I want the green bar to act like a horizontal slider. It should go through the TableWidget horizontally making the widget scroll along with it showing the current position of the frame indicated by the numbers shown on the header.
Something like as shown below (notice the Red line):
I know this might not be the best way to implement a timeline but i would appreciate any other ideas to implement.
A possible solution is to overwrite the itemChange method to restrict movement as shown below:
#include <QApplication>
#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QTableWidget>
#include <QHeaderView>
#include <QGraphicsProxyWidget>
class SeekBarItem: public QGraphicsRectItem{
public:
SeekBarItem(QRectF rect, QGraphicsItem *parent=nullptr)
: QGraphicsRectItem(rect, parent)
{
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setBrush(Qt::red);
}
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value){
if(change == QGraphicsItem::ItemPositionChange){
QPointF p = value.toPointF();
qreal max = parentItem()->boundingRect().bottom()- boundingRect().bottom();
qreal min = parentItem()->boundingRect().top()-boundingRect().top();
if(p.y() > max) p.setY(max);
else if (p.y() < min) p.setY(min);
p.setX(pos().x());
return p;
}
return QGraphicsRectItem::itemChange(change, value);
}
};
class TableWidget: public QTableWidget
{
public:
TableWidget(QWidget* parent=nullptr) : QTableWidget(10, 10, parent)
{
verticalHeader()->hide();
horizontalHeader()->setHighlightSections(false);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionMode(QAbstractItemView::MultiSelection);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView view;
QGraphicsScene *scene = new QGraphicsScene;
view.setScene(scene);
QGraphicsProxyWidget *proxy = scene->addWidget(new TableWidget);
QGraphicsRectItem *it = new QGraphicsRectItem(QRectF(0, 0, 10, proxy->boundingRect().height()), proxy);
it->setBrush(Qt::green);
SeekBarItem *seekBarItem = new SeekBarItem(QRectF(-5, 0, 20, 50));
seekBarItem->setParentItem(it);
view.resize(640, 480);
view.show();
return a.exec();
}
Update:
#include <QApplication>
#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QTableWidget>
#include <QHeaderView>
#include <QGraphicsProxyWidget>
#include <QScrollBar>
class TableWidget: public QTableWidget
{
public:
TableWidget(QWidget* parent=nullptr) : QTableWidget(10, 10, parent)
{
verticalHeader()->hide();
horizontalHeader()->setHighlightSections(false);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setSelectionMode(QAbstractItemView::MultiSelection);
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
}
};
class SeekBarItem: public QGraphicsRectItem{
public:
SeekBarItem(int width, QAbstractItemView *view, QGraphicsScene *scene)
: QGraphicsRectItem(nullptr),
proxy(new QGraphicsProxyWidget()),
m_view(view)
{
proxy->setWidget(m_view);
scene->addItem(proxy);
setParentItem(proxy);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setBrush(Qt::red);
setRect(0, 0, width, m_view->height());
scrollbar = m_view->horizontalScrollBar();
}
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value){
if(change == QGraphicsItem::ItemPositionChange){
QPointF p = value.toPointF();
qreal max = parentItem()->boundingRect().right()- boundingRect().right();
qreal min = parentItem()->boundingRect().left()-boundingRect().left();
if(p.x() > max) p.setX(max);
else if (p.x() < min) p.setX(min);
p.setY(pos().y());
float percentage = (p.x()-min)*1.0/(max-min);
int value = scrollbar->minimum() + percentage*(scrollbar->maximum() - scrollbar->minimum());
scrollbar->setValue(value);
return p;
}
return QGraphicsRectItem::itemChange(change, value);
}
private:
QGraphicsProxyWidget *proxy;
QAbstractItemView *m_view;
QScrollBar *scrollbar;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView view;
QGraphicsScene *scene = new QGraphicsScene;
view.setScene(scene);
TableWidget *table = new TableWidget;
SeekBarItem *seekBarItem = new SeekBarItem(15, table, scene);
view.resize(640, 480);
view.show();
return a.exec();
}

How can I transform QTabWidget coordinates to its child's coordinates?

I'm trying to generate a right-click menu on a QTabWidget (lists) containing only QListWidgets. I get a menu below where I click the distance of the tab bar's height, which is expected because the context menu is applied to the QTabWidget.
void onCustomContextMenuRequested(const QPoint& pos) {
QListWidgetItem * item = ((QListWidget*)(lists->currentWidget()))->itemAt(pos);
if (item) showContextMenu(item, QListWidget(lists->currentWidget()).viewport()->mapToGlobal(pos));
}
void showContextMenu(QListWidgetItem* item, const QPoint& globalPos) {
QMenu menu;
menu.addAction(item->text());
menu.exec(globalPos);
}
I can get the menu to appear at the mouse, while still referring to an item about 100px beneath it, by changing
QListWidget(lists->currentWidget()).viewport()->mapToGlobal(pos));
to
QListWidget(lists->currentWidget()).viewport()->mapToParent(mapToGlobal(pos)));
But I can't get the menu to refer to the item I am clicking on. I have tried transforming to and from parent coordinates to no effect.
QPoint pos_temp = ((QListWidget*)(lists->currentWidget()))->viewport()->mapFromParent(pos);
if (item) showContextMenu(item, QListWidget(lists->currentWidget()).viewport()->mapToGlobal(pos_temp));
I have also tried to and from global coordinate, and combinations of global and parent, to undesirable effect.
So how can I get the right click menu to refer to the item I am clicking on?
The position sent by the customContextMenuRequested signal is with respect to the widget where the connection is established, and in this case I am assuming that it is the main widget so when using itemAt() of QListWidget it throws inadequate values since this method waits for the position with respect to the viewport(). The approach in these cases is to convert that local position to a global one and then map that global to a local position of the final widget.
In the next part I show an example.
#include <QApplication>
#include <QTabWidget>
#include <QListWidget>
#include <QVBoxLayout>
#include <QMenu>
class Widget: public QWidget{
Q_OBJECT
QTabWidget *lists;
public:
Widget(QWidget *parent=Q_NULLPTR):QWidget(parent){
lists = new QTabWidget;
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &Widget::customContextMenuRequested, this, &Widget::onCustomContextMenuRequested);
auto layout = new QVBoxLayout(this);
layout->addWidget(lists);
for(int i=0; i<4; i++){
auto list = new QListWidget;
lists->addTab(list, QString("tab-%1").arg(i));
for(int j=0; j<10; j++){
list->addItem(QString("item %1-%2").arg(i).arg(j));
}
}
}
private slots:
void onCustomContextMenuRequested(const QPoint& pos){
QPoint globalPos = mapToGlobal(pos);
QListWidget *list = static_cast<QListWidget *>(lists->currentWidget());
if(list){
QPoint p = list->viewport()->mapFromGlobal(globalPos);
QListWidgetItem *item = list->itemAt(p);
if(item)
showContextMenu(item, globalPos);
}
}
void showContextMenu(QListWidgetItem* item, const QPoint& globalPos) {
QMenu menu;
menu.addAction(item->text());
menu.exec(globalPos);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
In the following link is the complete example.

QWidget::heightForWidth() is not called

I want to make my widget always have square size. Following this answer, I have overridden QWidget::heightForWidth(), and I also call setHeightForWidth(true) in the constructor, as suggested by #peppe. The size policy is set to Preferred,Preferred (for both horizontal size and vertical size).
However, heightForWidth() is not being called. Is there anything I am doing wrong?
This is the declaration of heightForWidth() in my Widget class:
virtual int heightForWidth(int) const;
This happens on Linux and Windows.
Your widget needs to be in a layout. The below works on both Qt 4 and 5.
In Qt 4, it will only force the toplevel window's minimum size if it's in a layout.
In Qt 5, it doesn't force the toplevel window size. There may be a flag for that or it's a bug but I don't recall at the moment.
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QDebug>
#include <QVBoxLayout>
#include <QFrame>
class Widget : public QWidget {
mutable int m_ctr;
public:
Widget(QWidget *parent = 0) : QWidget(parent), m_ctr(0) {
QSizePolicy p(sizePolicy());
p.setHeightForWidth(true);
setSizePolicy(p);
}
int heightForWidth(int width) const {
m_ctr ++;
QApplication::postEvent(const_cast<Widget*>(this), new QEvent(QEvent::UpdateRequest));
return qMax(width*2, 100);
}
QSize sizeHint() const {
return QSize(300, heightForWidth(300));
}
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.drawRect(rect().adjusted(0, 0, -1, -1));
p.drawText(rect(), QString("h4w called %1 times").arg(m_ctr));
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QVBoxLayout * l = new QVBoxLayout(&w);
l->addWidget(new Widget);
QFrame * btm = new QFrame;
btm->setFrameShape(QFrame::Panel);
btm->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
l->addWidget(btm);
w.show();
return a.exec();
}
To stay square if the widget is in a layout you must reimplement
bool hasHeightForWidth() const{ return true; }
int heightForWidth(int w) const { return w; }
functions of the layout class.

How to merge two cells of a qtable row?

I have created a table using qtablewidget. I would like to selectively merge cells of some rows. Is there any built-in function for that(sorry, I couldn't find one)? Otherwise, please specify how it can be done. Thanks
You could use the setSpan function of QTableView (which QTableWidget inherits) to achieve something like that for rows and columns.
That is, unless you're looking to really just merge individual cells. I'm not sure that can be achieved with setSpan, but have a look at it anyway.
Try QTableWidget::setSpan(int row, int column, int rowSpan, int columnSpan);
But in some versions it doesn't work.
Sample program
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QTableWidget>
#include <QHeaderView>
class TestWidget: public QWidget
{
Q_OBJECT
private:
QTableWidget *testTable;
QVBoxLayout *mainLayout;
QPushButton *button;
public:
TestWidget(QWidget *parent=nullptr) : QWidget(parent)
{
mainLayout = new QVBoxLayout(this);
testTable = new QTableWidget;
testTable = new QTableWidget(this);
testTable->setObjectName("testTable");
testTable->setFixedWidth(400);
testTable->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Minimum);
testTable->setColumnCount(4);
testTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
testTable->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QTableWidgetItem *itemC1 = new QTableWidgetItem("Column 1");
testTable->setHorizontalHeaderItem(0, itemC1);
QTableWidgetItem *itemC2 = new QTableWidgetItem("Column 2");
testTable->setHorizontalHeaderItem(1, itemC2);
QTableWidgetItem *itemC3 = new QTableWidgetItem("Column 3");
testTable->setHorizontalHeaderItem(2, itemC3);
QTableWidgetItem *itemC4 = new QTableWidgetItem("Column 4");
testTable->setHorizontalHeaderItem(3, itemC4);
testTable->setColumnWidth(0,100);
testTable->setColumnWidth(1,100);
testTable->setColumnWidth(2,100);
testTable->setColumnWidth(3,100);
testTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
testTable->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed);
testTable->setProperty("showDropIndicator", QVariant(false));
testTable->setDragDropOverwriteMode(false);
testTable->setSelectionMode(QAbstractItemView::SingleSelection);
testTable->setSelectionBehavior(QAbstractItemView::SelectRows);
testTable->setShowGrid(true);
testTable->setGridStyle(Qt::SolidLine);
testTable->setSortingEnabled(false);
testTable->setCornerButtonEnabled(false);
testTable->horizontalHeader()->setHighlightSections(false);
testTable->horizontalHeader()->setProperty("showSortIndicator", QVariant(false));
testTable->verticalHeader()->setVisible(false);
testTable->setAlternatingRowColors(true);
button = new QPushButton("Group");
connect(button, SIGNAL(clicked(bool)), this, SLOT(buttonClicked(bool)));
mainLayout->addWidget(testTable);
mainLayout->addWidget(button);
this->setLayout(mainLayout);
}
void addRow(QString c1, QString c2, QString c3, QString c4)
{
int row = testTable->rowCount();
testTable->insertRow(row);
QTableWidgetItem *c1Item = new QTableWidgetItem(c1);
QTableWidgetItem *c2Item = new QTableWidgetItem(c2);
QTableWidgetItem *c3Item = new QTableWidgetItem(c3);
QTableWidgetItem *c4Item = new QTableWidgetItem(c4);
c1Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c2Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c3Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
c4Item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
testTable->setItem(row, 0, c1Item);
testTable->setItem(row, 1, c2Item);
testTable->setItem(row, 2, c3Item);
testTable->setItem(row, 3, c4Item);
}
void updateCell(int rowNumber, int columnNumber, QString textString)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->item(rowNumber, columnNumber)->setText(textString);
}
void groupCells(int rowNumber, int columnNumber, int rowSpan, int columnSpan)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->setSpan(rowNumber,columnNumber,rowSpan,columnSpan);
}
void ungroupCells(int rowNumber, int columnNumber)
{
if(rowNumber>=testTable->rowCount() || columnNumber>=testTable->columnCount()) return;
testTable->setSpan(rowNumber,columnNumber,1,1);
}
public slots:
void buttonClicked(bool event)
{
Q_UNUSED(event)
if(button->text()=="Group")
{
groupCells(1,1,2,1);
button->setText("Ungroup");
}
else
{
ungroupCells(1,1);
button->setText("Group");
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestWidget wid;
wid.addRow("0,0", "0,1", "0,2", "0,3");
wid.addRow("1,0", "1,1", "1,2", "1,3");
wid.addRow("2,0", "2,1", "2,2", "2,3");
wid.addRow("3,0", "3,1", "3,2", "3,3");
wid.show();
return a.exec();
}
#include "main.moc"
Project file shall be downloaded from github