title pretty much says it all. I have a read-only text box on a form where users can edit the contents of this text box through buttons on the form. The form is basically a keypad. As users click the buttons, a digit will be added to the value in the text box.
Technically, the final application will be running on a machine with no keyboard but a touchscreen. Users interact with the application using the touchscreen and they should not be installing keyboards on the machine but in the event they do, I am making the text box read-only.
Now, how can I have the text box's cursor still blink even though it is read only?
I am wondering if I need to do something similar to this user's solution:
Hide QLineEdit blinking cursor
I have also tried using the setFocus method and I am looking into style sheets. However, nothing has panned out.
Other answers have given you technical solutions to your question. However, I think that you are going in a wrong direction. You want a QLineEdit that is read only, but with a cursor and still accepts input from a virtual keyboard... yeah, so it is not really read only... It is not smelling good.
And in general, arbitrarily and actively disabling standard functions is not a good idea. Especially, if it means hacking your way around standard widget behaviors an semantics to do it.
Let's think from the start. What is the issue of accepting input from a keyboard?
From your question I would dare to guess that you want to make sure that the QLineEdit only accepts digits, and forbid the user to input other characters.
If I am right what you want is a QValidator, either a QIntvalidator or a QRegExpValidator. Then you can let the users use a keyboard, but they will only be able to input digits, like they would with your virtual keyboard.
Create a class whiwh inherits from QLineEdit and ignore the key events (events triggered when the user press a key). It will make your line edit read only but without the look-and-feel:
class LineEdit: public QLineEdit
{
Q_OBJECT
public:
LineEdit(QWidget* parent=nullptr): QLineEdit(parent)
{
}
virtual void keyPressEvent(QKeyEvent* event)
{
event->ignore();
}
public slots:
void add(QString const& textToAdd)
{
setText(text() + textToAdd);
}
};
An usage example (the timer simulates the virtual keyboard):
LineEdit* line = new LineEdit;
line->show();
QTimer timer;
timer.setInterval(2000);
QObject::connect(&timer, &QTimer::timeout, [=]() { line->add("a"); });
timer.start();
Romha Korev's answer will appear to work, but it won't catch everything. It can still be possible to paste or drag&drop text into the line edit, or as a result of a locale dependent input-method keyboard event. I don't know all the various ways text can end up being entered into the line edit that way. You'd be hunting for holes to plug.
So I propose to abuse a QValidator for this. Do not set your line edit to read-only mode. Create your own validator that blocks all input unless you specifically disable it:
class InputBlockerValidator final: public QValidator
{
Q_OBJECT
public:
void enable()
{ is_active_ = true; }
void disable()
{ is_active_ = false; }
QValidator::State validate(QString& /*input*/, int& /*pos*/) const override
{
if (is_active_) {
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
private:
bool is_active_ = true;
};
Now set an instance of this as the validator of your line edit:
// ...
private:
QLineEdit lineedit_;
InputBlockerValidator validator_;
// ...
lineedit_.setValidator(&validator_);
Then, whenever you insert text into the line edit, disable and re-enable the validator:
validator_.disable();
lineedit_.insert(text_to_be_inserted);
validator_.enable();
Do not ever call setText() on the line edit. For some reason, this permanently prevents the validator from blocking input. I don't know if this is intended or a Qt bug. Only use insert(). To simulate setText(), use clear() followed by insert().
Related
I have some labels and layouts nested inside a QWidget to build a part of a sidebar. Each QWidget is its own section and one component currently looks like this:
To my understanding, you can only set hyperlinks with QLabel, but I'm trying to get the whole area between the white lines clickable. This is including the icon and the whitespace. Is there any way to achieve this?
This got marked as a duplicate to the opposite of what I was asking, so I'd like to reiterate that I'm trying to implement a hyperlink without QLabel.
You can easily have a widget open a link on click:
class Link : public QWidget {
Q_OBJECT
public:
Link(QUrl url, QWidget p = nullptr) : QWidget(p), _url(url) {}
QUrl _url;
void mouseReleaseEvent(QMouseEvent *) { QDesktopServices::openUrl(_url); }
}
You can avoid any extra signals and connections, and have each link widget store its own link internally, the url can be set on construction and changed at any time. Not using signals and slots makes it easier to change the link too, without having to disconnect previous connections.
IMO going for a signals and slots solution is only justified when you want different arbitrary behavior. In this case you always want the same - to open a particular link, so you might as well hardcode that and go for an easier and more computationally efficient solution.
I would just manually catch the SIGNAL for clicked() and use desktop services to open the url in code.
bool QDesktopServices::openUrl ( const QUrl & url ) [static]
Opens the given url in the appropriate Web browser for the user's desktop environment, and returns true if successful; otherwise returns false.
http://doc.qt.io/qt-4.8/signalsandslots.html
Using this type of syntax, or in the designer, you can also connect a signal to a slot.
connect(widgetThatRepresentsURL, SIGNAL(clicked()),
handlerThatWillOpenTheURL, SLOT(clicked_on_url()));
For widgets that don't have a signal set up for clicked (or whatever event you are interested in), you can subclass the widget in question and reimplement...
void QWidget::mousePressEvent ( QMouseEvent * event ) [virtual protected]
Specifically for creating a signal, there is emit. I've used this in the past like the following
void Cell::focusInEvent(QFocusEvent *e)
{
emit focus(this, true);
QLineEdit::focusInEvent(e);
}
with the following in the header
signals:
void focus(Cell *, bool);
My problem is that QTextBrowser can not show all data that is appended to it.
I am using qt5.4 on windows.
My class like this:
class InfoTextBrowser : public QTextBrowser
{
Q_OBJECT
public:
InfoTextBrowser(QObject *parent);
~InfoTextBrowser();
public slots:
void appendText(const QString& text) {
try
{
this->append(text);
}
catch(std::bad_alloc& e)
{
if(!memoryError)
{
QMessageBox::warning(this,"My app", QStringLiteral("Out of Memory"),QMessageBox::Ok);
this->clear();
memoryError = true;
}
}
QScrollBar* myBar = this->verticalScrollBar();
if (myBar!=NULL)
myBar->setValue(myBar->maximum());
};
private:
void contextMenuEvent(QContextMenuEvent *event);
private:
bool memoryError;
};
First I got bad_alloc exception and then I moved my application to 64bit then I do not get any exception. But When I run my program, QTextBrowser stops showing data after a while. It dies and it can not be cleared or refreshed.
I also tried QPlainTextEdit instead of this, but I could not be successful again. I want to show all data at run time or some part of it that could be shown. Because it stops just first paragraph when it dies. In addition, I do not want to show data by reading from a text file.
Is there anyone face with this situation?
Thanks in advance.
Actually, I decided that there is no feasible solution for this, you can not hold this data at run time, It very depends on hardware. You do not have a guarantee to find the needed memory as a whole block.
So I used QPlainTextEdit and restricted to shown text as 100000 blocks by using setMaximumBlockCount(100000)function in the constructor. I appended text with this->appendPlainText(text). Thus,output windows always shows last 100000 block of text. Rest of data can hold a text file or something like that. So user can look all of them from there.
I have a fairly complex dialog which the inputs are numbers with different allowed ranges. I was wondering what is the cleanest pattern to guarantee that my QLineEdits have correct input values.
The obvious way of doing this seems to check input values when the user clicks the OK button. Problem I have is that some of the GUI controls depend on the value of other inputs. So the code seems to be getting a bit nasty by having me to branch the logic of the controls for all the cases where a input has a wrong value.
Is there a nice pattern for this type of situation?
I was thinking about subclassing QLineEdit and using the focusOutEvent to check for the input of the dialogs. If the input is incorrect I would default the value and trigger the logic. This would guarantee that each lineedit is responsible for it's own validation. Is there an obvious pitfall by doing this?
QValidators are awesome, problem is when their state is intermediate.
Use the signals provided by QLineEdit and build a small validation class of slots. It'll be easier than subclassing them directly, and allow you more fine grained control.
You may very well work with subclassing QLineEdit, since it is quite easy by just setting up a connection to the appropriate signals.
class foo : public QLineEdit
{
Q_OBJECT
// ... staff here
private:
void signal_control()
{
connect(this,SIGNAL(textChanged(const QString & )),this, SLOT(text_validate(const QString & )));
private slots:
void text_validate(const QString &)
{
// validate your text here
}
};
You may also build a different class that just listens to signals generated from your QLineEdit object and validates separately. Befriending it may be a good idea.
In Qt you can define the tab order by using the Qt Designer or by using C++. The relationships between widgets are set relatively to each other, so there is no index or such thing. What I want right now is to "break" the circular chain of widgets so that I get a beginning and an end of the chain.
A circular tab order would be:
A - B
| |
D - C
I want (note missing link between A and D):
A - B
|
D - C
which is more like a line instead of a circle:
A - B - C - D
So the user "stops" at one end and has to go back using the other direction.
Update: I have another idea now. What if i reimplement:
bool QWidget::focusNextPrevChild(bool next)
According to the documentation one can use this to implement custom focus behavior.
In my dynamic scenario where buttons in the GUI are adjusted at run-time I will have to overload the function and set, for example, an internal flag allowFocusNext and allowFocusPrev which then ignores the focus request if necessary. I will report back here, when I have tried it. Meanwhile any comments are welcome!? :-)
I found a solution, but it is a bit hacky. The QWidget::setTabOrder will not allow to chain a widget with itself, so this approach won't help (even if you are using focus proxies)
However, you can define a "Focus Forwarder":
class FocusForwarder : public QWidget
{
public:
explicit FocusForwarder(QWidget *proxy) :
QWidget((QWidget *) proxy->parent()),
m_proxy(proxy)
{
setFocusPolicy(Qt::TabFocus);
}
protected:
void focusInEvent(QFocusEvent *) {
m_proxy->setFocus();
}
private:
QWidget *m_proxy;
};
And add them at the beginning and end of you chain:
FocusForwarder *w1 = new FocusForwarder(ui->bA);
FocusForwarder *w2 = new FocusForwarder(ui->bD);
QWidget::setTabOrder(w1, ui->bA);
QWidget::setTabOrder(ui->bA, ui->bB);
QWidget::setTabOrder(ui->bB, ui->bC);
QWidget::setTabOrder(ui->bC, ui->bD);
QWidget::setTabOrder(ui->bD, w2);
Details
For setTabOrder to work, the widgets must be in the same window. To ensure this, the Forwarder is placed in the proxy's parent (in the initializer list).
For this mechanism, the focus direction (Tab or Shit+Tab) does not matter. As soon as a FocusFowarder receives the focus, it will "forward" it to its proxy.
The direction is handled by Qt internally. You just add "sentinels" around your chain.
Use in QtDesigner
When you want to use it in QtDesigner, you'd create a Widget and promote it to the forwarder. As you cannot set the proxy directly, you could add a dynamic property for the proxy's name, like this:
class FocusForwarderDesigner : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString proxyName READ proxyName WRITE setProxyName)
public:
QString proxyName() {
return (m_proxy) ? m_proxy->objectName() : QString::null;
}
void setProxyName(QString name) {
m_proxy = parent()->findChild<QWidget *>(name);
}
explicit FocusForwarderDesigner(QWidget *parent = NULL) :
QWidget(parent) {}
protected:
void focusInEvent(QFocusEvent *) {
if (m_proxy) m_proxy->setFocus();
}
private:
QWidget *m_proxy;
}
In the designer, you would add a string-property with name proxyName and set it to the proxy's name. Don't forget to set the focus policy to Tab Focus in designer.
After some additional thoughts I post an answer to my own question because it is a working solution but it is not ideal. Therefore, I'm still searching for a better one! As a note, my application mainly relies on mouse wheel interactions for changing the focus of widgets.
In my question I mentioned that overriding:
bool focusNextPrevChild(bool next)
could lead to a working system. The "receiving" widget would simply ignore the focus by returning "true" if it is marked as "last item" or "first item" and the "next" parameter would lead to a circular behavior. Although this works for the tab and space+tab key combinations, there are cases where focusNextPrevChild is not called explicitly. In my case it is not called for focus changes related to mouse wheel events.
What I do instead is overriding:
void wheelEvent(QWheelEvent* event)
This gives me direct control over all the focus events related to the mouse wheel. My overridden function looks like this:
void SelectionIconButton::wheelEvent(QWheelEvent* event)
{
bool next = event->delta() > 0;
if (m_IsLastInFocusChain && next) {
event->accept();
return;
}
if (m_IsFirstInFocusChain && !next) {
event->accept();
return;
}
QPushButton::wheelEvent(event);
}
So this system's requirements are:
Each widget has to somehow implement two bools and handle their
state.
Each of those widgets has to be configured either at startup
or in dynamic screens during appliation use
Listening only to
wheelEvent does not allow me to handle tab key and space+tab key
combinations
You see that this solution works but it involves some effort to apply it to a large application. I was thinking about a more general solution. Maybe a global list that is updated when a screen is changing. This global list would then somehow decide if a focus change is allowed or not. Unfortunately, this again is problematic with mouse wheel events because some widgets are "active" and the wheel event does not even want to change focus but alter the value in an input field, for example, instead.
Edit:
I might have to add that the default implementation of QWidget::wheelEvent() and QPushButton::wheelEvent() and many more Qt-Widgets just ignore the event by setting event->ignore().
In my application all those ignored events are caught at a high level widget which then interprets the QWheelEvent and uses its delta to call focusPreNextChild() the right amount of time.
I'm trying to display a Google login dialog in a QWebView, and as I recall, Google likes to set your keyboard focus to the first input field on the page (in this case, the e-mail field).
Unfortunately, the QWebView widget doesn't actually respect this behaviour, and therefore loads the page with keyboard focus on nothing at all:
So I decided dig about a little, and inserted this code snippet into my class logic:
void GoogleAuthDialog::pageLoaded(bool ok) {
if (ok) {
ui->webView->setFocus();
ui->webView->page()->mainFrame()->setFocus();
QWebElement el = ui->webView->page()->mainFrame()->findFirstElement("input:not([type=hidden])");
if (!el.isNull()) {
el.setFocus();
el.evaluateJavaScript("this.focus()");
el.evaluateJavaScript("this.click()");
}
}
}
And the following declaration in my header file:
...
private slots:
void pageLoaded(bool);
Back in the class code, I connected the appropriate signal from the QWebView to my slot:
connect(ui->webView, SIGNAL(loadFinished(bool)), this, SLOT(pageLoaded(bool)));
Yes, I am throwing every possible thing I can think of at it to redirect keyboard focus to the first input box.
Unfortunately, the code did not seem to work, as while it did focus the right input box, I could not type anything inside of it until I clicked it myself, or pressed Tab:
Next I bound the function to my Control key, and proceeded to produce strange results.
If I placed focus into the password field manually, and pressed the Control key, I noticed that I would continue to have keyboard focus in the password field, but have 'visual' focus in the e-mail field:
Also, when I typed something in this 'state', occasionally a letter might 'leak' into the e-mail field before the visual and keyboard focus would 'reset' to the password field:
Is there a proper way of redirecting keyboard focus to an input field of my choosing?
I managed to redirect the keyboard input focus by simulating tab focusing via QKeyEvent:
void GoogleAuthDialog::pageLoaded(bool ok) {
if (ok) {
//Gets the first input element
QWebElement el = ui->webView->page()->currentFrame()->findFirstElement("input:not([type=hidden])");
if (!el.isNull()) {
el.setFocus();
}
// Simulate a forward tab, then back.
QKeyEvent *forwardTab = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
QKeyEvent *backwardTab = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier);
QCoreApplication::postEvent(ui->webView, forwardTab);
QCoreApplication::postEvent(ui->webView, backwardTab);
}
}
In my opinion, this does seem like a 'hack-ish' solution, so if there is a 'right' way to do this, I'm all ears.
pgh is right. Just set focus on the QWebView object during your app's initialization.
m_webView->setFocus();