How to deselect selected cell in QTableWidget by clicking on it again? - qt

I am trying to deselect a selected cell in QTableWidget by clicking on it again. I don't know if I missed an option or a signal in documentation (I hope not). I tried with signals cellClicked and cellActivated. None of them work. Thing is, if a cell is in a deselected state and I click on it, by the time signal cellClicked is emitted, cell already has selection. So I cannot check for selection in a slot that reacts to that signal.
So how can I deselect a selected cell? Selection mode is SingleSelection. I just hope I don't have to subclass QTableWidget.

I've only performed minimal testing but the following appears to do what you want...
#include <iostream>
#include <QApplication>
#include <QTableWidget>
int main (int argc, char **argv)
{
QApplication app(argc, argv);
QTableWidget tw(5, 10);
/*
* Disable any normal selection mode.
*/
tw.setSelectionMode(QAbstractItemView::NoSelection);
QObject::connect(&tw, &QTableWidget::cellClicked,
[&tw](int row, int col)
{
/*
* Make sure we have an item in the cell.
*/
auto *item = tw.itemAt(row, col);
if (!item) {
item = new QTableWidgetItem(QString("cell[%1, %2]").arg(row).arg(col));
tw.setItem(row, col, item);
}
/*
* Update selection based on current state.
*/
bool was_selected = item->isSelected();
tw.selectionModel()->clear();
item->setSelected(!was_selected);
});
tw.show();
return app.exec();
}

Related

Tooltips (or other action) in QFileDialog

I want to have a Tooltip (or ideally, a QWidget) pop-up when hovering over a file in a QFileDialog::getOpenFileName instance.
Is there a way to do this without subclassing the class?
I'm not sure there's any approved/definitive way of doing this. The following is a rather hacky way of achieving what (I think) you want but which makes certain assumptions regarding the widget hierarchy associated with a QFileDialog instance. Specifically, it relies on the assumption that the widget hierarchy subtended by a QFileDialog instance will contain one or more QAbstractItemView instances...
#include <iostream>
#include <QAbstractItemView>
#include <QApplication>
#include <QCursor>
#include <QFileDialog>
#include <QToolTip>
int
main (int argc, char **argv)
{
QApplication app(argc, argv);
QFileDialog fd;
/*
* Further to the comments by #Parisa.H.R, we need to make sure we use a
* non-native file dialog here otherwise there's no way to get the desired
* behaviour.
*/
fd.setOption(QFileDialog::DontUseNativeDialog);
/*
* Search the widget hierarchy under the QFileDialog looking for instances of
* QAbstractItemView or derived classes.
*/
for (auto *v: fd.findChildren<QAbstractItemView *>()) {
std::clog << "view = " << v << "(type=" << v->metaObject()->className()
<< ", name=\"" << v->objectName().toStdString() << "\")\n";
/*
* Connect the view's entered signal to a lambda which, for the time being,
* simply displays a tooltip showing the name of the filesystem item.
*/
QObject::connect(v, &QAbstractItemView::entered,
[](const QModelIndex &index)
{
QToolTip::showText(QCursor::pos(), index.data(Qt::DisplayRole).toString());
});
/*
* In order to receive the QAbstractItemView::entered signal mouse tracking
* must be enabled for the view.
*/
v->setMouseTracking(true);
}
fd.exec();
}

How to change the font of the qcombobox label/header only?

When I change the font of my QComboBox comboBox->setFont(whateverQFont); it is applied on the dropdown menu as well (all the items), and it overrides the Qt::FontRole data I have set on my items with comboBox->setItemData(index, itemSpecificFont, Qt::FontRole);
I'd like to set a font on the QComboBox label only and leave the dropdown displayed as it was. Or even better : to have directly the same font as the selected item.
Is there an easy way to do that ?
Edit: Solution of Jasonhan works fine for an editable QComboBox (-> setting the font on the QLineEdit) but is not applicable for a regular QComboBox, as the QLabel is private.
Before starting implement a custom model you could try with QListView.
It just applies to the drop-down menu and you can change its font with the usual setFont function; the you have to apply it to your QComboBox thorugh routine setView.
Something like this (it's not Qt C++ code, I've skipped all arguments in function calls):
QComboBox *combobox = new QComboBox();
combobox->setFont();
...
QListView *listview = new QListView();
listview->setFont();
combobox->setView(listview);
After 2 years, I saw this question. I don't know whether you have found a better method or not. If not, following code may give you a hint.
QComboBox label as you said is actually a QLineEdit, so you just need to set this component's font, and it will solve your problem.
QComboBox *box = new QComboBox();
//add some list items to box
if (box->lineEdit())
box->lineEdit()->setFont(font);//font is your desirable font
Something that works for a non-editable QComboBox is to install a QProxyStyle that sets the font when a CE_ComboBoxLabel control element is drawn.
Here's an example that sets the label font to italic:
#include <QApplication>
#include <QProxyStyle>
#include <QPainter>
#include <QComboBox>
class MyProxyStyle : public QProxyStyle
{
public:
void drawControl(QStyle::ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget = nullptr) const override
{
if (element == QStyle::CE_ComboBoxLabel)
{
auto fnt = painter->font();
fnt.setItalic(true);
painter->setFont(fnt);
}
QProxyStyle::drawControl(element, option, painter, widget);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setStyle(new MyProxyStyle);
QComboBox cb;
cb.addItem("Option 1");
cb.addItem("Option 2");
cb.addItem("Option 3");
cb.show();
app.exec();
}

How to continue text editing after formatting changes of QGraphicsTextItem?

I am trying to make changes (font changes) to a QGraphicsTextItem that is editable.
I am trying to change formatting of fragments of text, or the formatting applied at typing point (if I set text bold, the text I type after that action at cursor position would be bold).
Setting formatting for text fragments works - but I can't find a way to return the focus correctly to the item.
I can show the caret at the right position, but I can't type in the box unless I actually click in box (even though it seems hat I should be able to).
Simple sample (for some reason it crashes on closing program but I don't care about that since I am testing the text class, not the main program):
header: mytextitem.h
#include <QGraphicsTextItem>
class MyTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
MyTextItem();
~MyTextItem() {}
public slots:
void setItemBold(const int b);
};
mytextitem.cpp
#include "mytextitem.h"
#include <QTextCursor>
MyTextItem::MyTextItem()
{
setPlainText("ABCD");
setFont(QFont("Arial", 20));
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
setTextInteractionFlags(Qt::TextEditorInteraction);
}
void MyTextItem::setItemBold(const int b)
{
int _weight = (b != 0) ? QFont::Bold : QFont::Normal;
QTextCursor _cursor = textCursor();
//int p = _cursor.position(); // this won't help
QTextCharFormat _format = _cursor.charFormat();
_format.setFontWeight(_weight);
_cursor.setCharFormat(_format);
//_cursor.setPosition(p, QTextCursor::KeepAnchor); // makes no difference on allowing me to type, but I can make the cursor move
//_cursor.movePosition(QTextCursor::NoMove, QTextCursor::KeepAnchor, 0); // makes no difference but I just thought some action might
setTextCursor(_cursor);
setFocus(Qt::MouseFocusReason);
// grabKeyboard(); // does nothing
}
main.cpp
#include <QApplication>
#include <QGraphicsView>
#include <QGridLayout>
#include <QtWidgets>
#include <QCheckBox>
#include "mytextitem.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene(-20, -20, 150, 100);
QGraphicsView view(&scene);
QWidget widget;
QGridLayout layout(&widget);
layout.addWidget(&view, 0, 0);
QCheckBox bold("Bold");
layout.addWidget(&bold, 0, 1);
MyTextItem* item = new MyTextItem();
scene.addItem(item);
QObject::connect(&bold, SIGNAL(stateChanged(int)), item, SLOT(setItemBold(int)));
view.ensureVisible(scene.sceneRect());
widget.show();
return a.exec();
}
Editing the item is possible only if clicking in the box.
Assuming that I am already in the box (editing), and I push the "Bold" checkbox, I expect to be able to continue editing - type in the box - but even though I try to
set focus (which places the blinking text cursor in the box),
set position for cursor (I can move it, or select things... that works but I want to keep current position and selection)
grab keyboard - seems to do nothing
nothing seems to return me to the box so I continue typing (with the new font setting).
How can I get the QTextCursor or anything else to allow me to keep editing the text ?
You need to focus on QGraphicsView after format change. You can't focus on QGraphicsTextItem because it isn't QWidget.

How can I get my caret to show up on first double-click action on a QGraphicsTextItem?

