我在装有 OS-X 10.8.5 的 Mac 上运行 Qt 5.1.1 和 QtCreator 2.8.1。我有一个管理 ImageData 对象的 QAbstractListModel。在 main.cpp 中注册 ImageProvider 后,我可以使用 GridView 加载图像并在 QML 中显示它们。
接下来我在 View 中选择单个图像,例如,下面显示了几个带有橙色边框的选定图像:
然后是 C++ 模型函数:deleteSelected(),产生预期的结果:
但是,当我试图通过捕获其中一个角来调整窗口大小时,我遇到了崩溃。堆栈跟踪显示:异常类型:EXC_CRASH (SIGABRT) 并且我收到 Qt 错误:
ASSERT failure in QList<T>::at: "index out of range", … QtCore/qlist.h, line 452
The program has unexpectedly finished.
所以也许我不正确地删除了模型项或未能将更改通知模型,但我认为开始和结束 RemoveRows 发出了正确的信号来处理同步?毫无疑问,我还遗漏了其他内容。
我还调用了开始和结束 ResetModel,这可以防止应用程序在调整大小后崩溃,但在这种情况下,附加到模型的任何其他 View 都会恢复显示所有原始项目。
我已经搜索了解决方案,尝试了很多代码实验,并研究了发布的代码 here , here , here ,以及其他几个地方。
似乎无法让它正常工作,有什么建议吗?谢谢!
...
// Other Classes:
#include "datamodelcontroller.h"
#include "imageprovider.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
// Initialize and register model:
DataModelController model;
QQmlContext *context = engine.rootContext();
context->setContextProperty("DataModelFromContext", &model);
// Register image provider for each "role" to the model:
ImageProvider *imageProvider = new ImageProvider(&model);
engine.addImageProvider(QLatin1String("provider"), imageProvider);
// Get the main.qml path from a relative path:
PathResolver pathObject("qml/DebugProject/main.qml");
QString qmlPath = pathObject.pathResult;
// Create Component:
QQmlComponent *component = new QQmlComponent(&engine);
component->loadUrl(QUrl(qmlPath));
// Display Window:
QObject *topLevel = component->create();
QQuickWindow *window = qobject_cast<QQuickWindow*>(topLevel);
QSurfaceFormat surfaceFormat = window->requestedFormat();
window->setFormat(surfaceFormat);
window->show();
return app.exec();
}
class DataModelController : public QAbstractListModel
{
Q_OBJECT
public:
explicit DataModelController(QObject *parent = 0);
enum DataRoles {
FileNameRole = Qt::UserRole + 1,
ImageRole
};
// QAbstractListModel:
void addData(ImageData*& imageObj);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
QHash<int, QByteArray> roleNames() const;
// Get the Model Data:
QList<ImageData*> getModelData();
signals:
void imageSelectedStateChange(bool selectedImageStateValue);
public slots:
void testLoadData();
void removeData(int index);
void setSelected(int index);
bool isSelected(int index);
void toggleSelected(int index);
void deleteSelected();
int count();
private:
// Model Data here:
QList<ImageData*> _modelData;
};
DataModelController::DataModelController(QObject *parent) : QAbstractListModel(parent)
{
}
void DataModelController::addData(ImageData*& imageObj) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
_modelData << imageObj; // for QList<>
endInsertRows();
}
int DataModelController::rowCount(const QModelIndex &) const {
return _modelData.size();
}
// Slot:
int DataModelController::count() {
return this->rowCount();
}
QVariant DataModelController::data(const QModelIndex & index, int role) const
{
if(!index.isValid())
return QVariant();
ImageData* imgObj = _modelData[index.row()];
if (role == FileNameRole) {
string imgFileName = imgObj->getFileName();
QString fileName(imgFileName.c_str());
return fileName;
}
if (role == ImageRole) {
QString url = QString::number(index.row());
return url;
}
return QVariant();
}
QHash<int, QByteArray> DataModelController::roleNames() const {
QHash<int, QByteArray> roles;
roles[FileNameRole] = "filename";
roles[ImageRole] = "thumbnail";
return roles;
}
QList<ImageData*> DataModelController::getModelData() {
return _modelData;
}
void DataModelController::testLoadData()
{
int width = 256, height = 256;
for (int i = 0; i < 5; i++) {
ostringstream digit;
digit<<i;
string imgPath("qml/DebugProject/TempImages/"+digit.str()+".jpg");
PathResolver path(imgPath.c_str());
QImage img(path.pathResult);
// Initialize an Image Object:
string fileName("file"+digit.str());
ImageData *image = new ImageData(fileName);
image->setData(NULL);
image->nX = width;
image->nY = height;
image->setThumbnailQImage(img, width, height);
image->setSelected(false);
this->addData(image);
}
}
void DataModelController::removeData(int index) {
cout << "deleting index: " << index << endl;
this->beginRemoveRows(QModelIndex(), index, index);
_modelData.removeAt(index);
// delete _modelData.takeAt(index); // tried this
this->endRemoveRows();
//this->beginResetModel();
//this->endResetModel();
}
void DataModelController::setSelected(int index) {
ImageData *imgObj = this->getModelData().at(index);
if (!imgObj->getState()) {
imgObj->setSelected(true);
emit imageSelectedStateChange(imgObj->getState());
}
}
bool DataModelController::isSelected(int index) {
ImageData *imgObj = this->getModelData().at(index);
return imgObj->getState();
}
void DataModelController::toggleSelected(int index) {
ImageData *imgObj = this->getModelData().at(index);
imgObj->setSelected(!imgObj->getState());
emit imageSelectedStateChange(imgObj->getState());
}
void DataModelController::deleteSelected() {
for (int i = this->rowCount()-1; i >= 0; i--) {
if (this->isSelected(i)) {
cout << i << ": state: " << this->isSelected(i) << endl;
this->removeData(i);
cout << this->rowCount();
}
}
}
class ImageData
{
public:
ImageData();
ImageData(string filename);
long nX, nY; // image width, height
void setFileName(string filename);
string getFileName() const;
void setSelected(bool state);
bool getState() const;
void setThumbnail(const int width, const int height);
void setThumbnailQImage(QImage &imgStart, int width, int height);
QImage getThumbnail() const;
void setData(float* data);
float* getData() const;
private:
string _fileName;
QImage _thumbnail;
float* _data;
bool _isSelected;
void normalizeAndScaleData(float*& dataVector);
void getMaxMinValues(float& datamax, float& datamin, float*& data, const int numPixels, bool verbose);
unsigned char* getByteArrayFromFloatArray(int bytesPerRow, float*& dataVector);
};
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.1
import QtQuick.Controls.Styles 1.0
import QtQuick.Layouts 1.0
ApplicationWindow {
id: mainAppWindow
width: 1024*1.5*0.5
height: 256
color: "gray"
property real imgScale: 0.5
Window {
id: gridViewMenu
width: 160
height: 128
opacity: 0.8
color: "black"
visible: true
x: 1024;
Column {
id: colButtons
x: 10;
y: 10;
spacing: 25
CustomButton {
id: deleteSelectedButton; text: "Delete Selected"
onClicked: {
DataModelFromContext.deleteSelected();
}
}
CustomButton {
id: testDataButton; text: "Load Test Images"
visible: true
onClicked: DataModelFromContext.testLoadData()
}
}
}
Rectangle {
id: mainRect
width: mainAppWindow.width*0.95
height: mainAppWindow.height*0.8
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
color: "transparent"
// GRIDVIEW is here:
Rectangle {
id: gridContainer
anchors.centerIn: parent
width: parent.width
height: parent.height*0.9
color: "transparent"
GridView {
property int itemWidth: mainRect.width*imgScale;
id: gridView
interactive: true
anchors.centerIn: parent
width: parent.width;
height: parent.height;
cellWidth: itemWidth/3
cellHeight: itemWidth/3
focus: true
model: DataModelFromContext
delegate: gridDelegate
Behavior on opacity {
NumberAnimation { duration: 500; easing.type: Easing.InOutQuad }
}
Keys.onPressed: {
if (event.key == Qt.Key_D) {
DataModelFromContext.deleteSelected()
}
}
} // end of gridView
// GRIDVIEW Delegate:
Component {
id: gridDelegate
Rectangle {
id: gridImageWrapper
width: gridView.cellWidth
height: gridView.cellHeight
color: "black"
Rectangle {
id: imageBorder
anchors.fill: parent
color: "transparent"
border.color: "green"
border.width: 1
z: 1
}
MouseArea {
id: selectImage
anchors.fill: parent
onClicked: {
// toggleSelected triggers the C++ signal: imageSelectedStateChange
DataModelFromContext.toggleSelected(index);
console.log(index + "; " + DataModelFromContext.count() )
}
}
Rectangle {
id: selectedImageBorder
anchors.fill: parent
color: "transparent"
border.color: "orange"
border.width: 2
opacity: 0
z: 2
Connections {
target: DataModelFromContext
onImageSelectedStateChange: {
selectedImageBorder.opacity = DataModelFromContext.isSelected(index);
}
}
}
Image {
property int itemWidth: mainRect.width*imgScale
id: frontIcon
anchors.centerIn: parent
source: "image://provider/" + thumbnail
smooth: true
visible: true
sourceSize.width: itemWidth/3;
sourceSize.height: itemWidth/3;
}
} // end of Grid Delegate Rectangle
} // end of Grid Delegate
} // end of gridContainer Rectangle
} // End: mainRect
} // End Main Application Window
#include "imagedata.h"
ImageData::ImageData()
{
}
ImageData::ImageData(string filename)
{
_fileName = filename;
}
void ImageData::setFileName(string filename) {
_fileName = filename;
}
string ImageData::getFileName() const {
return _fileName;
}
void ImageData::setSelected(bool state) {
_isSelected = state;
}
bool ImageData::getState() const {
return _isSelected;
}
QImage ImageData::getThumbnail() const {
return _thumbnail;
}
void ImageData::setThumbnailQImage(QImage &imgStart, int width, int height) {
QImage img;
//QImage *imgStart = new QImage(pix, nX, nY, bytesPerRow, QImage::Format_Indexed8);
img = QPixmap::fromImage(imgStart).scaled(width, height, Qt::KeepAspectRatio).toImage();
_thumbnail = img;
}
void ImageData::setData(float* data) {
_data = data;
}
float* ImageData::getData() const {
return _data;
}
// Function: getMaxMinValues
void ImageData::getMaxMinValues(float& datamax, float& datamin, float*& data,
const int numPixels, bool verbose) {
datamin = 1.0E30;
datamax = -1.0E30;
for (int pix = 0; pix < numPixels-1; pix++) {
if (data[pix] < datamin) {datamin = data[pix];}
if (data[pix] > datamax) {datamax = data[pix];}
}
if (verbose) {
std::cout << "Min and Max pixel values = " << datamin << "; " << datamax << std::endl;
}
}
// Function: normalizeAndScaleData
void ImageData::normalizeAndScaleData(float*& dataVector)
{
// ---- Find Max and Min Values:
float datamin, datamax;
this->getMaxMinValues(datamax, datamin, dataVector, nX*nY, false);
// Get average and standard deviation:
float avg = 0, sig = 0;
for (int px = 0; px < nX*nY; px++) {
avg += dataVector[px];
}
avg /= nX*nY;
for (int px = 0; px<nX*nY; px++) {
sig += powf(dataVector[px] - avg, 0.5);
//sig += powf(dataVector[px] - avg, 2.0);
}
sig = pow(sig/(nX*nY), 0.5);
int deviations = 5;
if (datamin < avg-deviations*sig) {datamin = avg-deviations*sig;}
if (datamax > avg+deviations*sig) {datamax = avg+deviations*sig;}
// ---- ScaleImage Data Here (linear scaling):
for (int px = 0; px<nX*nY; px++) {
dataVector[px] = (dataVector[px]-datamin)/(datamax - datamin);
}
}
unsigned char * ImageData::getByteArrayFromFloatArray(int bytesPerRow, float*& dataVector)
{
unsigned char *pix = new unsigned char[nX*nY];
for (int row = 0; row < nY; row++) {
for (int col = 0; col < nX; col++)
pix[row*bytesPerRow + col] = (unsigned char) 255*dataVector[row*nX + col];
}
return pix;
}
// Function: setThumbnail
void ImageData::setThumbnail(const int width, const int height) {
QImage img;
float *dataVector = this->getData();
if (dataVector == NULL) {
dataVector = new float[width*height];
} else {
normalizeAndScaleData(dataVector);
}
// Map to Byte Array (nX = cols of pixels in a row, nY = rows):
int bytesPerPixel = 1; // = sizeof(unsigned char)
int pixelsPerRow = nX;
int bytesPerRow = bytesPerPixel*pixelsPerRow;
unsigned char *pix = getByteArrayFromFloatArray(bytesPerRow, dataVector);
// Calculate the Thumbnail Image:
QImage *imgStart = new QImage(pix, nX, nY, bytesPerRow, QImage::Format_Indexed8);
img = QPixmap::fromImage(*imgStart).scaled(width, height, Qt::KeepAspectRatio).toImage();
_thumbnail = img;
}
编辑:在 userr1728854 下面关于范围检查的评论之后,我编辑了 DataModelController::data() 的第一部分,以检查这是否是问题所在。
我的代码现在看起来像(比修改原始代码更容易引用下面,而且我不想通过更改我发布的内容来更改我的问题的上下文):
QVariant DataModelController::data(const QModelIndex & index, int role) const
{
cout << "model index = " << index.row() << endl; // add this to help troubleshoot
if (!index.isValid() || index.row() > this->rowCount() || !this->modelContainsRow(index.row()) ) {
return QVariant();
}
因此,即使这不是向 data() 方法添加范围检查的最可靠方法,该行:
cout < "模型索引="<< index.row() << endl;
当我调整窗口大小时,至少应该打印:“index.row()”,但事实并非如此。所以调整窗口大小似乎并没有访问 data() 方法,程序仍然崩溃。
最佳答案
这是对我为我的代码实现的实际“修复”的说明。上面的讨论并没有真正深入到这些细节,所以我写在这里是希望它能在将来对其他人有所帮助。
首先,我理所当然地将赏金授予 userr1728854。尽管答案没有提供有关如何修复它的所有详细信息,但答案在确定我的代码崩溃的真正原因方面肯定是正确的,为此我很感激。
我还要感谢 Marek R 关于在 assert_x 函数处设置断点的评论,这帮助我跟踪图像提供者的崩溃,我打算在上面发布其代码但必须有忘了(图像提供者不是错误的根源,只是症状)。
问题的根源是我从模型中删除了对象(比如 2,3 行)。如下例所示,该模型保留了对一组不再存在的索引号 (4, 5) 的引用:
接下来,当我调整 QML 窗口大小时,模型向图像提供者提供不再存在的索引引用,从而出现段错误。即,我的代码没有传递新的相对索引 0, 1, 2, 3(对于剩余的项目),而是传递了旧的绝对索引:0, 1, 4, 5.
这是通过图像组件的 QML 代码中的“thumbnail”角色名实现的:
Image {
.
.
source: "image://provider/" + thumbnail
.
.
}
修复是一个简单的两步过程:
修复 (1):将“thumbnail”角色名替换为 QML 模型的 index 变量,当删除或添加行时,其值会自动更新以与模型项一对一匹配.所以最后我使用了这段代码:
Image {
.
.
source: "image://provider/" + index
.
.
}
修复 (2):我需要在图像提供程序中添加范围检查,以防止删除后的索引值(最初是删除的索引 = -1)传递到我的模型。我使用的范围检查很简单(可能有更好的,但目前还可以):
bool ImageProvider::isValidIndex(DataModelController* model, int index) {
int size = model->count();
if (index >= size || index < 0)
return false;
return true;
}
这有点啰嗦,但我希望它能帮助到一些人。
关于c++ - 从 QAbstractListModel 中删除项目后 QML 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19882814/
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit