Qt - set QWidget with a QWidget class - c++

I'm learning to use Qt and I want to extend the Terminal Example of Qt. I want to use its console.cpp in a QWidget from de Containers tab in the Design editor.
In the Terminal Example of Qt, this class is used like this:
ui->setupUi(this);
console = new Console;
console->setEnabled(false);
setCentralWidget(console);
But as I want to use it in a smaller QWidget I don't know how to set it, which method can I use as equivalent of setCentralWidget for my QWidget? Image of the Design tab with the widget I want to set to the QWidget class
Can I also use the same QWidget in several tabs?
The console.cpp code is the following one.
#include "console.h"
#include <QScrollBar>
#include <QtCore/QDebug>
Console::Console(QWidget *parent)
: QPlainTextEdit(parent)
, localEchoEnabled(false)
{
document()->setMaximumBlockCount(100);
QPalette p = palette();
p.setColor(QPalette::Base, Qt::black);
p.setColor(QPalette::Text, Qt::green);
setPalette(p);
}
void Console::putData(const QByteArray &data)
{
insertPlainText(QString(data));
QScrollBar *bar = verticalScrollBar();
bar->setValue(bar->maximum());
}
void Console::setLocalEchoEnabled(bool set)
{
localEchoEnabled = set;
}
void Console::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_Backspace:
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Up:
case Qt::Key_Down:
break;
default:
if (localEchoEnabled)
QPlainTextEdit::keyPressEvent(e);
emit getData(e->text().toLocal8Bit());
}
}
void Console::mousePressEvent(QMouseEvent *e)
{
Q_UNUSED(e)
setFocus();
}
void Console::mouseDoubleClickEvent(QMouseEvent *e)
{
Q_UNUSED(e)
}
void Console::contextMenuEvent(QContextMenuEvent *e)
{
Q_UNUSED(e)
}
The Qt Example is this one: http://doc.qt.io/qt-5/qtserialport-terminal-example.html
Thanks so much!

If you're wanting to add it via designer just promote the QWidget that you added in your screegrab. (Right click > "Promote to..." > Fill in name & path to the console header).
Or not using promotion, you can add the console to a layout in code :
Console* console = new Console();
ui->your_layout_name_here->addWidget( console );

Related

How to press a key in QT?

