How to programmatically stop editing QTreeWidget in Qt? - c++

I have a QTreeWidget and two buttons "+" and "-". When I press "+" I want to add new item to QTreeWidget and I want that item to be in edit mode. I managed to do that with following code (it gets called every time "+" is pressed):
// QTreeWidgetItem* lastItem = getLastItem();
// if (lastItem) { widget->closePersistentEditor(lastItem); }
QTreeWidgetItem* item = new QTreeWidgetItem(widget, {"100000"});
item->setFlags(item->flags() | Qt::ItemIsEditable);
widget->addTopLevelItem(item);
widget->editItem(item);
Problem is when I try to add a new item, but don't exit edit mode before adding (press Enter or something). I get error edit: editing failed and new item is added below current item (which is still in edit mode).
What I would like is that current item exists edit mode and that newly added item becomes focused and enters edit mode.
I tried to do that with first getting the last item in a QTreeWidget and calling closePersistentEditor(lastItem) (commented code) and then creating and adding new item, but it didn't work. So, how to close currently opened edit on item?
EDIT:
Ok, I have added additional code with minimal example. Only thing you have to do to build it is to add QTreeWidget and QPushButton to the form mainwindow.ui and connect that button to on_btnAdd_clicked():
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTreeWidget>
#include <QTreeWidgetItem>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnAdd_clicked()
{
QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget, {"100000"});
item->setFlags(item->flags() | Qt::ItemIsEditable);
ui->treeWidget->addTopLevelItem(item);
ui->treeWidget->editItem(item);
}
EDIT2: This is happening on macOS (Mojave) with Qt 5.12.

Ok, it looks like this is a bug in Qt for macOS. Workaround that I did is following:
QTreeWidgetItem* lastItem = getLastTreeWidgetItem(widget);
if (lastItem) {
widget->setDisabled(true);
widget->setDisabled(false);
}
conversation->setFlags(conversation->flags() | Qt::ItemIsEditable);
getLastTreeWidget() is my own method that returns last added item in a QTreeWidget. Now every time when I press button to add new item, previous gets deselected and newly added enters edit mode.

Related

How to get the text of a Qcomplete in a Qcombobox

I have a combobox that I fill with a QList and now I have generated a search engine with the help of the QCompleter, so far everything is fine, but I have edited it to make it visually more attractive, and it worked correctly until I have trouble selecting with the mouse one of the options that the QCompleter threw at me and the selection does not respect me, it only respects me if I give an Enter with the keyboard, but only for a moment.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qApp->setStyleSheet("QAbstractItemView#completerPopup {font: 15pt ;color: rgb(255, 255, 255);}");
}
QCompleter *completer = new QCompleter(list, this);
completer->setFilterMode(Qt::MatchContains);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->popup()->setObjectName("completerPopup");
ui->cobobox->setCompleter(completer);
I manage to make it work but I have to remove some lines of code that are the ones that help me edit the QCompleter as Pupop.
QCompleter *completer = new QCompleter(list, this);
completer->setFilterMode(Qt::MatchContains);
completer->setCaseSensitivity(Qt::CaseInsensitive);
ui->cobobox->setCompleter(completer);
The error it throws when I select is the following
Setting a QCompleter on non-editable QComboBox is not allowed.
In the qtcreator form designer select your combobox and in properties window mark editable checkbox.

QObject::findChild returns 0 for QLabels added to statusbar

I created a application running in a QMainWindow using qtcreator, so the typical way.
I added two 'manually' (meaning: not with the Form editor) created qlabels to the statusbar:
in the header:
QLabel *label_timestamp;
QLabel *contentLabel_timestamp;
in the constructor:
MainWin::MainWin(const CmdLineOptions &opts, QWidget *parent)
: QMainWindow(parent),
ui(new Ui::MainWin),
m_connectionStatusLabel(new QLabel),
m_client(new QMqttClient),
m_mqttmanager(new MQTTManager(m_client)),
m_mqttServerName("localhost")
{
ui->setupUi(this);
label_timestamp = new QLabel(this);
contentLabel_timestamp = new QLabel(this);
label_timestamp->setText("system time");
contentLabel_timestamp->setText("dd.mm.yyyy, hh:mm:ss:zzz"); /* just testing output */
statusBar()->addPermanentWidget(label_timestamp);
statusBar()->addPermanentWidget(contentLabel_timestamp);
}
If I do a
Label *label = findChild<QLabel *>(QString("contentLabel_")+objName);
elsewhere in this class implementation with objName being 'timestamp', of course, findChild() returns 0. It's working fine with other QLabels created using QtCreator in the form editor, findChild() finds them all. Isn't the statusbar widget and its content also a child of ui? Does somebody eventually know a way out of there?
I want to use findChild to generically fill my labels following a naming scheme with content I receive over MQTT, this is the background. Would be great if the statusbar content would need a special handling but could also be handled in this dynamic approach.
Thanks a lot
findChild uses the objectName, in the case of Qt Creator this establishes it in the MOC, but in your case you must establish it:
label_timestamp = new QLabel(this);
contentLabel_timestamp->setObjectName("label_timestamp");
contentLabel_timestamp = new QLabel(this);
contentLabel_timestamp->setObjectName("contentLabel_timestamp");
And then you can recover it with:
QLabel *label_1 = findChild<QLabel *>("label_timestamp");
if(label_1){
// some code
}
QLabel *label_2 = findChild<QLabel *>("contentLabel_timestamp");
if(label_2){
// some code
}

QAbstractItemView Tab Focus While Editing Item

I have a QTreeView populated with items from a model. When a call to edit() is made on an index, a custom editor displays. The editor consists of two QLineEdit widgets.
I want the focus to switch between the two QLineEdit widgets when Tab is pressed. However, pressing Tab cycles through everything else on my program. All my QPushButton and QTabWidget objects are included in the Tab order even though they are completely different widgets than my editor.
I've tried setting the tab order using setTabOrder() to loop it between the two QLineEdit widgets, however this still doesn't isolate the editor widget from the surrounding widgets. Why is this happening?
NOTE: I'm not trying to disable tab ordering anywhere else, just isolate it to my editor for the time being.
Thanks for your time!
This can be easily implemented using QWidget::focusNextPrevChild as follows:
class EditWidget : public QWidget
{
public:
EditWidget(QWidget *pParent) : QWidget(pParent)
{
QHBoxLayout *pLayout = new QHBoxLayout(this);
setLayout(pLayout);
pLayout->addWidget(m_pEdit1 = new QLineEdit ());
pLayout->addWidget(m_pEdit2 = new QLineEdit ());
}
bool focusNextPrevChild(bool next)
{
if (m_pEdit2->hasFocus())
m_pEdit1->setFocus();
else
m_pEdit2->setFocus();
return true; // prevent further actions (i.e. consume the (tab) event)
}
protected:
QLineEdit *m_pEdit1;
QLineEdit *m_pEdit2;
};

QT Making a Dropdown Menu Widget Programmatically

I am using QT 5.5 with Mac OSX. I want to make several dropdown menu widgets programmatically that will have various options that can change the values of certain variables.
So for example, I would have dropdown menu 1 represent variable "command" have:
- Q
- W
- E
- R
And by selecting whichever one, then it would make command = Q, or command = W. So that way, I can send command to another program knowing that I sent Q, or W.
My current mainwindow looks something like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
//******* Set up
ui->setupUi(this);
ui->centralWidget->setLayout(new QVBoxLayout);
// 01: Creation of Console
console = new Console;
console->setEnabled(false);
/************** Adding Widgets *********************/
//creation and attribution of slider
slider = new QSlider();
slider->resize(255, 20);
slider->setOrientation(Qt::Horizontal);
slider->setRange(0, 255); //0-255 is range we can read
//creation and attribution of the lcd
lcd = new QLCDNumber();
lcd->setSegmentStyle(QLCDNumber::Flat);
lcd->resize(255, 50);
//03: Adding widgets to layout
//add console as a widget to the main widget
//layout with slider and lcd underneath console
ui->centralWidget->layout()->addWidget(console);
ui->centralWidget->layout()->addWidget(slider);
ui->centralWidget->layout()->addWidget(lcd);
////////I WANT TO ADD VARIOUS DROPDOWN MENUS HERE NEXT TO EACH OTHER////////
/************** Connection Events ***********************/
....
}
Assuming you want a ComboBox, Here is how you can do it:
QStringList commands = { "Q", "W", "E", "R" };
QComboBox* combo = new QComboBox(this);
combo->addItems(commands);
connect( combo, &QComboBox::currentTextChanged, this, &MainWindow::commandChanged);
Now you will get the command text when user changes the combo box item. and you can write your code based on that.
MainWindow::commandChanged(const QString& command_text)
{
//Do the logic based on command_text
}
Another option if you want to choose combobox item texts differently is that you set itemData for combobox items. and get them in your slot by currentData property of the ComboBox.

How to change contents of QMainWindow dynamically

I have a QMainWindow that starts out with nothing but a menubar with a menu that has two options. When the first is clicked the window should be populated with QLabels and various input widgets to recieve data. When the second option is clicked the window should be populated with a QTextEdit(obviously removing whatever was on the window at the time)
The following is code I have tried :
void OrderWindow::displayAddOrder(){
QVBoxLayout* tlayout = new QVBoxLayout();
QHBoxLayout* row = new QHBoxLayout();
row->addWidget(nameLbl);
tlayout->addLayout(row);
qDeleteAll(children());
delete layout();
setLayout(tlayout);
}
It's a bit messy since I've been trying various things. When I click on a menu option with this code it simply says the application has stopped working.
Any help would be appreciated.
You have at least the following options:
Always show the actual widget, and hide the rest. This is simple in case of two widgets like in your example. You could use this technique with the observer design pattern for any number of widgets.
Use the QStackedWidget class which basically behaves the way as your custom observer pattern implementation would be, although you will need to use an extra class for this.
Therefore, I would suggest to write the following code:
orderwindow.h
...
class QStackedWidget;
class OrderWindow
{
...
public:
explicit OrderedWindow(QWidget *parent);
...
private:
QStackedWidget m_stackedWidget;
...
}
...
orderwindow.cpp
#include "orderwindow.h"
#include <QStackedWidget>
...
OrderWindow::OrderWindow(QWidget *parent)
: QWidget(parent)
, m_stackedWidget(new QStackedWidget(this))
{
QWidget *firstPageWidget = new QWidget;
QWidget *secondPageWidget = new QWidget;
m_stackedWidget->addWidget(firstPageWidget);
m_stackedWidget->addWidget(secondPageWidget);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
setLayout(layout);
}
...
void OrderWindow::displayAddOrder() {
m_stackedWidget->setCurrentWidget(nameLbl);
}
...
you can use a QStackedWidget
start with showing an empty page and then show the correct page as needed:
there is then no need to mess with adding or removing widgets
Yes, you can use a QStakedWidget if your input options are fixed. If it's not, I suggest you to use an abstract factory pattern to create the stacked widget content. This woluld make your code more readable.