QInputDialog with Multiline text - c++

I need to create a text input dialog with multiple lines. Is there any way to do this using QInputDialog?
If not, is the simplest recommendation to subclass QPlainTextEdit?

QInputDialog and more precisely getText work only with a QLineEdit.
Just implement a small dialog sublass which contains a QPlainTextEdit. Shouldn't be too much work. Not as fast as QInputDialog, but not too much effort either.
Update: since version Qt 5.2, QInputDialog has getMultiLineText.

I was able to do that using QInputDialog and setting QInputDialog::UsePlainTextEditForTextInput.
However, one issue that I found was that QInputDialog was always selecting the text in the dialog (in exec()) and there is no flag to prevent that. The workaround was to connect the change in the selection to a lambda that deselects everything:
auto noteDialog = new QInputDialog(this);
noteDialog->setOptions(QInputDialog::UsePlainTextEditForTextInput);
noteDialog->setWindowTitle("Title");
noteDialog->setLabelText("Notes:");
noteDialog->setTextValue("text");
auto lineEdit = noteDialog->findChild<QPlainTextEdit*>();
connect(lineEdit, &QPlainTextEdit::selectionChanged, [lineEdit](){
if(lineEdit->textCursor().hasSelection())
{
QTextCursor cursor = lineEdit->textCursor();
cursor.clearSelection();
cursor.movePosition(QTextCursor::End);
lineEdit->setTextCursor(cursor);
}
});
bool ok = noteDialog->exec();
if (ok)
doSomething(noteDialog->textValue());
If you are fine with the selected text, QInputDialog::getMultiLineText alone should do the trick.

I needed this to work in Qt4 and this was one of the very few questions I found asking about it, none had any implementations as answers. I've just managed to replicate the functionality and look of Qt5 however, so even though it's in Python, I may as well post here as it could come in useful to others.
To get it working, it needed its own layout, and since QInputDialog builds the layout, QDialog had to be subclassed. I then added getMultiLineText to QInputDialog, which returns a reading from the QPlainTextEdit and the result of the new dialog.
class _QInputDialogMultiline(QDialog):
"""Build a replica interface of QInputDialog.getMultilineText."""
def __init__(self, parent, title, label, text='', **kwargs):
super(_QInputDialogMultiline, self).__init__(parent, **kwargs)
if title is not None:
self.setWindowTitle(title)
self.setLayout(QVBoxLayout())
self.layout().addWidget(QLabel(label))
self.textEdit = QPlainTextEdit()
self.layout().addWidget(self.textEdit)
buttonLayout = QHBoxLayout()
buttonLayout.addStretch()
okButton = QPushButton('OK')
buttonLayout.addWidget(okButton)
cancelButton = QPushButton('Cancel')
buttonLayout.addWidget(cancelButton)
self.layout().addLayout(buttonLayout)
self.textEdit.setPlainText(text)
self.textEdit.selectAll()
okButton.clicked.connect(self.accept)
cancelButton.clicked.connect(self.reject)
class QInputDialog(QInputDialog):
#classmethod
def getMultiLineText(cls, parent, title, label, text='', **kwargs):
dialog = _QInputDialogMultiline(parent, title, label, text, **kwargs)
result = dialog.exec_()
return (str(dialog.textEdit.toPlainText()), bool(result))
Example to show they look the same:
QInputDialog.getMultiLineText(None, 'Window Title', 'This is a label.', 'Initial text')
Qt4 (my code):
Qt5 (standard code):

To retrieve the user's input from QInputDialog::getText() into an expandable string:
bool ok;
std::string* comment = new std::string;
QString qComment = QInputDialog::getText(0,
"<title_of_input_dialog_displayed_at_top>",
"<label_of_input_field_displayed_at_left>",
QLineEdit::Normal, "<enter_this_or_that_here>", &ok);
if (ok && !qComment.isEmpty()) {
QByteArray qba = qComment.toLocal8Bit();
comment->assign(qba.data(), qba.size());
} else if (ok) { // user clicked Ok but did not enter text
comment->assign("<default_user_input>");
} else { // user clicked Cancel
...
}
delete comment;

Related

