I am working on creating a simple List Widget that displays "double" values when a "run" button is clicked. The widget contains 20 rows and 6 columns. Right now, I would just like for the values to all be the same number. I have the widget displaying how I want it, however, I am new to C++ and am not sure how to get the values to display in the widget when I click a pushbutton "run". Here is my code thus far:
qlistdouble.cpp
#include "qlistdouble.h"
#include <QList>
#include <QTableWidgetItem>
Qlistdouble::Qlistdouble(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
}
Qlistdouble::GetNumbers()
{
}
void Qlistdouble::MyClass(QList<double*> *slotString)
{
connect(ui.getNumber, SIGNAL(clicked()), this, SLOT(clear()));
for( int row = 0; row < 20; row++ )
{
for( int column = 0; column < 6; column++ )
{
slotString = new QList<double*>;
QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg(5));
ui.tableWidget_values->setItem(row, column, newItem);
}
}
}
qlistdouble.h
#ifndef QLISTDOUBLE_H
#define QLISTDOUBLE_H
#include <QtGui/QMainWindow>
#include "ui_qlistdouble.h"
class Qlistdouble : public QMainWindow
{
Q_OBJECT
public:
Qlistdouble(QWidget *parent = 0, Qt::WFlags flags = 0);
~Qlistdouble();
void MyClass(QList<double*> *slotString);
private:
Ui::QlistdoubleClass ui;
};
#endif // QLISTDOUBLE_H
I know that this is a simple question, but if someone could give me some suggestions on how to go about this, I would really appreciate it. Thanks in advance.
Displaying lists in Qt is usually achieved through QListView as a view and a QAbstractListModel as an underlying model. The list of model view examples is quite helpful.
I suggest looking at the source code of one of the simple example programs supplied with qt. essentially all you need to do is construct a Qapplication and your main window class, then call show on the main window and then exec() to start the event loop. I could write some from memory but the example code will be better.
Reading your comment, and using the very helpful link the other poster provided, I suggest starting with the trivial wizard example. It is a complete executable in a single file. You could put your own widget in one of the pages and calculate the numbers in when the page is shown.
http://qt-project.org/doc/qt-4.8/dialogs-trivialwizard.html
If this still seems too complex, then I suggest you may be trying to do too much as initial step. In this case I suggest instead writing a console application that prints the numbers to std::cout.
Related
I am working on Unix 19.02 and Qt 5.13.0 .
I am trying to make an introduction for my application before anything is shown up in the window. So, what I thought is to make the QPainter change the background color in a while-loop. I tried it too many times and I had a problem in the timing : "When is the moment or the event that I should make the qpainter start the introduction?". I searched and found out the best thing is to reimplement this function :
void showEvent(QShowEvent* event) {...}
inside that function, I call "repaint()", and it will do all the introduction.
I tried all of these ways :
Making the while-loop inside the "paintEvent" function.
Making the while-loop outside the "paintEvent" function, and every time we change the color of the background I call "repaint()". Surely I made a small timeout for the computer to slow it down a bit.
-Note: using these two ways above, the window shows up, but it's background-color is black and nothing shows up neither the color doesn't change until the introduction is finished!
Lastely, a while-loop that does not use "QPainter", it uses stylesheet instead and after we change the "background-color" property. We call : ui->centeralWidget->style->polish(ui->centeralWidget);
The last way, forces the window not to show up until the introduction is finished completely (it takes 5 seconds to execute the introduction, the while-loop). this is the code :
QString ss; // for opacity
QString ss_prefix = "background-color: rgba(255, 255, 255, ";
QString ss_suffix = ");";
while(i<=50) {
i++;
Opacity += 5;
qDebug() << "Changing opacity : " << ss_prefix+std::to_string(Opacity).c_str()+ss_suffix;
ui->centralWidget->setStyleSheet(
ss_prefix+std::to_string(Opacity).c_str()+ss_suffix);
ui->centralWidget->style()->polish(ui->centralWidget);
QThread::currentThread()->msleep(100); // 100 * 50 = 5000/1000 = 5 seconds to execute!
}
Nevertheless, there are other ways to make an introduction, but I really would want to know what is happening and fix this problem!
The last equation is meant to calculate the time to execute :
100(sleep) * 50(The code will get executed 50 times) = 5000 / 1000 (it is in milli, so divide by 1000) = 5 seconds
Wish you understood my situation.
You're not providing complete code, so I am partially guessing here.
You need to wait for the main app event loop to get started before starting your animation. If you call that animation routine (with the msleep() at the end) from the QMainWindow (or whatever your top-level QWidget is) constructor, the main window is never given a chance to show itself (until the animation is done).
If I'm right, you could consider starting your animation from the first QWidget::showEvent() by using a bool flag to know when the first show happens. Another way would be to start a QTimer::singleShot(0, this, &MainWindow::animate) in the constructor (the timer will trigger once the main event loop starts). There are other ways, but the main point is that you need to let the QApplication event loop to be running and the window to have already been initialized.
Having said all that, I'd probably take a different approach, maybe with a QSplashScreen or a custom QWidget to show the animation before, or while, loading the main window in the background.
Also, look into QString template/replacement system, specifically the arg() methods and how they can be used to simplify string construction.
As #MaximPaperno points out, the problem is while-loop + msleep() since they block the eventloop preventing the GUI from working normally. For these cases, Qt provides strategies such as QTimer, QXAnimation, etc. In this case a simple solution is to use QVariantAnimation:
*.h
// ...
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private Q_SLOTS:
void applyOpacity(const QVariant &value);
// ...
*.cpp
// ...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVariantAnimation *animation = new QVariantAnimation(this);
animation->setStartValue(0);
animation->setEndValue(255);
animation->setDuration(5 * 1000);
connect(animation, &QVariantAnimation::valueChanged, this, &MainWindow::applyOpacity);
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
// ...
void MainWindow::applyOpacity(const QVariant & value)
{
bool ok;
int opacity = value.toInt(&ok);
if(ok) {
QString qss = QString("background-color: rgba(255, 255, 255, %1)").arg(opacity);
ui->centralWidget->setStyleSheet(qss);
}
}
It's a trivial situation. Just show welcome window first. When it is closed, show your main window. Use signals to achieve it.
We have a longstanding convention in our UI that items are shown in bold when they have been changed but the change is not yet committed. Strangely, until now we haven't used any combo boxes, but I have a use for one now and need to implement this behaviour. So I need to programmatically bold (and later un-bold) the text displayed by a closed combo box. However, I don't want to bold the entire list of items in the pop-up. I could accept bolding the selected item in the list if that's easier.
I've seen lots of answers doing almost this, but usually trying to modify the list items rather than the button. I've tried variations on most of them; unfortunately I didn't keep records of what I tried. For what it's worth, my code currently looks like:
myCombo->setStyleSheet(
"QComboBox {font-weight: bold;} "
"QComboBox QAbstractItemView::item {font-weight: normal;}"
);
This turns the button bold, but also the list items. The same behaviour is seen when I apply the normal weight just to the QAbstractItemView without the ::item, and when I tried a different technique based on :open and :closed on the QComboBox.
I will say I'm fairly new to Qt. I am using Qt5 on Fedora 26, but will be deploying to CentOS 7.
It seems that setting the font-style in QComboBox overrides the view's (and it shouldn't, IMHO).
But, when I tried to explicitly set a view to the combo box, this way:
view = new QListView();
myCombo->setView(view);
the stylesheet posted by the OP suddenly worked.
By the way, the new view is different from the original (e.g. has a white background, etc) and I guess the OP isn't happy with that. One could go on styling it, of course, but one would rather prefer a ready to use view, with a consistent style.
Inspecting the default QComboBox view:
QComboBox * combo = new QComboBox();
qDebug() << combo->view();
yelds this:
QComboBoxListView(0x2091880)
So, there is a specific QComboBoxListView class, which is nowhere to be found in documentation and is defined in qcombobox_p.h, not a file one could include, really, but at least we can understand where the issue come from, in the viewOptions overridden method:
QStyleOptionViewItem option = QListView::viewOptions();
option.showDecorationSelected = true;
if (combo)
option.font = combo->font(); // <--- here
return option;
That combo is a private pointer to QComboBox, initialized in construction:
QComboBoxListView(QComboBox *cmb = 0) : combo(cmb) {}
which will always override the view options font with its own.
Let's have a copy of the QComboBoxListView class, edited and renamed:
comboitemview.h
#ifndef COMBOITEMVIEW_H
#define COMBOITEMVIEW_H
#include <QListView>
#include <QComboBox>
class ComboItemView : public QListView
{
Q_OBJECT
QComboBox * _box;
public:
ComboItemView(QComboBox *box);
protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
QStyleOptionViewItem viewOptions() const;
};
#endif // COMBOITEMVIEW_H
comboitemview.cpp
#include "comboitemview.h"
#include <QPaintEvent>
#include <QPainter>
ComboItemView::ComboItemView(QComboBox * box = 0) : _box(box){}
void ComboItemView::paintEvent(QPaintEvent *event)
{
if (_box)
{
QStyleOptionComboBox opt;
opt.initFrom(_box);
opt.editable = _box->isEditable();
if (_box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, _box))
{
QStyleOptionMenuItem menuOpt;
menuOpt.initFrom(this);
menuOpt.palette = palette();
menuOpt.state = QStyle::State_None;
menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
menuOpt.menuRect = event->rect();
menuOpt.maxIconWidth = 0;
menuOpt.tabWidth = 0;
QPainter p(viewport());
_box->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
}
}
QListView::paintEvent(event);
}
void ComboItemView::resizeEvent(QResizeEvent *event)
{
resizeContents(viewport()->width(), contentsSize().height());
QListView::resizeEvent(event);
}
QStyleOptionViewItem ComboItemView::viewOptions() const
{
QStyleOptionViewItem option = QListView::viewOptions();
option.showDecorationSelected = true;
return option;
}
And finally use it to style the view font:
myCombo->setView(new ComboItemView(myCombo));
myCombo->setStyleSheet(
"QComboBox {font-weight: bold;} "
"QComboBox QAbstractItemView {font-weight: normal;}"
);
I'm new to QT Creator GUI and I'm having a hard time with Signals and Slots. I'm using QT 4.2.1 to make a word search puzzle for practicing
Below is a portion of my code, creating a 2D puzzle using vectors. I tried to use array but the size of the puzzle will be decided by the user. I had a lot of compile errors using array. So I decided to use Vector
I tried some options from this post: Get index of QPushButton on 2D array QPushButton but they all don't work.
Can you help me to see why the signals and slots are not connected? And is there a way to check if the signal is connected?
I would appreciate any helps and feedback. Thank you!
void MainWindow::displayPuzzle(QVector<QVector<QPushButton*>>& button, QVector<QVector<QChar>> puzzle2D){
widget1 = new QWidget;
QGridLayout* l = new QGridLayout;
QSignalMapper mapper;
for (int r = 0; r < puzzle2D.size(); r++) {
QVector<QPushButton*> vect;
for (int c = 0; c < puzzle2D[r].size(); c++) {
//format the button
vect.push_back(new QPushButton);
vect.last()->setText(puzzle2D[r][c]);
vect.last()->setFixedSize(60,60);
vect.last()->show();
auto pos = QString{"%1 %2"}.arg(r).arg(c);
//connect with signals and slots
mapper.connect(vect.last(), SIGNAL(clicked()), SLOT(map()));
mapper.setMapping(vect.last(), pos);
//add button to layout
l->addWidget(vect.last(), r, c);
l->setSpacing(0);
l->setMargin(0);
l->setContentsMargins(-1,-1,-1,-1);
}
button.push_back(vect);
}
connect(&mapper, SIGNAL(mapped(QString)), SLOT(puzzleClick(QString)));
widget1->setLayout(l);
}
void MainWindow::puzzleClick(QString text){
int r = text.split(" ").at(0).toInt();
int c = text.split(" ").at(1).toInt();
QMessageBox::information(this, "click:", r + " " + c );
}
The answer you are linking is very complete and useful, I think you should stick with that one. Any suggestion about those methods would be just rewriting the original answer. Maybe you can ask something more specific about what you can't understand of those options.
Another solution, not cited in that answer, would be implementing a class extending QPushButton, intercept the clicked() signal and re-emit it with the data you want. The same thing can be obtained with lambda functions in Qt5 and C++11: have a look at this: qt-custom-qpushbutton-clicked-signal the two answers to this questions explain how to do this.
I create own widget based on QTableView. It's something like file dialog (list). I want to act intuitively.
a) working with whole rows
b) indicator also worked with whole rows
c) using switched enter the lower level (subdirectory)
d) after run program or moving to a lower level cursor must be on the first row of the table (row 0)
And there is problem. I can not force the program to place the cursor on the first line.
I tried several methods, but none succeeded. setCurrentIndex. selectRow etc. Cursor is always somewhere else. Not highlighted, not on first line, but once it's on 10 position second on 4 position etc. It behaves unpredictably.
How can I do it?
Here my code is:
mFileBoxWidget::mFileBoxWidget(QWidget *parent) :
QTableView(parent)
,model(new QFileSystemModel())
{
this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
this->setShowGrid(false);
this->verticalHeader()->setVisible(false);
this->installEventFilter(this);
model->setReadOnly(true);
this->setModel(model);
this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch );
this->setColumnWidth(1,70);
this->setColumnWidth(2,70);
this->setColumnWidth(3,110);
this->setRootIndex(model->setRootPath("C://"));
this->setSelectionMode(QAbstractItemView::SingleSelection);
this->setSelectionBehavior(QAbstractItemView::SelectRows);
//this->selectRow(0); //Does not work - goto first row
//this->setCurrentIndes(Index); //Does not work - goto index x=0 y=0
}
Thank you with advance for all your responses.
Solved!
The problem is that the model is asynchronous. So reads the data in another thread. When I tried to set the index to the first line, still basically did not exist. The solution is, of course, to wait for loading the thread. At this point signal directoryLoaded(QString) is send. As a result, it is necessary to wait for the signal, and only then set index.
connect(myModel, SIGNAL(directoryLoaded(QString)), this, SLOT(onLoaded()));
void mFileBoxWidget::onLoaded()
{
QModelIndex index = myModel->index(myModel->rootPath());
this->setCurrentIndex(index.child(0, index.column()));
}
You shouldn't name your member variable model. QTableView has function model(), the compiler thinks this->model is meant to be this->model(), therefore you get the error you mentioned.
This is untested code, but I think something like this should work:
QModelIndex firstRow = QTableView::model()->index(0, 0);
QTableView::selectionModel()->select(firstRow,
QItemSelectionModel::ClearAndSelect |
QItemSelectionModel::Rows );
EDIT: (2013-06-19 06:12:58 UTC)
A simple (and ugly) workaround that worked so far for me is triggering a call to m_tableView->selectRow(0); from a QTimer.
Here's the sample code:
Header:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
class QTableView;
class QFileSystemModel;
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = 0);
~MainWidget();
private:
void layoutWidgets();
QFileSystemModel *m_model;
QTableView *m_tableView;
private slots:
void selectFirstRow();
// for debugging only
void selectionChanged();
};
#endif // MAINWIDGET_H
Implementation:
#include "mainwidget.h"
#include <QTableView>
#include <QHBoxLayout>
#include <QFileSystemModel>
#include <QHeaderView>
#include <QTimer>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
m_tableView = new QTableView(this);
m_model = new QFileSystemModel(this);
m_model->setReadOnly(true);
m_tableView->setModel(m_model);
m_tableView->setRootIndex(m_model->setRootPath(QDir::homePath()));
m_tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_tableView->setShowGrid(false);
m_tableView->verticalHeader()->setVisible(false);
m_tableView->setColumnWidth(1,70);
m_tableView->setColumnWidth(2,70);
m_tableView-> setColumnWidth(3,110);
m_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
//m_tableView->->setSectionResizeMode(0, QHeaderView::Stretch ); // Qt 5?
layoutWidgets();
connect(m_tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged()) );
// This works
QTimer::singleShot(1000, this, SLOT(selectFirstRow()));
// Direct invocation - doesn't works
// selectFirstRow();
}
void MainWidget::layoutWidgets()
{
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(m_tableView);
setLayout(mainLayout);
setFixedSize(500,500);
}
void MainWidget::selectFirstRow()
{
m_tableView->selectRow(0);
}
void MainWidget::selectionChanged()
{
qDebug("Selection changed");
}
MainWidget::~MainWidget()
{}
The weird thing is, if QTimer::singleShot() needs to be triggered with a delay of at least ~25 ms., otherwise it wouldn't work in my system.
Here's the alternative, subclassing QTableView:
#include "mytableview.h"
#include <QFileSystemModel>
#include <QHeaderView>
#include <QTimer>
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
QFileSystemModel *myModel = new QFileSystemModel;
setModel(myModel);
setRootIndex(myModel->setRootPath(QDir::homePath()));
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setShowGrid(false);
verticalHeader()->setVisible(false);
//installEventFilter(this);
myModel->setReadOnly(true);
//setSectionResizeMode(0, QHeaderView::Stretch ); // Qt 5
setColumnWidth(1,70);
setColumnWidth(2,70);
setColumnWidth(3,110);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
QTimer::singleShot(100, this, SLOT(selectFirstRow()));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged()));
}
void MyTableView::selectFirstRow()
{
// qDebug("Selecting first row");
// QModelIndex firstRow = QTableView::model()->index(0,0);
// if(firstRow.isValid()){
// selectionModel()->select(firstRow, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
// }else{
// qDebug("Invalid index");
// }
selectRow(0);
}
void MyTableView::selectionChanged()
{
qDebug("Selection changed.");
}
I face a very serious situation. By writing this question I hope that really professionals will express their opinion regarding to the problem I am going to describe. I have reported a bug in https://bugreports.qt.io/ :
I have created QPropertyAnimation for maximumWidth property of QTextEdit and it does not work (it immediately changes state from starting state to the end state), though it works for minimumWidth property.
Please see the attached code.
And have attached .h and .cpp files. See those files here (files are named new.h and new.cpp).
And I got the follwing response:
MaximumWidth is not the property you want to animate. It holds the maximum width that the widget can have, it's related to layouting and so on. Changing the maximumWidth (as well as the minimumWidth) does not necessarily trigger a relayout and repaint. You should animate the size.
Please explain me whether it is a bug or no? Please tell me how the minimumWith property is being animated but when it concerns to the maximumWidth property, then I should not work and that is OK? I just don't get their point... Please explain.
P.S. I have written this code because I wanted to close by animation the right QTextEdit and be sure that when I resize the main window, where the button and two QTextEdit are, the closed QTextEdit does not being restored.
Did you check the actual value of maximumWidth? You don't seem to set a specific maximumWidth in your code.
The default value for maximumWidth is 16777215, and you set a duration of 1 msec. for the closing animation. Fading from 16777215 to 3 in 1 msec. would look like "instant", I guess.
I don't think it is a bug; I'd call it "undefined behavior". That means that if you try to animate minimumWidth, nobody can tell you for sure what is supposed to happen, and maybe the code has some optimizations or corner cases where sometimes it works, others it doesn't.
Anyway, minimumWidth and maximumWidth aren't supposed to be used to define what the size of a QWidget is, only what it must not exceed; i.e. they weren't designed to do what you are trying to do, so it can be called a bug. If you want to animate, you have to use a deterministic approach, which in this case is using the geometry property.
This is not a bug, the response you got from the bug report pretty well explains the problem with your code and a solution.
Dear Sofahamster I have changed my code to the code below and it works fine. Thanks for your hint!
Header file
class MyWidget : public QWidget
{
Q_OBJECT
QTextEdit *m_textEditor1;
QTextEdit *m_textEditor2;
QPushButton *m_pushButton;
QHBoxLayout *m_layout;
QVBoxLayout *m_buttonLayout;
int m_deltaX;
bool m_isClosed;
public:
MyWidget(QWidget * parent = 0);
~MyWidget(){}
void resizeEvent( QResizeEvent * event );
private slots:
void closeOrOpenTextEdit2(bool isClosing);
};
Source file
MyWidget::MyWidget(QWidget * parent):QWidget(parent),m_deltaX(0)
{
m_pushButton = new QPushButton(this);
m_pushButton->setText(">");
m_pushButton->setCheckable(true);
m_pushButton->setFixedSize(16,16);
connect(m_pushButton, SIGNAL(clicked(bool)), this, SLOT(closeOrOpenTextEdit2(bool)));
m_textEditor1 = new QTextEdit(this);
m_textEditor1->setText("AAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAA AAAAAAAAAAA AAAAAAAAAAA AA");
m_textEditor2 = new QTextEdit(this);
m_buttonLayout = new QVBoxLayout();
m_buttonLayout->addWidget(m_pushButton);
m_buttonLayout->addItem( new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding) );
m_layout = new QHBoxLayout;
m_layout->addWidget(m_textEditor1, 10);
m_layout->addSpacing(15);
m_layout->addLayout(m_buttonLayout);
m_layout->setSpacing(0);
m_layout->addWidget(m_textEditor2, 4);
setLayout(m_layout);
resize(800,500);
}
void MyWidget::closeOrOpenTextEdit2(bool isClosing)
{
m_isClosed = isClosing;
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "maximumWidth");
if(isClosing) //close the second textEdit
{
m_textEditor2->setMaximumWidth(m_textEditor2->width());
int textEdit2_start = m_textEditor2->maximumWidth();
m_deltaX = textEdit2_start;
int textEdit2_end = 3;
animation1->setDuration(500);
animation1->setStartValue(textEdit2_start);
animation1->setEndValue(textEdit2_end);
m_pushButton->setText("<");
}
else //open
{
int textEdit2_start = m_textEditor2->maximumWidth();
int textEdit2_end = m_deltaX;
animation1->setDuration(500);
animation1->setStartValue(textEdit2_start);
animation1->setEndValue(textEdit2_end);
m_pushButton->setText(">");
}
animation1->start();
}
void MyWidget::resizeEvent( QResizeEvent * event )
{
if(!m_isClosed)
m_textEditor2->setMaximumWidth( QWIDGETSIZE_MAX );
}