So, I've this code below:
foreach (QLineSeries* series, lineSeriesMap.values())
{
// ...
}
And I will modify series objects in this loop and I don't want to modify the original one, but create a new edited one. I'm extremely new to C++ and Qt so, I want something as the Java code below:
QLineSeries editedSeries = new QLineSeries(series);
I'm deleting elements, editing and re-ordering them from series by the way. But, as I said I need them both.
EDIT:
I've tried your answers but best way I believe is putting the code. This is a project made by some co-worker who changed jobs so its not my code, as i said I dont know C++.
chartwidget.h
void fillAreaSeries();
//...
QHash<QString,QLineSeries*> lineSeriesEntersMap;
QHash<QString,QLineSeries*> lineSeriesExitsMap;
chartwidget.cpp
void ChartWidget::fillAreaSeries() {
foreach (QLineSeries* seriesEnter, lineSeriesEntersMap.values())
{
if (lineSeriesExitsMap.contains(seriesEnter->name())) {
QLineSeries* seriesExit = lineSeriesExitsMap.value(seriesEnter->name());
if (!((seriesEnter->points().size() == 1) && (seriesExit->points().size() == 1))) {
for(int i = seriesEnter->points().size() - 1; i > 0; i--)
{
if (seriesEnter->points().at(i - 1).y() > seriesEnter->points().at(i).y())
{
seriesEnter->removePoints(i, 1);
}
}
for (int i = seriesExit->points().size() - 1; i > 0; i--)
{
if (seriesExit->points().at(i - 1).y() < seriesExit->points().at(i).y())
{
seriesExit->removePoints(i-1, 1);
}
}
QVector<QPointF> editPoints = seriesExit->pointsVector();
std::sort(editPoints.begin(),editPoints.end(), [] (const QPointF & p1, const QPointF & p2)
{
return p1.y() < p2.y();
});
seriesExit->replace(editPoints);
qDebug() << "__Swap:__";
qDebug() << seriesEnter->points().at(0).y();
qDebug() << seriesExit->points().at(0).y();
qDebug() << seriesEnter->points().at(1).y();
qDebug() << seriesExit->points().at(1).y();
QAreaSeries* series = new QAreaSeries(seriesEnter, seriesExit);
series->setName(seriesEnter->name());
series->setOpacity(0.50);
series->setPen(Qt::NoPen);
series->setPointLabelsFormat(seriesEnter->name().split("-").at(0));
areaSeriesMap.insert(series->name(), series);
}
}
}
}
Edit 3:
So, QLineSeries contains QPointF list. I've the code below:
foreach (QLineSeries* seriesEnter, lineSeriesEntersMap.values())
{
QLineSeries* entersToBeEdited = new QLineSeries(chart);
entersToBeEdited->setName(seriesEnter->name());
entersToBeEdited->points().append(seriesEnter->points());
//...
append doesnt work and returns 0 points. But I can set a name. I also tried appending by looping through items and adding it by
entersToBeEdited->points().push_back(seriesEnter->points().at(i));
and still nothing. I also tried << and += but no luck.
Looking at the class definition of QLineSeries, I don't see any simple way to copy your instance in order to duplicate it.
Thus you will have first to create a new instance :
QLineSeries editedSeries;
and manually copy the content of your original series in it.
editedSeries.append(originalSeries.points());
As you cannot modify the data once it is in the QLineSeries object, I would recommend to subclass QLineSeries or modify the QList<QPointF> obtained via originalSeries.points() before adding it to your new chart.
QLineSeries is not copyable, so you can't do what you want by modifying a copy. You will need to create a new QLineSeries from scratch.
Related
So I have reached my ceiling of knowledge when it comes to Qt and C++ in general I guess. I am creating check boxes in a QScrollArea based off the input from a QComboBox. Depending on the value selected in the QComboBox, a specific number of check boxes are created. Once I created those check boxes, I am having a problem understanding how to interact (in my case, simply check to see if they are checked or not) with them outside of the function they are being created and called in. I know how to work with them if the buttons were static, but since the check boxes are dynamic (is that the right word?) and can change, I don't know what to do. Below is a little snippet of code on how the check boxes are created. If I now want to simply check if any of the boxes are checked, how do I do that. Can I "return" or "call" the created check boxes in another function somehow? I know I'll simply need to loop through the array and check, I just simply don't know how to get the array of check boxes into another function or how to return them in the function below.
Thanks for the help!
void MyProgram::create_checkboxes(QString opnum)
{
QWidget* MDAcheckboxes = new QWidget(ui->MDA);
QVBoxLayout* MDAlayout = new QVBoxLayout(MDAcheckboxes);
QCheckBox *MDAmycheckBox[9];
QList<QString> boxes;
if (opnum == "640")
{
boxes << "16-1" << "16-2";
for (int i = 0; i < 2; i++)
{
MDAmycheckBox[i] = new QCheckBox(MDAcheckboxes);
MDAmycheckBox[i]->setText(boxes[i]);
MDAlayout->addWidget(MDAmycheckBox[i]);
}
ui->MDA->setWidget(MDAcheckboxes);
}
else if (opnum == "645")
{
boxes << "13-01"<<"13-2"<<"13-3"<<"13-4"<<"13-5";
for (int i = 0; i < 5; i++)
{
MDAmycheckBox[i] = new QCheckBox(MDAcheckboxes);
MDAmycheckBox[i]->setText(boxes[i]);
MDAlayout->addWidget(MDAmycheckBox[i]);
}
ui->MDA->setWidget(MDAcheckboxes);
}
else if (opnum == "650")
{
boxes << "13-6"<<"13-7"<<"13-8"<<"13-9"<<"13-10"<<"13-11"<<"13-12"<<"13-13"<<"13-14";
for (int i = 0; i < 9; i++)
{
MDAmycheckBox[i] = new QCheckBox(MDAcheckboxes);
MDAmycheckBox[i]->setText(boxes[i]);
MDAlayout->addWidget(MDAmycheckBox[i]);
}
ui->MDA->setWidget(MDAcheckboxes);
}
}
All your checkBoxes should have a parent. In this case you will be able to find it with findChildren. It also can be done without groupBox if you sure that app has no any other checkboxes and findChildren will not return you checkboxes which you don't need.
Try this:
QList<QCheckBox *> allButtons = ui->groupBox->findChildren<QCheckBox *>();
qDebug() <<allButtons.size();
for(int i = 0; i < allButtons.size(); ++i)
{
if(allButtons.at(i)->isChecked())
qDebug() << "Use" << allButtons.at(i)->text()<< i;//or what you need
}
In general case:
QList<QCheckBox*> allButtons = parentOfCheckBoxes->findChildren<QCheckBox *>();
Moreover findChildren allows you to find children with special objectName which can be useful in some cases. Note that you can set the same objectName to the different objects.
http://qt-project.org/doc/qt-5/qobject.html#findChildren
I'm creating a new instance of QTableWidgetItem for each row the user may add, and adding it to a QVector of QTableWidgetItems.
I'd like to do soemthing like the following to name each instance in the following iteration with the row number included in the instance name:
QVector<QCheckBox> *checkBox_array;
for(int r=0;r<user_input;r++)
{
ui->tableWidget->insertRow(r);
*checkBox%1.arg(r) = new QCheckBox; //create an instance "checkBox1" here
checkBox_array->pushBack(checkBox%1.arg(r))
}
or something like the following, which does not compile in its current state:
for(int r=0;r<7;r++)
{
ui->tableWidget->insertRow(r);
checkBox_array->push_back();
checkBox_array[r] = new QCheckBox;
ui->tableWidget->setCellWidget(r,2,checkBox_array[r]);
}
is this possible? How can I work around this issue? All I need is to get the new widgets into the array without having to name them explicitly. Thanks in advance!
Thanks in advance.
Try something like this:
for(int r=0;r<7;r++)
{
ui->tableWidget->insertRow(r);
ui->tableWidget->setCellWidget(r,2,new QCheckBox(QString("checkBox%1").arg(r)));
}
It creates some widgets.
When you want change something in this widget or get data then use cellWidget() method, but don't forget cast it! For example:
for(int r=0;r<7;r++)
{
QCheckBox* curBox = qobject_cast<QCheckBox*>(ui->tableWidget->cellWidget(r,2));
if(curBox)
{
qDebug() << curBox->text() << curBox->isChecked();
curBox->setText("This is new Text");
}
else
qDebug() << "fail";
}
Im trying to make the code which reads xml files and deserialize various qt controls from this xml, and im doing this using QDomDocument, and i want to get the QLlist from my deserealization method. And im having a bit of troubles, here is some code of the template class (.h) file:
QList<T*> deserialize(QIODevice *input)
{
QList<T*> objects = QList<T*>();
if(_deserializeObject(input, objects))
return objects;
}
bool _deserializeObjects(QIODevice* input, QList<QObject*>& list);
and my .cpp file with deserialize method, here im reading the control tags from file:
bool Serializer::_deserializeObjects(QIODevice* input, QList<QObject *> &objects)
{
QDomDocument doc;
if (!doc.setContent(input))
return false;
QDomElement root= doc.documentElement();
for(int j = 0; j < root.childNodes().length();j++)
{
QObject* object;
qDebug() << root.tagName();
if(root.tagName().contains("QGroupBox")) // <------- Here i need to determine which control i need to process.
{
????
}
qDebug () << object->metaObject()->className();
qDebug() << object->metaObject()->propertyCount();
for(int i = 0; i < object->metaObject()->propertyCount(); i++)
{
object->metaObject()->cast()
QMetaProperty prop = object->metaObject()->property(i);
QString propName = prop.name();
if(propName == "objectName")
continue;
QDomNodeList nodeList = root.elementsByTagName(propName);
if(nodeList.length() < 1)
continue;
QDomNode node = nodeList.at(0);
QVariant value = object->property(propName.toLatin1().data());
QString v = node.toElement().text();
if(propName == "x")
{
x = v.toInt();
}
else if(propName == "y")
{
y = v.toInt();
}
else if(propName == "width")
{
width = v.toInt();
}
else if(propName == "height")
{
height = v.toInt();
}
if(propName == "geometry")
{
continue;
}
object->setProperty(propName.toLatin1().data(), QVariant(v));
}
object->setProperty("geometry",QVariant(QRect(x,y,width,height)));
objects.push_back(object);
}
return true;
}
In this part
if(root.tagName().contains("QGroupBox")) // <------- Here i need to determine which control i need to process.
{
????
}
qDebug () << object->metaObject()->className();
qDebug() << object->metaObject()->propertyCount();
for(int i = 0; i < object->metaObject()->propertyCount(); i++)
{
...
}
I want to actually somehow get the type of the control by name, so the question is, can i cast QGroupBox to QObject saving the QGroupBox properties so QObject metaObject class name would be QGroupBox, so i can pass all this properties? Because i don't want to make the loops for each control type. Also i when i got the result like so:
QList<QObject *> ds = s.deserialize<Object>((QIODevice*)&f);
Can i then just pass all QObjects in a loop and using QMetaObject class name and using qobject_cast cast each object to QPushButton,QLabel etc.?
QGroupBox is a subclass of QObject; therefore every QGroupBox is also a QObject, so you can treat it as one whenever you like. An explicit cast isn't necessary.
Iterating over all the diffent objects-derived-from-QObject in a loop will do what you want, provided that the methods you call on them are virtual methods (which they presumably will be -- in particular, QObject::metaObject() is a virtual method, so your loop will get the appropriate QMetaObject returned even if it is calling them method through a QObject pointer).
(As an aside, the annoying part of the process will probably be the part where you have read the name of the object's type from the XML and now need to instantiate an object of that type. AFAIK there is no good automatic way to do that in C++, so the best you can do is a factory function containing a giant switch statement with a separate case for every type you might want to instantiate)
Alternatively, use a right tool for a right job. Chances are that what you are really building here is some XML thing for defining widget layouts etc. Qt already has a tool for that, the Qt Designer which uses an XML format for the UI definitions and a C++ code generator for actually producing a C++ code during compile time.
I have a QGridLayout where I add my custom QWidgets.
When I try to delete all of them they are supposedly removed from the layout (as the function layout.count() returns 0) but they are still shown in the interface and I can interact with them.
Here you have the way I add widgets:
void MyClass::addCustomWidget(CustomWidget *_widget, int r, int c)
{
layout->addWidget(_widget, r, c);
_widget->show();
}
And here the way I delete them:
void MyClass::clearLayout()
{
qDebug() << "Layout count before clearing it: " << layout->count();
int count = layout->count();
int colums = layout->columnCount();
int rows = layout->rowCount();
int i=0;
for(int j=0; j<rows; j++)
{
for(int k=0; k<colums && i<count; k++)
{
i++;
qDebug() << "Removing item at: " << j << "," << k;
QLayoutItem* item = layout->itemAtPosition(j, k);
if (!item) continue;
if (item->widget()) {
layout->removeWidget(item->widget());
} else {
layout->removeItem(item);
}
qDebug() << "Removed!";
}
}
qDebug() << "Layout count after clearing it: " << layout->count();
}
Any kind of help or tip to delete items/widgets correctly from a QGridLayout?
P.D. : I have seen on the internet that a lot of people deletes the widget directly (delete _widget) after removing them from the layout. In my case it is not possible as I need to mantain that widgets in memory.
You can also use deleteLater() to avoid issue with maintaining child
count during iterations:
for (int i = 0; i < gridLayout.count(); i++)
{
gridLayout.itemAt(i)->widget()->deleteLater();
}
Just to be clear. You didn't "delete" the widgets. You only removed them from layout. Removing from layout means only that widget will be no more managed (resized/positioned) by this layout BUT it doesn't mean that widget will be "deleted" (in C++ way). Also widget won't be magically hidden. Your widget after removing from layout still leaves in widget it was created / managed in. So owner of this layout still has this widget as child (visible child).
You have to
hide widget or if you're sure it will not be used anymore
delete widget with "delete" keyword
Also you don't need to call removeWidget(item->widget()); removeItem(item) will be enough for all layout items (even those with widget inside)
Try
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0);
It is supposed to be safe. If for any reasons it doesn't work, you can use a collection of widgets or layoutitems, which is updated every time you add a widget. Then to delete you loop on the collection and remove each element from the layout.
Header:
class Grid : QGridLayout
{
public:
Grid(QWidget* parent);
void addElement(QWidget* element, int x, int y);
void delElement(int x, int y);
void resize(int width, int height);
void clear();
protected:
QSize size;
};
void Grid::clear(){
for(int i = 0;i<size.width();i++){
for(int j = 0;j<size.height();j++){
QLayoutItem* item = takeAt(i*size.width() + j);
if(item != NULL) delete item;
}
}
}
None of these answers worked for me. In my situation, I have several object each with their own QChartView. The idea is that the user selects which object they want to view and a common area central in the main window is updated with the QChartView of the user-selected object. This should have been straightforward, simply remove the widget from the chart area, add a new one. What ended up working for me was this:
while( QLayoutItem* item = ui->mPlotArea->layout()->takeAt(0) )
{
item->widget()->setVisible(false);
ui->mPlotArea->layout()->removeItem(item);
}
ui->mPlotArea->layout()->addWidget( pv );
pv->setVisible(true);
Where mPlotArea is a QFrame and pv is a derived class of QChartView. I can't explain why the other answers did not worked, but I spent a couple of hours trying different widgets and different ways of removing without deleting, organizing, etc...
I need my QT application to create a table and copy this table into the clipboard, so that it can be pasted as table into libreoffice Writer or MS Word later.
My first approach was to create html code for the table and insert it into the clipboard with
QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/html", html.toUtf8());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);
This approach didn't work. When pasting, the table cells where just appended to each other and inserted without formatting.
My second approach using RTF:
QTextDocument rtfDocument;
rtfDocument.setHtml(html);
But I found no way to copy this QTextDocument into the clipboard. Is there any?
If I could get the RTF code out of the QTextDocument, I could use a way like
QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/rtf", rtfDocument.getCode());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);
But I also didn't find a function returning the rtf code.
edit:
With the last code box above I have a working way to copy rtf code into the clipboard. So any solution that can create RTF code representing a table would solve my problem.
I'm not sure what the source of your data is, but here is code we used to subclass the normal QTableView to make it copy-able. Some of the code has been cut out, but you can get the basic idea. RTF/HTML is overkill--all the spreadsheets accept good ol' CSV.
Of course, this answer won't help at all if you require formatting. I wasn't clear from your question if that was a requirement or not.
// Escapes a string according to RFC-4180 specification.
static QString csvEscape(const QString &value) {
if (value.contains(QRegExp(QLatin1String("[\"\\n\\r,]")))) {
QString escaped(value);
escaped.replace(QLatin1String("\""), QLatin1String("\"\""));
return QString::fromLatin1("\"%1\"").arg(escaped);
} else {
return value;
}
}
void ClipboardAwareTableView::Copy() const {
QModelIndexList indexes = selectedIndexes();
Q_ASSERT(!indexes.isEmpty());
if(indexes.isEmpty()) {
return;
}
// The default sort is by rows then columns. This is what we want.
qSort(indexes);
// Remember the mapping between model columns and visible columns. This is
// local instead of an instance member because it would need to be invalidated
// any time a column is added, removed, or moved. The minor performance hit
// is worth the simplicity.
QHash<int, int> map_cache;
// Before we start exporting text, we have to know the index of the left-
// most column in our selection range so we can add the appropriate number
// of column separators.
int minimum_column = GetViewColumnIndex(indexes.first().column(), &map_cache);
for (int i = 1; i < indexes.size(); ++i) {
minimum_column =
qMin(minimum_column,
GetViewColumnIndex(indexes.at(i).column(), &map_cache));
}
// Keep track of the previous index so that we know if we need a new line and
// how many column separators to insert. We start with an invalid index.
QModelIndex previous;
QString text;
for (int i = 0; i < indexes.size(); ++i) {
QModelIndex current = indexes.at(i);
// Do we need to add a new line character?
if (previous.isValid() && current.row() != previous.row()) {
text.append(QLatin1String("\n"));
}
// Are we on a new line?
if (!previous.isValid() || current.row() != previous.row()) {
// Add enough separators to get from the minimum to the current column.
text.append(QString::fromLatin1(",")
.repeated(GetViewColumnIndex(current.column(), &map_cache) -
minimum_column));
} else {
// Add enough separators to get from the previous to the current column.
text.append(QString::fromLatin1(",")
.repeated(GetViewColumnIndex(current.column(), &map_cache) -
GetViewColumnIndex(previous.column(), &map_cache)));
}
// Append the text. If the column delegate is a QStyledItemDelegate, we use
// the display text.
QStyledItemDelegate *delegate =
qobject_cast<QStyledItemDelegate*>(
itemDelegateForColumn(current.column()));
if (delegate) {
text.append(csvEscape(delegate->displayText(current.data(), QLocale())));
} else {
text.append(csvEscape(current.data().toString()));
}
previous = current;
}
qApp->clipboard()->setText(text);
}
int ClipboardAwareTableView::GetViewColumnIndex(
int model_column_index,
QHash<int, int> *cached_mappings) const {
if (cached_mappings->contains(model_column_index)) {
return cached_mappings->value(model_column_index);
}
int view_index = 0;
for (int i = 0; i < model()->columnCount(); ++i) {
if (model_column_index == i) {
cached_mappings->insert(model_column_index, view_index);
return view_index;
} else if (!isColumnHidden(i)) {
++view_index;
}
}
throw std::invalid_argument("model_column_index was out of range.");
}
void ClipboardAwareTableView::keyPressEvent(QKeyEvent *event) {
if (event->matches(QKeySequence::Copy) && !selectedIndexes().isEmpty()) {
Copy();
event->accept();
return; // The base class implementation will overwrite the clipboard.
}
event->ignore();
QTableView::keyPressEvent(event);
}
You could try using QTextDocument::toHtml() and set the mime type to text/html
I wrote in gedit 1[tab space]2[tab space]3\n4[tab space]5[tab space]6 and copied it to spreadsheet and it worked. So, I think if you use "\t" for separating cells in rows and "\n" for separating rows, it will work.