I got a project that required drag & drop xml files from windows explorer into a TableModel, whatever I tried, that widget rejects(with the annoying block icon) the process and not any functions below are called.
i've tried the following:
reimplement QAbstarctTableView::flags to support drag & drop
remiplement QAbstractTableView::supportedDropActions.
remiplement QAbstractTableView::mimeTypes. return "text/uri-list", "text/plain", "application/xml", "text/xml". (some said that "text/uri-list" shall be enough.)
according to most posts i found in the internet, I shall be able to drag files into the TableView Widget now. That's not true for me. T_T
here's my code.
// set the flags to accept drop & drag
Qt::ItemFlags XMLFileModel::flags(const QModelIndex& index) const {
Qt::ItemFlags defaultFlags = Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}
.
// drop mime data
bool XMLFileModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
qDebug() << "Drop Mime data" << endl;
if (action == Qt::IgnoreAction)
return true;
if (! data->hasUrls())
return false;
QList<QUrl> urls = data->urls();
foreach(QUrl url , urls) {
std::shared_ptr<QFile> file(new QFile(url.path()));
if (! file->open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox::warning(NULL, QString("note"), QString("unable to open file!"));
return false;
}
XMLFilePtr xml(new XMLFile(file->fileName(), file));
addXMLFile(xml);
}
return true;
}
.
Qt::DropActions XMLFileModel::supportedDropActions() const
{
qDebug () << "supportedDropActions";
return Qt::CopyAction | Qt::MoveAction;
}
.
// define the acceptable mime type
QStringList XMLFileModel::mimeTypes() const
{
qDebug () << "mimeTypes";
QStringList types;
types << "text/uri-list" << "text/plain" << "application/xml" << "text/xml";
qDebug() << types;
return types;
}
.
I tried to add
QMimeData* mimeData(const QModelIndexList &indexes) const;
and now I could drag&drop inside the tableview or even between table views. debug messages print as well.
then I continued to do the test.
I wrote a test class:
class test : public QTableView
{
Q_OBJECT
public:
explicit test(QWidget *parent = 0);
virtual void dropEvent(QDropEvent *event);
virtual void dragEnterEvent ( QDragEnterEvent * event );
void startDrag ( Qt::DropActions supportedActions );
};
.
void test::dropEvent(QDropEvent *event) {
qDebug() << "test";
qDebug() << event->mimeData()->formats();
event->accept();
}
void test::dragEnterEvent(QDragEnterEvent *event) {
qDebug() << "drag enter";
qDebug() << event->mimeData();
event->accept();
}
void test::startDrag(Qt::DropActions supportedActions) {
qDebug() << "true";
}
.
and add a new test widget in the mainWindow that accepts the same model.
It amazed me that drag&drop internally or between table views is still working. but when I tried to drop the item from my desktop, none of these three functions are called.
now I am thinking that the problem might be the incompatible mime-type between my OS and Qt D&D framework. I may not return the right mime type
any one suffered and solved this problem or any suggestions? >_<
= = = = = = = =
hey, I got the same problem with the drop site example, too!
I don't think you need to reimplement any of those functions except for dragEnterEvent and dropEvent. Did you call QWidget::setAcceptDrops(true)? This is important.
Related
I have an std::set that contains unique values. I have an std::queue that holds the same values
in order to age the values in std::set.
I'd like to use a timer to determine when to pop a value from the queue and then erase the value from the set.
The timer is created/started every time data is added to an empty set/queue.
If data is added to a non-empty set/queue, no change is made to the timer.
The timer would fire every X milliseconds to execute a function.
The function would pop a value from the queue then erase that value from the set.
If the set/queue is now empty the timer would stop.
If the set/queue is not empty, no change is made to the timer.
This program runs in Windows 10.
Does this way make sense? Is there a better/more efficient/simpler way to age the data?
I've read the docs on Using Timer Queues so I see how the queue and the timers are created and destroyed. What I don't see is a recommendation for starting/stopping timers.
Should I be creating a new TimerQueueTimer to wait for X milliseconds once, run the func and then create a new TimerQueueTimer if the set/queue is not empty?
Should I instead create a single TimerQueueTimer to run periodically X milliseconds but delete it once the set/queue is empty?
Is there a 3rd technique I should use instead?
Here's my example code.
using unsignedIntSet = std::set<std::uint32_t>;
using unsignedIntQ = std::queue<std::uint32_t>;
unsignedIntQ agingQ;
unsignedIntSet agingSet;
HANDLE gDoneEvent = NULL;
HANDLE hTimer = NULL;
HANDLE hTimerQueue = NULL;
VOID CALLBACK ageTimer(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
if (!agingQ.empty())
{
auto c = agingQ.front();
agingSet.erase(c);
agingQ.pop();
if (!agingQ.empty())
{
// rerun CreateTimerQueueTimer() here?
}
}
SetEvent(gDoneEvent);
}
int createTimerForAgingQ()
{
// create timer if it doesn't already exist
if (gDoneEvent == NULL)
{
gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (gDoneEvent == NULL)
{
std::cerr << "CreateEvent() error: " << WSAGetLastError() << std::endl;
return -1;
}
hTimerQueue = CreateTimerQueue();
if (hTimerQueue == NULL)
{
std::cerr << "CreateTimerQueue() error: " << WSAGetLastError() << std::endl;
return -1;
}
if (!CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK)ageTimer, NULL, 500, 0, WT_EXECUTEONLYONCE))
{
std::cerr << "CreateTimerQueueTimer() error: " << WSAGetLastError() << std::endl;
return -1;
}
}
}
void addUnique(unsigned char* buffer, int bufferLen)
{
// hash value
auto h = hash(buffer, bufferLen);
// test insert into set
auto setResult = agingSet.emplace(h);
if (setResult.second)
{
// enqueue into historyQ
agingQ.emplace(h);
if (!gDoneEvent) createTimerForAgingQ();
}
}
Research shows that the CreateTimerQueue/CreateTimerQueueTimer may not be the way to go.
Use of ThreadpoolTimer
This question involves Qt but could be pure C++ problem with my logic.
I am adding QQuickItems to an std::map store info about a list of QQuickItems & their respective parents.
The code:
std::array<std::string, 2> ObjectNamesArray = { "quickitemObj1", "quickitemObj2" };
std::map<QQuickItem*, QQuickItem*> items;
for(const auto& quickitem: ObjectNamesArray) {
QQuickItem * item = Qmlengine->rootObjects()[0]->findChild<QQuickItem*>(quickitem.c_str());
if (item != NULL)
items.insert(std::make_pair(item, item->parent());
// for a test, following works fine with the item pointer within this loop
qreal width ? item->width();
}
Debugging through above loop, the items map shows zero items with the tag not accessible.
Iterating over the map again like this.
std::map<QQuickItem*, QQuickItem*>::iterator it = items.begin();
while (it != items.end()) {
QQuickItem* item = it->first;
QQuickItem * itemParent = it->second; // crashes here as *item is null
it++;
}
Problem:
But, when I try to iterate through the map, there are no valid pointers to my QQuickItems. In fact looks like there are not items added to the map.
Question:
What is wrong with my logic? How should I add QQuickItems to an std::map so that I can safely retrieve them back.
First you have const string inside this method istead of QQuickItem name
findChild<QQuickItem*>("quickitem.c_str()");
So it gives you 0
You should add checking if item is NULL before adding to container.
Second thing is that you talking about searching items map, but here, you are appending to parents
parents.insert(std::make_pair(item, item->parent());
BTW: When you use Qt, I recommend you using Qt containers
EDIT:
This works for me, after some improvements and if i have appropriate objects in QML
CPP
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
std::array<std::string, 2> ObjectNamesArray = { "quickitemObj1", "quickitemObj2" };
std::map<QQuickItem*, QQuickItem*> items;
for(const auto& quickitem: ObjectNamesArray) {
QQuickItem * item = engine.rootObjects()[0]->findChild<QQuickItem*>(quickitem.c_str());
if (item != NULL)
items.insert(std::make_pair(item, (QQuickItem*)item->parent()));
}
std::cout << "Map: " << items.size() << std::endl; //it gives 2
std::map<QQuickItem*, QQuickItem*>::iterator it = items.begin();
while (it != items.end()) {
QQuickItem* item = it->first;
QQuickItem * itemParent = it->second; // no crash
it++;
}
QML
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item
{
objectName: "quickitemObj1"
}
Item
{
objectName: "quickitemObj2"
}
}
i'm new with irrlicht and i was trying to implement key events. I followed the irrlicht tutoriel on their website but it doesn't work.
Here's the code :
class MyEventReceiver : public irr::IEventReceiver
{
public:
MyEventReceiver()
{
for (irr::u32 i = 0; i < irr::KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;
}
virtual bool OnEvent(const irr::SEvent& event)
{
std::cout << event.EventType << std::endl;
if (event.EventType == irr::EET_KEY_INPUT_EVENT)
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
return (false);
}
virtual bool IsKeyDown(irr::EKEY_CODE keyCode) const
{
return (KeyIsDown[keyCode]);
}
private:
bool KeyIsDown[irr::KEY_KEY_CODES_COUNT];
};
Normally if i press a button the onEvent should be called but i can press any button i want it never calls this function. Of course i created a MyEventReceiver in the main.
Can someone who knows irrlicht help me please ?
I got the same problem, it's because you created it but you don't give it when you create your device: like this, IrrlichtDevice* device = createDevice(driverType,
core::dimension2d<u32>(640, 480), 16, false, false, false, --->&receiver<---);
I am trying to stream audio data from disk using OpenAL's buffer queueing mechanism. I load and enqueue 4 buffers, start the source playing, and check in a regular intervals to refresh the queue. Everything looks like it's going splendidly, up until the first time I try to load data into a recycled buffer I got from alSourceUnqueueBuffers(). In this situation, alBufferData() always sets AL_INVALID_OPERATION, which according to the official v1.1 spec, it doesn't seem like it should be able to do.
I have searched extensively on Google and StackOverflow, and can't seem to find any reason why this would happen. The closest thing I found was someone with a possibly-related issue in an archived forum post, but details are few and responses are null. There was also this SO question with slightly different circumstances, but the only answer's suggestion does not help.
Possibly helpful: I know my context and device are configured correctly, because loading small wav files completely into a single buffer and playing them works fine. Through experimentation, I've also found that queueing 2 buffers, starting the source playing, and immediately loading and enqueueing the other two buffers throws no errors; it's only when I've unqueued a processed buffer that I run into trouble.
The relevant code:
static constexpr int MAX_BUFFER_COUNT = 4;
#define alCall(funcCall) {funcCall; SoundyOutport::CheckError(__FILE__, __LINE__, #funcCall) ? abort() : ((void)0); }
bool SoundyOutport::CheckError(const string &pFile, int pLine, const string &pfunc)
{
ALenum tErrCode = alGetError();
if(tErrCode != 0)
{
auto tMsg = alGetString(tErrCode);
Log::e(ro::TAG) << tMsg << " at " << pFile << "(" << pLine << "):\n"
<< "\tAL call " << pfunc << " failed." << end;
return true;
}
return false;
}
void SoundyOutport::EnqueueBuffer(const float* pData, int pFrames)
{
static int called = 0;
++called;
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
if(tState == AL_STATIC)
{
Stop();
// alCall(alSourcei(mSourceId, AL_BUFFER, NULL));
}
ALuint tBufId = AL_NONE;
int tQueuedBuffers = QueuedUpBuffers();
int tReady = ProcessedBuffers();
if(tQueuedBuffers < MAX_BUFFER_COUNT)
{
tBufId = mBufferIds[tQueuedBuffers];
}
else if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
void* tConverted = convert(pData, pFrames);
// the fifth time through, we get AL_INVALID_OPERATION, and call abort()
alCall(alBufferData(tBufId, mFormat, tConverted, pFrames * mBitdepth/8, mSampleRate));
alCall(alSourceQueueBuffers(mSourceId, 1, &mBufferId));
if(mBitdepth == BITDEPTH_8)
{
delete (uint8_t*)tConverted;
}
else // if(mBitdepth == BITDEPTH_16)
{
delete (uint16_t*)tConverted;
}
}
void SoundyOutport::PlayBufferedStream()
{
if(!StreamingMode() || !QueuedUpBuffers())
{
Log::w(ro::TAG) << "Attempted to play an unbuffered stream" << end;
return;
}
alCall(alSourcei(mSourceId, AL_LOOPING, AL_FALSE)); // never loop streams
alCall(alSourcePlay(mSourceId));
}
int SoundyOutport::QueuedUpBuffers()
{
int tCount = 0;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tCount));
return tCount;
}
int SoundyOutport::ProcessedBuffers()
{
int tCount = 0;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_PROCESSED, &tCount));
return tCount;
}
void SoundyOutport::Stop()
{
if(Playing())
{
alCall(alSourceStop(mSourceId));
}
int tBuffers;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tBuffers));
if(tBuffers)
{
ALuint tDummy[tBuffers];
alCall(alSourceUnqueueBuffers(mSourceId, tBuffers, tDummy));
}
alCall(alSourcei(mSourceId, AL_BUFFER, AL_NONE));
}
bool SoundyOutport::Playing()
{
ALint tPlaying;
alCall(alGetSourcei(mSourceId, AL_SOURCE_STATE, &tPlaying));
return tPlaying == AL_PLAYING;
}
bool SoundyOutport::StreamingMode()
{
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
return tState == AL_STREAMING;
}
bool SoundyOutport::StaticMode()
{
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
return tState == AL_STATIC;
}
And here's an annotated screen cap of what I see in my debugger when I hit the error:
I've tried a bunch of little tweaks and variations, and the result is always the same. I've wasted too many days trying to fix this. Please help :)
This error occurs when you trying to fill buffer with data, when the buffer is still queued to the source.
Also this code is wrong.
if(tQueuedBuffers < MAX_BUFFER_COUNT)
{
tBufId = mBufferIds[tQueuedBuffers];
}
else if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
You can fill buffer with data only if it unqueued from source. But your first if block gets tBufId that queued to the source. Rewrite code like so
if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
I have a series of QTextEdits and QLineEdits connected to a slot through a QSignalMapper(which emits a textChanged(QWidget*) signal). When the connected slot is called (pasted below), I need to be able to differentiate between the two so I know whether to call the text() or toPlainText() function. What's the easiest way to determine the subclass type of a QWidget?
void MainWindow::changed(QWidget *sender)
{
QTextEdit *temp = qobject_cast<QTextEdit *>(sender);
QString currentText = temp->toPlainText(); // or temp->text() if its
// a QLineEdit...
if(currentText.compare(""))
{
...
}
else
{
...
}
}
I was considering using try-catch but Qt doesn't seem to have very extensive support for Exceptions... Any ideas?
Actually, your solution is already almost there. In fact, qobject_cast will return NULL if it can't perform the cast. So try it on one of the classes, if it's NULL, try it on the other:
QString text;
QTextEdit *textEdit = qobject_cast<QTextEdit*>(sender);
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender);
if (textEdit) {
text = textEdit->toPlainText();
} else if (lineEdit) {
text = lineEdit->text();
} else {
// Return an error
}
You can also use sender->metaObject()->className() so you won't make unnecesary casts. Specially if you have a lot of classes to test. The code will be like this:
QString text;
QString senderClass = sender->metaObject()->className();
if (senderClass == "QTextEdit") {
QTextEdit *textEdit = qobject_cast<QTextEdit*>(sender);
text = textEdit->toPlainText();
} else if (senderClass == "QLineEdit") {
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender);
text = lineEdit->text();
} else {
// Return an error
}
I know is an old question but I leave this answer just in case it would be useful for somebody...