I am new to QT.
How can I press and release a button in Qt ?
In java I do the below program to control key events ?
Robot r = new Robot();
r.keyPress(KeyEvent.VK_ENTER);
r.keyRelease(KeyEvent.VK_ENTER);
I want to press a key in keyboard programatically.
But , How can I do the same thing in QT ?
You can either create QKeyEvent and send them to the application using QApplication::sendEvent(). Or if you want higher level API, you can build your application with QtTest module and use keyClick functions. See https://doc.qt.io/qt-6/qtest.html
In Qt, key presses are handled by the Event System. Like other languages/frameworks events are encapsulated in an object, in Qt's case, QEvent. All subclasses of QObject have a virtual QObject::event(QEvent *e) method to handle event objects sent to it. This method does not directly handle the event, but calls the object's appropriate event handler based on the QEvent::Type enum. In the case of key presses, the QEvent::type() method returns QEvent::KeyPress.
While most events are handled internally without programmer intervention, you may send events manually using the QCoreApplication class or its subclass QGuiApplication. An instance of one of these classes is typically instantiated in the boilerplate main.cpp file created when you generate a new project with Qt Creator. These classes have access to the methods QCoreApplication::sendEvent(QObject *receiver, QEvent *event), which sends an event directly to receiver, and QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority), which sends the event to Qt's event queue to be processed later.
I've created a project to demonstrate this functionality. This app just displays a plain rectangle which can be either red or blue. The rectangle only switches colors when it receives a QKeyEvent indicating that the C key was pressed. Below the rectangle is a button which programmatically produces this event and sends it to the rectangle's widget. The project went on a bit long and is a bit messy, but I hope it helps some.
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Here I modify the boilerplate code to allow MainWindow w to have access
// to QApplication a so that a widget in MainWindow w can use postEvent()
MainWindow w(nullptr, &a);
w.show();
return a.exec();
}
mainwindow.h
#include <QCoreApplication>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr, QCoreApplication* app = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGridLayout>
#include <QLabel>
#include "keypressacceptor.h"
#include "keypressgenerator.h"
MainWindow::MainWindow(QWidget *parent, QCoreApplication *app)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QGridLayout* layout = new QGridLayout(this);
KeyPressAcceptor* kpa = new KeyPressAcceptor(this);
KeyPressGenerator* kpg = new KeyPressGenerator();
kpg->registerReceiver(kpa);
kpg->registerApp(app);
layout->addWidget(kpa);
layout->addWidget(kpg);
centralWidget()->setLayout(layout);
}
MainWindow::~MainWindow()
{
delete ui;
}
keypressacceptor.h
#include <QWidget>
class KeyPressAcceptor : public QWidget
{
Q_OBJECT
public:
explicit KeyPressAcceptor(QWidget *parent = nullptr);
bool handleKeyPress(const int &key);
protected:
bool event(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
signals:
private:
QColor m_color;
};
keypressacceptor.cpp
#include "keypressacceptor.h"
#include <QEvent>
#include <QKeyEvent>
#include <QPainter>
KeyPressAcceptor::KeyPressAcceptor(QWidget *parent)
: QWidget{parent}
, m_color(QColor(220, 20, 20))
{
// Setting focus policy so that the widget can accept focus.
// This is necessary to process key press events.
setFocusPolicy(Qt::StrongFocus);
}
bool KeyPressAcceptor::handleKeyPress(const int &key)
{
// This method performs some arbitrary action, in this case changing a
// color, to indicate that a key press has been processed.
switch (key) {
case Qt::Key_C:
// If the "C" key was pressed, switch m_color
if (m_color == QColor(220, 20, 20)) {
m_color = QColor(20, 20, 220);
} else {
m_color = QColor(220, 20, 20);
}
// Call update() to tell Qt to repaint this widget once Qt has entered
// the main event loop
update();
return true;
default:
return false;
}
}
bool KeyPressAcceptor::event(QEvent *event)
{
switch (event->type()) {
case QEvent::KeyPress:
// If the received event is of type QEvent::KeyPress, then cast the
// variable event to type QKeyEvent*, then use the event's key()
// method to pass as an argument to this class's handleKeyPress()
// method.
return handleKeyPress(static_cast<QKeyEvent*>(event)->key());
// Note! This overrides QWidget's default behavior upon receiving a
// QKeyEvent event
default:
// Otherwise, be sure to use the class's superclass to process any
// other events.
return QWidget::event(event);
}
}
void KeyPressAcceptor::paintEvent(QPaintEvent *event)
{
// Don't need to use the event parameter in this implementation.
Q_UNUSED(event)
// Want to draw a rectangle centered in the widget whose height is half
// the widget's height and whose width is half the widget's width.
// The color of the rectangle is determined by m_color.
// First define the rectangle using the height and width properties of
// QWidget to determine the rectangle's height, width, and coordinates of
// top left corner.
QRect rect(width() / 4, height() / 4, // Coordinates of top left corner
width() / 2, height() / 2); // Width and height
// Create a QPainter object to paint with
QPainter painter(this);
// Set pen and brush for rectangle's outline and fill respectively.
painter.setPen(QColor(0,0,0)); // Black 1px pen
painter.setBrush(QBrush(m_color)); // Solid fill of color m_color
// Draw the rectangle
painter.drawRect(rect);
}
keypressgenerator.h
#include <QCoreApplication>
#include <QPushButton>
#include <QObject>
class KeyPressGenerator : public QPushButton
{
Q_OBJECT
public:
explicit KeyPressGenerator(QWidget *parent = nullptr);
void registerApp(QCoreApplication* app);
void registerReceiver(QObject* receiver);
public slots:
void generateKeyPress();
private:
QCoreApplication* m_app;
QObject* m_receiver;
};
keypressgenerator.cpp
#include "keypressgenerator.h"
#include <QCoreApplication>
#include <QKeyEvent>
KeyPressGenerator::KeyPressGenerator(QWidget *parent)
: QPushButton{parent}
, m_app(nullptr)
, m_receiver(nullptr)
{
setText("Push Button to Send C Key Press");
// Connect clicked signal to generateKeyPress so when button is clicked
// a programmatically generated keypress is sent to m_receiver
connect(this, &KeyPressGenerator::clicked,
this, &KeyPressGenerator::generateKeyPress);
}
void KeyPressGenerator::registerApp(QCoreApplication *app)
{
m_app = app;
}
void KeyPressGenerator::registerReceiver(QObject *receiver)
{
m_receiver = receiver;
}
void KeyPressGenerator::generateKeyPress()
{
if (m_app == nullptr || m_receiver == nullptr) return;
// Generate the key press event. Check documentation for an explanation of
// the constructor's parameters.
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_C, Qt::NoModifier);
m_app->postEvent(m_receiver, event);
}