Pyqt get qlistwidget item when widget inside itemwidget is clicked

this is the current application looks like this:
It has a Qlistwidget listWidget_links where each item has own itemwidget set (combobox, checkbox, button, ...) Now I came upon a problem that neither google or I can solve.
If the user presses the create button or any other item's itemwidget how do I let a method know the item the widget was pressed changed inside.
item_widget.comboBox_type.currentIndexChanged.connect(self.itemupdate_linktype)
item_widget.checkBox_hide.stateChanged.connect(self.itemupdate_hidden)
cbox = self.sender() # gives the widget that released the signal
cbox.parent() #I discovered by a lucky try, returns NodeLinkItemWidgetUI.Ui_Form object which is a item's itemwidget
Here is how items get created to in order to understand the program structure better:
def createNewLink(self, nodename, nodeclass):
item = QtWidgets.QListWidgetItem(self.listWidget_links)
item_widget = NodeLinkItemWidgetUI.Ui_Form(nodename, nodeclass)
item.nodename = nodename
item.nodeclass = nodeclass
item.setSizeHint(QtCore.QSize(130, 160))
self.listWidget_links.addItem(item)
self.listWidget_links.setItemWidget(item, item_widget)
Edit: solved setting the variable item_widget.item = item seems to work, but is there a more elegant way?

QGraphicsScene, QTextEdit and lost focus

QTextEdit and similar widgets embedded in QGraphicsScene lose focus after using standard context menu (copy/paste), i. e. you need to click on QTextEdit again to continue editing. Scene emits focusItemChanged with newFocusItem==0.
First question: Is it a bug or standard behavior?
My investigation shows that function QGraphicsItemPrivate::setVisibleHelper() clears focus here:
if (hasFocus && scene) {
// Hiding the focus item or the closest non-panel ancestor of the focus item
QGraphicsItem *focusItem = scene->focusItem();
bool clear = true;
if (isWidget && !focusItem->isPanel()) {
do {
if (focusItem == q_ptr) {
clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true);
break;
}
} while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel());
}
if (clear)
clearFocusHelper(/* giveFocusToParent = */ false, hiddenByPanel);
}
QGraphisItem has undocumented (internal) flag QGraphicsItem::ItemIsFocusScope. If the flag is set for QTextEdit's proxy-item it gets focus back after menu, but in any case focus cleared at first and after that Item receives it again or not.
Second Question: What is flag QGraphicsItem::ItemIsFocusScope for?
Looks like QGraphicsItem::ItemIsFocusScope is for FocusScope QML item. QtQuick1 is QGraphicsScene based and used that flag.
I'm not sure about side effects but that helps:
auto edit = new QLineEdit();
auto item = scene->addWidget(edit);
item->setFlag(QGraphicsItem::GraphicsItemFlag::ItemIsPanel);
Tested on Qt 5.9, Linux
EDIT
For me looks as bug:
add QLineEdit to scene
click to focus QLineEdit
hit ContextMenu key to show context menu
hit Esc key to exit context menu
try to type
Expected: QLineEdit is focused and text appears
Actual: QLineEdit lost input focus
Please find it or report with Qt bug tracker
So it's OK to have workaround using QGraphicsItem::ItemIsFocusScope flag for example.
#if (QT_VERSION < QT_VERSION_CHECK(<fixed in Qt version>))
// it's workaround of bug QTBUG-...
# if (QT_VERSION == QT_VERSION_CHECK(<version you are develop with>)
item.setFlag(QGraphicsItem::ItemIsFocusScope);
# else
# error("The workaround is not tested on this version of Qt. Please run tests/bug_..._workaround_test")
# endif

Add widget with editable properties through code