I am struggling to get a QGraphicsTextItem to work as a user friendly object.
Since it is very hard to move while being editable, I start it as not editable, and make it editable on double-click. Then turn editing off on losing focus.
My problem is, the caret does not show up on first edit.
I have tried getting the position based on mouse position (as in this question that was trying to solve a different problem), or calling the QGraphicsTextItem::mouseDoubleClickEvent(event);
No matter what I try, the caret is invisible on first action - until I start typing (or if I focus out and back in) - even though it is at the correct location.
After typing, or unselecting and reselecting , the caret shows up in normal location every time.
I have tried to call the QTextCursor in the item constructor, setting its position at 0, made no difference.
What made a difference : one of the 2 situations (neither of which I can do though):
a) start item with Qt::TextEditorInteraction in constructor
b) start item with no moving/focus/selectable flags
I can't do either - because my default state of item must be movable, and that interferes with text editing (as explained at start).
I have tried to disable those flags during editing though... with no effect.
Here is a simple code to demonstrate the problem, I hope somebody can have an idea.
mytextitem.h
#ifndef TEXTITEM_H
#define TEXTITEM_H
#include <QGraphicsTextItem>
class MyTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
MyTextItem();
protected:
virtual void focusOutEvent (QFocusEvent * event);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event);
};
#endif // TEXTITEM_H
mytextitem.cpp
#include "mytextitem.h"
#include <QTextCursor>
#include <QAbstractTextDocumentLayout>
#include <QGraphicsSceneMouseEvent>
#include <QFont>
MyTextItem::MyTextItem()
{
setHtml("ABCD");
setFont(QFont("Arial", 50));
setTextInteractionFlags(Qt::NoTextInteraction);
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
}
void MyTextItem::focusOutEvent(QFocusEvent *event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::NoTextInteraction);
QTextCursor _cursor = textCursor();
_cursor.clearSelection();
setTextCursor(_cursor);
}
void MyTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
setTextInteractionFlags(Qt::TextEditorInteraction);
QGraphicsTextItem::mouseDoubleClickEvent(event); // or the version in linked question
}
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene s;
QGraphicsView view(&s);
s.setSceneRect(-20, -100, 800, 600);
view.show();
MyTextItem* t = new MyTextItem();
s.addItem(t);
return app.exec();
}
I have also considered editing text - not sure if that would work but I think it would affect the undo stack which I will have to deal with soon....
How can I get my caret to show up on first double-click action on the text item ?
(As a user, not seeing a caret would make me uncertain if I can type... even though it works... I would not have confidence in the object if I do not have feedback of my action. That's why I care about this problem.)
I can't explain it... after trying EVERYTHING to get the caret to show, the solution was so simple:
I had to change the order of flags being set, in constructor.
The QGraphicsTextItem flag must be set AFTER setting the QGraphicsItem flags.
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
setTextInteractionFlags(Qt::NoTextInteraction);

problem with replacing a background image using signal / slot in qt

i want to make a simple chess program. So far i've made the board using QTableWidget and loaded the piece pictures in the cells of table. Now i wnat to use signal and slot so that when user clicks a cell and then click another cell the piece picture from first cell goes to second cell, But I don't know how to do it.
Note that i don't want these "piece moves" obey the real chess rules. I only wnat to do the picture replacement between two cells . Later i will make them obey the rules.
here is the code. in this code only table's item(0,0) has a picture. can anyone say how to write a code so that when i click that item then click to item(1,1) , picture "1.bmp" goes to background of item(1,1)?
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QHBoxLayout>
#include <QTableWidget>
#include <QHeaderView>
class Table : public QWidget
{
Q_OBJECT
public:
Table(QWidget *parent = 0);
slots:
//??????
};
Table::Table(QWidget *parent)
: QWidget(parent)
{
QHBoxLayout *hbox = new QHBoxLayout(this);
QTableWidget *table = new QTableWidget(8 , 8 , this);
table->setFixedSize(900,900);
table->horizontalHeader()->setDefaultSectionSize(100);
table->verticalHeader()->setDefaultSectionSize(100);
table->horizontalHeader()->setResizeMode(QHeaderView::Fixed);
table->verticalHeader()->setResizeMode(QHeaderView::Fixed);
QString fileName = "/1.bmp";
QPixmap pic(fileName);
QBrush brush(pic);
QTableWidgetItem* item = new QTableWidgetItem();
item->setBackground(brush);
table->setItem(0,0,item);
hbox->addWidget(table);
setLayout(hbox);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Table table;
table.show();
return app.exec();
}
There are really two questions here.
The first one regarding signals/slots with QTableWidgetItem, and the second regarding handling mouse clicks on the QTableWidget.
Signals Slots on a QTableWidgetItem
note: I don't recommend doing it this way, read all the way to the bottom*
Using signals and slots requires that the object that emits the signal, have the signal defined in the class definition. Likewise the object that receives a slot, must have that slot declared in the class definition.
You'll notice (on the Qt docs) that QTableWidgetItem doesn't have a signals or slots to set/remove the background brush you are using to draw your picture. So, you will have to subclass QTableWidgetItem, and provide this signals/slots yourself.
Example:
class ChessItem : public QTableWidgetItem
{
// constructor / destructor
// other methods
public slots:
void slotChangeBackground( const QBrush & brush )
{
setBackground( brush );
}
};
Handling Mouse Clicks on the QTableWidget
edit: I removed the event handling paragraph, because using QTableWidget's builtin signals is easier
QTableWidget offers the cell clicked signal:
void QTableWidget::cellClicked ( int row, int column )
So in your Table class add a slot, then connect it the cellClicked signal to it:
// in your Table's constructor:
connect( table, SIGNAL( cellClicked(int, int) ), this, SLOT( slotCellClicked(int,int) ) ) );
// elsewhere...
void slotCellClicked(int row, int column) {
// handle mouse clicking here
}
The problem as I see it is you don't want to just connect any ol signal to slotChangeBackground, because that would change every background. So I suggest not using signals/slots for changing the background, and instead use QTableWidget::itemAt ( int ax, int ay ) in your slotCellClicked(x,y) to retrieve the item at a coordinate, then call setBackground on it.

Resources