In a QTableView, irrespective if there are entries or not. I want to show grid to it (considering its fixed size QTableView), but only for columns, not for rows.
You have to overwrite the paintEvent() method, in addition to setting the setShowGrid() to false.
#include <QApplication>
#include <QPainter>
#include <QStandardItemModel>
#include <QTableView>
#include <QHeaderView>
#include <QPaintEvent>
class TableView: public QTableView{
public:
using QTableView::QTableView;
protected:
void paintEvent(QPaintEvent *event){
QTableView::paintEvent(event);
if (horizontalHeader()->count() == 0 || verticalHeader()->count() == 0)
return;
QPainter painter(viewport());
QStyleOptionViewItem option;
option.init(this);
const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
const QColor gridColor = static_cast<QRgb>(gridHint);
const QPen gridPen = QPen(gridColor, 0, gridStyle());
painter.setPen(gridPen);
int w = horizontalHeader()->offset();
for(int i=0; i<horizontalHeader()->count(); ++i){
w += horizontalHeader()->sectionSize(i);
painter.drawLine(w-1, 0, w-1,viewport()->height());
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TableView view;
QStandardItemModel model(5, 5);
for(int i=0; i< model.rowCount(); ++i)
for(int j=0; j < model.columnCount(); ++j)
model.setItem(i, j, new QStandardItem(QString("%1-%2").arg(i).arg(j)));
view.setModel(&model);
view.setShowGrid(false);
view.show();
return a.exec();
}
Related
I have established a connection with the slot, the function is called, and the values via qDebug () are output, but the table does not change, what is wrong?
mainwindow.cpp
<pre>MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tableWidget->horizontalHeader()->hide();
ui->tableWidget->verticalHeader()->hide();
//Matrix *matr=new Matrix;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_updateTbl(int **mas, int n){
for(int i=0;i<n;i++){
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
for(int j=0;j<n;j++){
ui->tableWidget->insertColumn(ui->tableWidget->columnCount());
ui->tableWidget->setItem(i,j,new QTableWidgetItem( QString::number(mas[i][j])));
}
}
}
</pre>
main.cpp
<pre>
#include "mainwindow.h"
#include <QApplication>
#include "matrix.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
Matrix matr;
MainWindow mywnd;
QObject::connect(&matr,SIGNAL(updateTbl(int**,int)), &mywnd, SLOT(on_updateTbl(int**,int)));
matr.upTable();
return a.exec();
}
</pre>
matrix.cpp
<pre>
#include "matrix.h"
#include <QFile>
#include <QDebug>
#include <QString>
#include <QTextStream>
Matrix::Matrix()
{
QFile file("mas.txt");
this->mas=alloc_mem(n,n);
array_to_file(file,n,n);
fill_array(file,mas,n,n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if (mas[i][j]!=mas[n-j-1][n-i-1]) {
symmetrical=false;
break;
}
}
}
if(symmetrical){
for(int i=0;i<n;i++){
mas[i][i]=0;
mas[i][n-i-1]=0;
}
//print(mas,n,"Измененная");
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(mas[i][j]) vec.append(mas[i][j]);
}
}
}
int** Matrix::alloc_mem(int height, int width){
int** mas = new int*[height];
for (int i = 0; i < height; i++) {
mas[i] = new int[width];
}
return mas;
}
void Matrix::array_to_file(QFile &file, int height, int width){
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
int to_mas;
QTextStream out(&file);
for (int i = 0; i < height; i++){
for (int j = i + 1; j < width; j++){
to_mas=rand()%100-50;
out<<to_mas<<" ";
}
out<<1<<" ";
out<<"\n";
}
file.close();
}
void Matrix::fill_array(QFile &file, int **mas, int height, int width){
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
QTextStream in(&file);
QStringList matr = in.readAll().split("\n");
for (int i = 0; i < height; i++){
for (int j = 0; j < width-i; j++){
mas[i][j]=matr.at(i).split(" ").at(j).toInt();
mas[height-1-j][width-1-i]=mas[i][j];
}
}
file.close();
}
void Matrix::print(int **mas, int n, QString name){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
}
}
}
void Matrix::upTable(){
emit updateTbl(mas,n);
}
</pre>
mainwindow.h
<pre>
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
private slots:
void on_updateTbl(int**,int);
};
#endif // MAINWINDOW_H
</pre>
matrix.h
<pre>
#ifndef MATRIX_H
#define MATRIX_H
#include <QVector>
#include <QFile>
#include <QString>
#include <QObject>
class Matrix:public QObject
{
Q_OBJECT
public:
Matrix();
~Matrix(){}
//bool check_symmetrical(int **mas,int n);
void print_vector(QVector<int> vector);
int** alloc_mem(int height,int width);
void array_to_file(QFile& file,int height,int width);
void fill_array(QFile& file,int **mas,int height,int width);
void print(int** mas,int n,QString name);
void upTable();
private:
int n=10;
int **mas;
bool symmetrical=true;
QVector<int> vec;
signals:
void updateTbl(int**,int);
};
#endif // MATRIX_H
</pre>
If I copy this code into the constructor, then everything is fine and everything changes, but nothing works from the function
How do you want to see table with values when widget with updated table is not displayed?
MainWindow w;
w.show(); // <--- w is displayed, tableWidget is not modified in this widget
Matrix matr;
MainWindow mywnd; // <--- mywnd is not displayed
QObject::connect(&matr,SIGNAL(updateTbl(int**,int)), &mywnd, SLOT(on_updateTbl(int**,int)));
// mywnd is updated
matr.upTable();
add
mywnd.show();
you will see the other widget with updated content.
I am using C++ Qt5. Currently I have a QStandardItemModel being displayed as a QTreeView with multiple rows and columns. I am aware of using setStyleSheet(), but the issue there is that every row and column that the mouse hovers is highlighted.
I would only like specific rows of the first column to be highlighted, and then have a function called for each cell highlighted that I would then use to manipulate my game.
The solution for a personalized painting is to use a custom delegate, and to indicate which item should change the color a role should be used, in the following code I show an example:
#include <QApplication>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <QTreeView>
class StyledItemDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
protected:
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const{
QStyledItemDelegate::initStyleOption(option, index);
if(index.data(Qt::UserRole +1).toBool())
option->backgroundBrush = QBrush(Qt::red);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView w;
StyledItemDelegate delegate(&w);
w.setItemDelegate(&delegate);
QStandardItemModel model;
model.setColumnCount(4);
w.setModel(&model);
for(int i=0; i<4; i++){
auto it = new QStandardItem(QString::number(i));
model.appendRow(it);
for(int j=0; j<3; j++){
it->appendRow(new QStandardItem(QString("%1-%2").arg(i).arg(j)));
}
}
QObject::connect(&w, &QTreeView::clicked, [&](const QModelIndex & index){
bool last_state = model.data(index, Qt::UserRole +1).toBool();
model.setData(index, !last_state, Qt::UserRole +1);
});
w.expandAll();
w.show();
return a.exec();
}
I have subclassed QTreeWidget (called it ToolsSelectorWidget) and enabled reordering in it by overriding QTreeWidget::dropEvent()
void ToolsSelectorWidget::dropEvent(QDropEvent *event) {
QModelIndex droppedIndex = indexAt(event->pos());
if( !droppedIndex.isValid() || droppedIndex.parent().isValid()) {
return;
}
QTreeWidget::dropEvent(event);
}
Also, I am adding QWidgets (QPushButton, QLineEdit) to top level items of QTreeWidget:
ToolsSelectorWidget::ToolsSelectorWidget(QWidget *parent) : QTreeWidget(parent) {
header()->hide();
setSelectionMode(QAbstractItemView::SingleSelection);
setDragEnabled(true);
viewport()->setAcceptDrops(true);
setDropIndicatorShown(true);
setDragDropMode(QAbstractItemView::InternalMove);
for(int i=0; i<4; ++i) {
QTreeWidgetItem *part = new QTreeWidgetItem(this);
part->setFlags(part->flags() & Qt::ItemFlag((~Qt::ItemIsDropEnabled)));
setItemWidget(part, 0, new QLabel("Part" + QString::number(i) + " Preview", this));
setItemWidget(part, 1, new QLineEdit("Part" + QString::number(i) + " Name", this));
setItemWidget(part, 2, new QCheckBox("Part" + QString::number(i) + " Visible", this));
setItemWidget(part, 3, new QCheckBox("Part" + QString::number(i) + " Locked", this));
}
}
So now I have 4 top level items each containing 4 QWidgets. It's populating them fine, but when I rearrange them by drag and drop the QWidgets disappear and I end up having an empty row. What should I do to preserve them?
Before:
After Part2 has been moved and is under Part4, it's children have been preserved, but it's conents, which are QWidgets, are gone:
Why are widgets deleted?
When the drag and drop is performed, the data of the selected items is coded (roles and associated values) and saved in a QMimeData. When the drop is accepted, the source items are deleted and new items are created with the information stored in the QMimeData, inside the saved information there is no widgets information since this does not have relation with the model. And since the items are deleted, their widgets are also deleted.
To check it we can use the following example
#include <QApplication>
#include <QLabel>
#include <QTreeWidget>
#include <QDebug>
static void on_destroyed(){
qDebug()<<"destroyed";
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeWidget w;
w.setSelectionMode(QAbstractItemView::SingleSelection);
w.setDragEnabled(true);
w.viewport()->setAcceptDrops(true);
w.setDropIndicatorShown(true);
w.setDragDropMode(QAbstractItemView::InternalMove);
for(int i=0; i< 5; i++){
QTreeWidgetItem *it = new QTreeWidgetItem(&w);
QLabel *lbl = new QLabel(QString::number(i));
QObject::connect(lbl, &QObject::destroyed, on_destroyed);
w.setItemWidget(it, 0, lbl);
}
w.show();
return a.exec();
}
It shows that the widgets will emit the signal they destroy when you drag and drop the items.
Possible workaround:
One possible solution is to remove the widgets before accepting the drop and set them in the new items which I have not implemented.
I have explored another solution, it is to change the QTreeWidget for a QTreeView + QStandardItemModel. In the case of the QCheckBox, the checkboxes with the Qt::ItemIsUserCheckable flag are enabled, in the case of the QLineEdit a delegate will be used and to always be shown, the openPersistentEditor() method must be used.
#include <QApplication>
#include <QStandardItemModel>
#include <QTreeView>
#include <QHeaderView>
#include <QDropEvent>
#include <QStyledItemDelegate>
#include <QLineEdit>
class ToolsSelectorDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const{
QLineEdit *le = new QLineEdit(parent);
return le;
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const{
QRect r(option.rect);
r.adjust(2, 2, -2, -2);
editor->setGeometry(r);
}
};
class ToolsSelectorWidget: public QTreeView{
QStandardItemModel model;
public:
ToolsSelectorWidget(QWidget *parent=nullptr): QTreeView(parent){
setItemDelegate(new ToolsSelectorDelegate(this));
setModel(&model);
header()->hide();
setSelectionMode(QAbstractItemView::SingleSelection);
setDragEnabled(true);
viewport()->setAcceptDrops(true);
setDropIndicatorShown(true);
setDragDropMode(QAbstractItemView::InternalMove);
for(int i=0; i<4; ++i) {
QList<QStandardItem *> items;
for(const QString & text: {"Preview", "Name", "Visible", "Locked"}){
QStandardItem *it = new QStandardItem(QString("Part%1 %2").arg(i).arg(text));
it->setFlags(it->flags() & ~Qt::ItemIsDropEnabled & ~Qt::ItemIsEditable);
items.append(it);
if(text == "Visible" || text == "Locked"){
it->setFlags(it->flags() | Qt::ItemIsUserCheckable);
it->setCheckState(Qt::Unchecked);
}
else if (text == "Name") {
it->setFlags(it->flags() | Qt::ItemIsEditable);
}
}
for(const QString & children: {"The", "quick", "Brown", "fox", "jump...", "over", "the", "lazy", "dog"})
items.first()->appendRow(new QStandardItem(children));
model.invisibleRootItem()->appendRow(items);
for( int i = 0; i < model.rowCount(); ++i )
openPersistentEditor(model.index(i, 1));
}
}
protected:
void dropEvent(QDropEvent *event) {
QModelIndex droppedIndex = indexAt(event->pos());
if( !droppedIndex.isValid() || droppedIndex.parent().isValid())
return;
QTreeView::dropEvent(event);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ToolsSelectorWidget w;
w.show();
return a.exec();
}
Is there any function which I can use in order to pad my QImage object?
I have tried to search over the net unsuccefuly.
Thx in advance.
Here is my code:
#include "mainwindow.h"
#include <QApplication>
#include "qimage.h"
#include <QImage>
#include <QLabel>
#include <QColor>
#include "qcolor.h"
#include <Qdebug>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QGraphicsView>
int main(int argc, char *argv[])
{
printf("Init!");
qDebug() << "C++ Style Debug Message";
QApplication a(argc, argv);
QGraphicsScene scene;
QGraphicsView view(&scene);
int height;
int width;
unsigned char *p, *p_begin;
QImage img("C:\\Users\\Owner\\Pictures\\2013-09-26\\IMG_0836.JPG");
height = img.height();
width = img.width();
p = (unsigned char *)malloc(height * width * sizeof(unsigned char));
p_begin = p;
qDebug() << "Begin For Loop";
for (int row = 0; row < height; ++row)
{
for (int col = 0; col < width; ++col)
{
QColor clrCurrent( img.pixel( col, row ));
*p = (unsigned char)((clrCurrent.green() * 0.587) + (clrCurrent.blue() * 0.114) + (clrCurrent.red() * 0.299));
p++;
}
}
qDebug() << "Finished First Loop!";
p = p_begin;
for (int row = 0; row < height; ++row)
{
for (int col = 0; col < width; ++col)
{
QColor clrCurrent(img.pixel(col, row));
clrCurrent.setBlue((int)(*p));
clrCurrent.setGreen((int)(*p));
clrCurrent.setRed((int)(*p));
img.setPixel(col, row, clrCurrent.rgba());
p++;
}
}
QPixmap pixmap = QPixmap::fromImage(img);
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
scene.addItem(item);
view.show();
return a.exec();
}
Hi I edited my question, i added my code in order to give you more feeling of what is going on. any help would be appreciated.
The QImage offers no way to change its size. You need to create a new, larger image, erase its contents, start a QPainter on it, then draw the source image in the center of the new image. That way you'll have padding.
Below is a function that returns a padded version of the image, with a given color used for padding, and a test harness for it.
// https://github.com/KubaO/stackoverflown/tree/master/questions/image-pad-35968431
#include <QtGui>
template <typename T>
QImage paddedImage(const QImage & source, int padWidth, T padValue) {
QImage padded{source.width() + 2*padWidth, source.height() + 2*padWidth, source.format()};
padded.fill(padValue);
QPainter p{&padded};
p.drawImage(QPoint(padWidth, padWidth), source);
return padded;
}
int main() {
QImage source{64, 64, QImage::Format_ARGB32_Premultiplied};
source.fill(Qt::red);
auto padded = paddedImage(source, 16, Qt::blue);
padded.save("test.png");
}
Output:
I have an 2D array QPushButton, how can I get index of the button when user clicks on its? such as When user clicks on the button a[2][3] it will show (2,3) ?
The example looks like this:
Qt 4/5 Using Object Names
You can give your buttons unique object names. The names should ideally be valid C++ identifiers.
// https://github.com/KubaO/stackoverflown/tree/master/questions/button-grid-22641306
#include <QtGui>
#if QT_VERSION_MAJOR >= 5
#include <QtWidgets>
#endif
struct Display : QLabel {
Q_SLOT void onClicked() {
auto const elements = sender()->objectName().split('_');
auto const i = elements.at(1).toInt();
auto const j = elements.at(2).toInt();
setText(QString{"(%1,%2)"}.arg(i).arg(j));
}
Q_OBJECT
};
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QWidget window;
QGridLayout layout{&window};
QVarLengthArray<QPushButton, 12> buttons(12);
Display display;
const int rows = 4, columns = 3;
for (int i = 0; i < rows; ++ i)
for (int j = 0; j < columns; ++j) {
auto & button = buttons[i*columns+j];
button.setText(QString{"(%1,%2)"}.arg(i).arg(j));
button.setObjectName(QString{"buton_%1_%2"}.arg(i).arg(j));
layout.addWidget(&button, i, j);
display.connect(&button, SIGNAL(clicked()), SLOT(onClicked()));
}
layout.addWidget(&display, rows, 0, 1, columns);
window.show();
return a.exec();
}
#include "main.moc"
Qt 5 - Using Lambdas
In Qt 5 and C++11, you should use functors to generate custom slot for each button, on the fly. For example:
// https://github.com/KubaO/stackoverflown/tree/master/questions/button-grid-22641306
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QWidget window;
QGridLayout layout{&window};
QVarLengthArray<QPushButton, 12> buttons(12);
QLabel display;
const int rows = 4, columns = 3;
for (int i = 0; i < rows; ++ i)
for (int j = 0; j < columns; ++j) {
auto text = QStringLiteral("(%1,%2)").arg(i).arg(j);
auto & button = buttons[i*columns+j];
button.setText(text);
layout.addWidget(&button, i, j);
QObject::connect(&button, &QPushButton::clicked, [&display, text] {
display.setText(text);
});
}
layout.addWidget(&display, rows, 0, 1, columns);
window.show();
return a.exec();
}
Qt 4/5 - Using QSignalMapper
QSignalMapper is pretty much designed for what you want. It lets you map a QObject* to "something else", like a string. For example:
#include <QtGui>
#if QT_VERSION_MAJOR >= 5
#include <QtWidgets>
#endif
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QSignalMapper mapper;
QWidget window;
QGridLayout layout{&window};
QVarLengthArray<QPushButton, 12> buttons(12);
QLabel display;
const int rows = 4, columns = 3;
for (int i = 0; i < rows; ++ i)
for (int j = 0; j < columns; ++j) {
auto text = QString{"(%1,%2)"}.arg(i).arg(j);
auto & button = buttons[i*columns+j];
button.setText(text);
layout.addWidget(&button, i, j);
mapper.connect(&button, SIGNAL(clicked()), SLOT(map()));
mapper.setMapping(&button, text);
}
display.connect(&mapper, SIGNAL(mapped(QString)), SLOT(setText(QString)));
layout.addWidget(&display, rows, 0, 1, columns);
window.show();
return a.exec();
}
Qt 4/5 - Using the Property System
You can leverage the fact that a QWidget is a QObject. QObjects have a property system, so you can set each button's index as a property, and then retrieve it in the slot connected to the clicked() signal. For example:
#include <QtGui>
#if QT_VERSION_MAJOR >= 5
#include <QtWidgets>
#endif
const char kIndex[] = "index";
struct Display : QLabel {
Q_SLOT void onClicked() {
setText(sender()->property(kIndex).toString());
}
Q_OBJECT
};
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QWidget window;
QGridLayout layout{&window};
QVarLengthArray<QPushButton, 12> buttons(12);
Display display;
const int rows = 4, columns = 3;
for (int i = 0; i < rows; ++ i)
for (int j = 0; j < columns; ++j) {
auto index = QString{"(%1,%2)"}.arg(i).arg(j);
auto & button = buttons[i*columns+j];
button.setText(index);
button.setProperty(kIndex, index);
layout.addWidget(&button, i, j);
display.connect(&button, SIGNAL(clicked()), SLOT(onClicked()));
}
layout.addWidget(&display, rows, 0, 1, columns);
window.show();
return a.exec();
}
#include "main.moc"
in general, you would have to loop over the array and test the event target for equality with each element until you reach the correct index