I've created a custom widget plugin that is a container( it overloads the qframe). In this container i would like to add children through the code. I've succeeded in doing this. However the children aren't editable or clickable in the designer. I know for children to be editable through the designer, they should be added as code to the XML, but i don't know how to do this.
Would anybody know if it's at all possible to do this?
The code I have, with arrowedFrame as my custom widget plugin class:
arrowedFrame::arrowedFrame(QWidget *parent, Qt::WindowFlags f) : (parent, f)
{
QLabel* testLabel = new QLabel(this);
}
this adds a new Label, as member (sorry i can't yet post pictures, but imagine a box with a label in it). But as i said this label isn't at all editable through the designer.
The solution I found to this is by taking this guide. And adding some things in the initialize function:
void PlotContainerPlugin::initialize(QDesignerFormEditorInterface *formEditor)
{
if (initialized)
return;
QExtensionManager *manager = formEditor->extensionManager();
myFormEditor = formEditor;
Q_ASSERT(manager != 0);
manager->registerExtensions(factory, Q_TYPEID(QDesignerContainerExtension));
initialized = true;
}
The first part just gets a manager:
QExtensionManager *manager = formEditor->extensionManager();
And then we use this manager to,
manager->registerExtensions(factory, Q_TYPEID(QDesignerContainerExtension));
register the plugin(a plotter I my case) with the designer.
Hope this helps:)

QDialog on accept return custom class object

I'm using qt-creator to build a little QT application.
I have a main window where I have some controls like a "new contact" button.
Pressing the button a QDialog is shown, it contains 3 line edits: name, mobile and email.
The dialog is shown through the Signal/Slot system. It works fine but I want to create a Contact object when OK is clicked and I want to give back that Contact to my main window in order to put it in a QList created in the main window code.
The approach is:
QMainWindow -> new contact -> QDialog is shown
QDialog -> ok -> QMainWindow
Should I pass the QList from the main window to the QDialog as argument or there is a best way?
Should I pass the QList from the main window to the QDialog as argument or there is a best way?
In my opinion, best would be a custom QDialog subclass with three QLabels and 3 QLineEdits.
The labels would get the following type of values:
Label 1: name
Label 2: mobileNumber
Label 3: email
You would use then QLabels and QLineEdits to display them with the input coming from the user.
Then, as your program probably already does, just handle the "accept" event respectively. You could use the following method to retrieve the text entered by the end user:
text : QString
This property holds the line edit's text.
Setting this property clears the selection, clears the undo/redo history, moves the cursor to the end of the line and resets the modified property to false. The text is not validated when inserted with setText().
The text is truncated to maxLength() length.
By default, this property contains an empty string.
Then, in the handler of the accepted signal, you could call three accessor methods, like:
QString name() const { return nameLineEdit->text(); }
QString mobileNumber() const { return mobileNumberLineEdit->text(); }
QString email() const { return emailLineEdit->text(); }
You could also store that in a dedicated structure depending on you build up your data representation, so the structure would be something like this:
struct Contact {
QString name;
QString mobileNumber;
QString email;
};
and then you would have the accessor for that as follows
Contact contact() const;
Make a subclass of QDialog. Call Yourclass::exec() to show the dialog (exec is a function in QDialog), then afterwards Yourclass::contactDetails() to get them. contactDetails is a perfectly ordinary member function that you have to write.

Qt: add a file selection field on the form (QLineEdit and "browse" button)

I need to display QLineEdit with "Browse" button at my form. When user clicks button, QFileDialog should be opened, and so on.
This is pretty common thing, but I can't find ready-made solution for that. I expected in Qt Designer some widget like QFileSelect, or something like that, but found nothing similar.
Should I implement it by hand? Or, what is the correct way to do this?
Should I implement it by hand? Or, what is the correct way to do this?
Yes, I agree with you that it is a common thing, but unfortunately you will need to implement this yourself. The good news is that you can do this easily by something like this:
MyMainWindow::createUI()
{
label = new QLabel("foo");
button = new QPushButton("Browse");
connect(button, SIGNAL(clicked()), SLOT(browse()));
layout = new QHorizontalLayout();
layout->addWidget(label);
layout->addWidget(button);
setLayout(layout);
}
void MyMainWindow::browse()
{
QString directory = QFileDialog::getExistingDirectory(this,
tr("Find Files"), QDir::currentPath());
if (!directory.isEmpty()) {
if (directoryComboBox->findText(directory) == -1)
directoryComboBox->addItem(directory);
directoryComboBox->setCurrentIndex(directoryComboBox->findText(directory));
}
}