QComboBox: Only show the icons when expanded

Starting from a "normal" QCombobox
I'd like to get a QCombobox that only shows the icon when it's expanded, but not when it's collapsed.
I've found several answers to similar questions, but all of them show code for much more complex situations and I have not managed to distill the core of it.
There are two approaches I've seen: attaching a QListView or using a QItemDelegate (or both).
But I could not find any sample code that is straight to the point.
This is my starting point:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->iconsComboBox->addItem(QIcon(":/icons/1.png"), "red");
ui->iconsComboBox->addItem(QIcon(":/icons/2.png"), "green");
ui->iconsComboBox->addItem(QIcon(":/icons/3.png"), "pink");
auto quitAction = new QAction();
quitAction->setShortcuts(QKeySequence::Quit);
addAction(quitAction);
connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
}
The full working code at that stage is here: https://github.com/aoloe/cpp-qt-playground-qcombobox/tree/simple-qcombobox
How can I hide the icon when the QCombobox is closed?
I have accepted the two pull requests by eyllanesc:
A solution extending QComboBox
A solution using QProxyStyle
You can get the code and run it to see it in action.
One possible solution is to override the paintEvent method:
##ifndef COMBOBOX_H
#define COMBOBOX_H
#include &ltQComboBox>
#include &ltQStylePainter>
class ComboBox : public QComboBox
{
public:
using QComboBox::QComboBox;
protected:
void paintEvent(QPaintEvent *)
{
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
opt.currentIcon = QIcon();
opt.iconSize = QSize();
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
};
#endif // COMBOBOX_H
and if you want to use it in the .ui then you must promote it.
Another possible solution is to use a QProxyStyle
#ifndef COMBOBOXPROXYSTYLE_H
#define COMBOBOXPROXYSTYLE_H
#include <QProxyStyle>
class ComboBoxProxyStyle : public QProxyStyle
{
public:
using QProxyStyle::QProxyStyle;
void drawControl(QStyle::ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const
{
if(element == QStyle::CE_ComboBoxLabel){
if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
QStyleOptionComboBox cb_tmp(*cb);
cb_tmp.currentIcon = QIcon();
cb_tmp.iconSize = QSize();
QProxyStyle::drawControl(element, &cb_tmp, p, w);
return;
}
}
QProxyStyle::drawControl(element, opt, p, w);
}
};
#endif // COMBOBOXPROXYSTYLE_H
ui->iconsComboBox->setStyle(new ComboBoxProxyStyle(ui->iconsComboBox->style()));

Qt signals and slots passing data

I'm pretty new to c++ and qt. I'm not sure if i use the right terminology describe what I want to achieve. But here it goes.
My application spawns and removes widgets in a gridlayout when the user pushes buttons. Managed to do this successfully. However when the user uses the spawned widgets I want the widgets to interact with each other.
QList<QLineEdit*> m_ptrLEPathList;
QList<QPushButton*> m_ptrPBList;
qint8 m_noFields;
void MainWindow::on_pbIncFields_clicked()
{
//create widgets and place on a new row in a gridLayout
QLineEdit *lineEditPath = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
//storing pointers in lists to be able to delete them later.
m_ptrLEPathList.append(lineEditPath);
m_ptrPBList.append(pushButton);
ui->gridLayout->addWidget(m_ptrLEPathList.last(),m_noFields,0);
ui->gridLayout->addWidget(m_ptrPBList.last(),m_noFields,1);
connect(m_ptrPBList.last(), SIGNAL(clicked(bool), this, SLOT(on_addPath()));
m_noFields++;
}
void MainWindow::on_pbDecFields()
{
//delete last spawned widgets
}
void MainWindow::on_addPath()
{
QFileDialog getPath();
getPath.exec();
//somehow set the text of the line edit spawned on the same row as the pushbutton
}
So my slot is executed when I push any spawned button but I have no idea how to store the data from the file dialog in the related lineEdit.
Is the basic idea of what I'm trying to do ok or is there any other solution to achieve the fuctionality I'm looking for?
In on_addPath slot you can use QObject::sender method to get the clicked button, and, assuming m_ptrLEPathList and m_ptrPBList lists are equal, you can easily get the corresponding QLineEdit:
void MainWindow::on_addPath()
{
QFileDialog dialog;
if (!dialog.exec())
{
return;
}
QStringList fileNames = dialog.selectedFiles();
if (fileNames.isEmpty())
{
return;
}
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn)
{
return;
}
Q_ASSERT(m_ptrPBList.size() == m_ptrLEPathList.size());
int index = m_ptrPBList.indexOf(btn);
if (index == -1)
{
return;
}
QLineEdit *edit = m_ptrLEPathList.at(index);
edit->setText(fileNames.first());
}
You are including 'on_addPath' function out of the scope of the 'MainWindow' class, so when the slot is called you have not access to member elements in the class.
Try to include the slot function into the class and check if you have direct access to the member elements. Also, the 'lineEditPath' element must be a member object, so it must be included into the class definition.
Something like this:
void MainWindow::on_addPath()
{
QFileDialog getPath();
getPath.exec();
QStringList fileNames = dialog.selectedFiles();
if (fileNames.isEmpty())
{
return;
}
m_lineEditPath->setText(fileNames.first());
}
First off, void on_addPath() must be void MainWindow::on_addPath()
As for linking the data from QFileDialog it is simple. Try this:
void MainWindow::on_addPath() {
/* Get the push button you clicked */
QPushButon *btn = qobject_cast<QPushButton*>( sender() );
/* Make sure both the lists have the same size */
Q_ASSERT(m_ptrPBList.size() == m_ptrLEPathList.size());
/* If the sender is a button in your list */
if ( m_ptrPBList.contains( btn ) ) {
/* Get the index of your push button */
int idx = m_ptrPBList.indexOf( btn );
/* Get the corresponding line edit */
QLineEdit *le = m_ptrLEPathList.at( idx );
/* Get your path */
QString path = QFileDialog::getOpenFileName( this, "Caption", "Default Location" );
/* If you path is not empty, store it */
if ( not path.isEmpty() )
le->setText( path );
}
}
Add a map to private section
QMap<QPushButton*, QLineEdit*> map;
Then
QLineEdit *lineEditPath = new QLineEdit(this);
QPushButton *pushButton = new QPushButton(this);
map.insert(pushButton, lineEditPath);
You can use sender() method like follow:
void on_addPath()
{
QFileDialog getPath();
getPath.exec();
QObject* obj = sender();
QPushButton *pb = 0;
if((pb = qobject_cast<QPushButton *>(obj)) != 0) {
QLineEdit* lineEdit = map->value(pb, 0);
if( lineEdit != 0 )
lineEdit->setText( getPath.<some function to get selected file name> );
}
}
I think the cleanest solution would be to contain the QLineEdit and QPushButton in a custom widget class, if it suits your project. That way you could use the file dialog inside this class, and you won't have to store the widgets in lists. It is hard to give you all the information, as you didn't really provide any details what your application is supposed to do. But in any case, the custom widget class would look something like this (you should define all the functions inside a .cpp file):
#ifndef WIDGETCONTAINER_H
#define WIDGETCONTAINER_H
#include <QWidget>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QFileDialog>
class WidgetContainer : public QWidget
{
Q_OBJECT
public:
WidgetContainer(QWidget *parent = 0) : QWidget(parent)
{
setLayout(new QHBoxLayout);
button.setText("BUTTON");
layout()->addWidget(&lineEdit);
layout()->addWidget(&button);
connect(&button, SIGNAL(clicked()), this, SLOT(buttonPressed()));
}
private:
QLineEdit lineEdit;
QPushButton button;
private slots:
void buttonPressed()
{
QString filename = QFileDialog::getSaveFileName();
lineEdit.setText(filename);
}
};
#endif // WIDGETCONTAINER_H

QT4.8 - Implement highlight to QLineEdit

I am looking for a way to implement a highlighter for the QLineEdit widget.
I am using a QLineEdit to store a a path variable in my application, and I would to highlight environment variables.
Something like this:
${MY_ENVVAR}/foo/bar/myfile
Practically I would to have something like the QHightligher class.
Subclass QSyntaxHighliger
Write your own highlightBlock() method
Detect in your string this specific text which must be colored (you can do this for example by regular expression QRegExp) and use setFormat() method to paint string from x to x+n with some color
Helpful link: http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#highlightBlock
I never used highliter with QLineEdit before, so it can be impossible. But we can simply attach highliter to the QTextEdit. So we should create lineEdit from textEdit, there are many examples in web how to do that.
For example (I use code given by hyde with small additions.)
TextEdit.h
#ifndef TEXTEDIT_H
#define TEXTEDIT_H
#include <QTextEdit>
#include <QCompleter>
#include <QTextEdit>
#include <QKeyEvent>
#include <QStyleOption>
#include <QApplication>
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
explicit TextEdit(QWidget *parent = 0)
{
setTabChangesFocus(true);
setWordWrapMode(QTextOption::NoWrap);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setFixedHeight(sizeHint().height());
}
void keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
event->ignore();
else
QTextEdit::keyPressEvent(event);
}
QSize sizeHint() const
{
QFontMetrics fm(font());
int h = qMax(fm.height(), 14) + 4;
int w = fm.width(QLatin1Char('x')) * 17 + 4;
QStyleOptionFrameV2 opt;
opt.initFrom(this);
return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h).
expandedTo(QApplication::globalStrut()), this));
}
};
#endif // TEXTEDIT_H
Usage (in the main.cpp)
#include "textedit.h"
//...
TextEdit *t = new TextEdit;
t->show();
new Highlighter(t->document());
Highlighter constructor as example
Highlighter::Highlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
}

Qt catch pressed key

i think of to write primitive snake. i have window where program paints random lines.
but i can't think up how to catch pressed key to change direction of painted line.
class QUpdatingPathIte : public QGraphicsPathItem
{
void advance(int phase)
{
if (phase == 0)
return;
int x,y;
int w,a,s,d;
char c;
//
// HOW TO MAKE THIS HERE? (i had done early in console application)
// but i can't do this in GUI , what should i do?
scanf("%c",&c);
if (c=='w')
{ x=-20; y=0; }
else if (c=='s')
{ x=20; y=0; }
else if (c=='a')
{ x=0; y=-20; }
else if(c=='d')
{ x=0; y=20; }
QPainterPath p = path();
p.lineTo(x, y);
setPath(p);
}
};
#include <QtGui/QApplication>
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene s;
QGraphicsView v(&s);
QUpdatingPathIte item;
item.setPen(QPen(QColor("black")));
s.addItem(&item);
v.show();
QTimer *timer = new QTimer(&s);
timer->connect(timer, SIGNAL(timeout()), &s, SLOT(advance()));
timer->start(500);
return a.exec();
}
QGraphicsView inherits from from QWidget because it inherits from QAbstractScrollArea. You should create a custom subclass of QGraphicsView and just override the function keyPressEvent(). Example:
class SnakeView : public QGraphicsView
{
protected:
keyPressEvent(QKeyEvent *e)
{
// Do something
// Otherwise pass to the graphics view
QGraphicsView::keyPressEvent(e)
}
};
Then rather than creating a QGraphicsView object you would create a SnakeView object.
QGraphicsView reimplements keyPressEvent and will forward events to the QGraphicsScene. QGraphicsScene supports key presses through its keyPressEvent handler. By default the QGraphicsScene will forward that key press to the current QGraphicsItem through its keyPressEvent function.
Given the above, what you likely want to do is reimplement the keyPressEvent handler in your QUpdatingPathItem:
void QUpdatingPathItem::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Key::Key_A: /* do something useful */; break;
case Key::Key_S: /* do something useful */; break;
case Key::Key_W: /* do something useful */; break;
case Key::Key_D: /* do something useful */; break;
}
}
I couldn't really follow what you were trying to do in your advance method, so I can't offer much in the way of a suggested handler.
Besides implementing QObject::installEventFilter, you could also subclass any of QGraphicsView, QGraphicsScene, or QGraphicsItem and override the keyPressEvent function.
thanks you all. Then I didn't reach my aim. But time went on and I've come to the same problem and now I solved it.
There are right and worth answers but then I wasn't good enough to put them together because no one was full and sufficient. Now I did it so:
HEADER FILE "header.h"
class MyGraphicView: public QGraphicsView
{
Updating *m_update;
public:
MyGraphicView(Updating *update);
void keyPressEvent(QKeyEvent *event);
};
#include "header.h"
void GraphicView::keyPressEvent(QKeyEvent *event)
{
switch ( event->key())
{case Qt::Key_Up:
m_update->move_up();
break;
case Qt::Key_Down:
m_update->move_down();
break;
case Qt::Key_Left:
{ m_update->move_right();
break;
}
case Qt::Key_Right:
{ m_update->move_left();
break;
}
}
}
Thanks again!
Your choice of QGraphicsView isn't very fortunate.
Basically, when you are implementing some custom painted graphical item (the graphic area of the game is this), you should create your own widget and simply implement the QWidget::keyPressEvent() method.