I've been struggling with a really strange behavior that I do not manage to resolve on my own, so here I am.
I'm creating an GUI for a console application which works great. The user can either select a file to be loaded or reload a previous selected file. Those two actions are handled with two different slots. I'm handling errors in the file's format with exceptions and it worked great in the console version, but for the GUI not too well for the moment...
When an error is thrown in the openFile slot, the catch block makes his work and the method is stopped as expected BUT then the other slot is being called unexpectedly. I have no idea why and so have no idea how to correct that behavior.
Here's the relevant code :
void Loader::openSourceFile()
{
fileName = QFileDialog::getOpenFileName(myWindow, tr("Select source file"), QString(), tr("Text files (*.txt)"));
try{
parseSourceFile();
} catch(MyException &e)
{
QString msg = QString(e.what());
myWindow->alertUser(msg);
return;
}
}
void Loader::reloadSourceFile()
{
try{
parseSourceFile();
} catch(MyException &e)
{
QString msg = QString(e.what());
myWindow->alertUser(msg);
return;
}
}
the context of use :
myLoader = new Loader(this);
menuTop = menuBar()->addMenu(tr("&File"));
//open source file
openAction = new QAction(QIcon(":images/document.png"), tr("&Open"), this);
connect(openAction, SIGNAL(triggered()), myLoader, SLOT(openSourceFile()));
menuTop->addAction(openAction);
//reload source file
reloadAction = new QAction(QIcon(":images/reload.png"), tr("&Reload"), this);
connect(openAction, SIGNAL(triggered()), myLoader, SLOT(reloadSourceFile()));
menuTop->addAction(reloadAction);
Note : after heavy debugging, I found that one function in the moc file of that class is being called after the execution of the catch block :
void Loader::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
Loader *_t = static_cast<Loader *>(_o);
switch (_id) {
case 0: _t->openSourceFile(); break;
case 1: _t->reloadSourceFile(); break;
default: ;
}
}
Q_UNUSED(_a);
}
it is called with _id = 1 which explain the undesired execution of the other slot. But why this happens ??? Does anybody could explain how this can be avoided ? I've already made my own application class derived from qApplication and override notify() but that does not change anything.
Should:
reloadAction = new QAction(QIcon(":images/reload.png"), tr("&Reload"), this);
connect(openAction, SIGNAL(triggered()), myLoader, SLOT(reloadSourceFile()));
menuTop->addAction(reloadAction);
be:
reloadAction = new QAction(QIcon(":images/reload.png"), tr("&Reload"), this);
connect(reloadAction, SIGNAL(triggered()), myLoader, SLOT(reloadSourceFile()));
// ^^^^^^^^^^^^
menuTop->addAction(reloadAction);
Related
It's my first time to ask question on stackoverflow. And I'm a chinese girl, if my description about this problem has so much grammar error that you can't understand it easyly, I'm so sorry.
Below is my question:
headerfile:
class AdbDriver : public QObject
{
Q_OBJECT
private:
QString PnPutilPath_;
QProcess *process_;
public:
explicit AdbDriver(QObject *parent = 0);
~AdbDriver();
void installDriver();
};
AdbDriver::AdbDriver(QObject *parent):QObject(parent){
PnPutilPath_ = qgetenv("WINDIR") + "\\sysnative\\pnputil.exe";
process_ = new QProcess();
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
}
sourcefile:
AdbDriver::~AdbDriver(){
delete process_;
}
void AdbDriver::installDriver(){
QFile file(PnPutilPath_);
if(file.exists()){
qDebug()<<"pnputil.exe exist";
QString generaladbDriver = "E:/driver_androidusb/generaladb.inf";
qDebug()<<"the programming include driver:"<<generaladbDriver;
QFile file(generaladbDriver);
if(file.exists()){
qDebug()<<"yes, the driver is right in bihu package";
}
else{
qDebug()<<"loss driver in bihu package";
}
QStringList arguments;
arguments<<"-i"<<"-a"<<generaladbDriver;
process_->start(PnPutilPath_, arguments);
while(!process_->waitForStarted()){
qDebug()<<"wait";
}
qDebug()<<"while out";
process_->waitForReadyRead();
qDebug()<<"start";
// qDebug()<<process_->readAll();
process_->close();
}
else{
qDebug()<<"sorry, your computer has no tool pnputil.exe.";
}
}
when i Commented out code
qDebug()<<process_->readAll();
and use
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
it works properly.But if i use
qDebug()<<process_->readAll();
instead of
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
it will be wrong. what's the reason?
According to Qt document, both setReadChannelMode and setStandardOutputFile have to be called before QProcess::start to take effect, so replacing
qDebug() << process_->readAll();
with
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
is same as just commenting out qDebug() << process_->readAll();.
So I guess the child process does not output anything, so process_->readAll() will block, and the program halts.
(你看一下E:/log.txt有没有内容,估计是process_->readAll()阻塞了)
I have an application which has the parent frame as the MainFrame and two child frames, ChildA and ChildB. This should be a document oriented GUI such that when user clicks a document, it should be able to open another instance of FrameA.
When I click the executable of GUI MainFrame, ChildA and ChildB loads normally. However when the executable is running and when I click a document I realized that Windows is opening it as another process such that MainFrame, ChildA and ChildB loads again which I dont want cause I only want to open another instance of ChildA to show the contents of the document.
My code is as follows:
bool MainFrameApp::OnInit()
{
m_AnotherInstanceRunning=false;
m_checker = new wxSingleInstanceChecker;
if(m_checker->IsAnotherRunning()) m_AnotherInstanceRunning=true;
//Some other code
if(!m_AnotherInstanceRunning)
{
frame= new MainFrame(0L);
std::cout<<"Frame:"<<frame; //Prints the address
auto m_ChildA=new SomeChildFrame(frame);
frame->Show();
m_FrameA->Show(true);
wxTheApp->SetTopWindow(frame);
}
if(m_AnotherInstanceRunning)
{
frame=dynamic_cast<MainFrame*>(wxTheApp->GetTopWindow());
std::cout<<"Frame:"<<frame; //prints 0
frame->OpenDocument(frame,strDocDirectory,strDocName); //Does not work
return false;
}
My question is, when I click on a document how can I open that document within an already running MainFrame.
EDIT 1:
This is how I achieved it after countless of hours...
Below is the code for MainFrame.h
const int SERVER_ID=wxNewId();
const int SERVERSOCKET_ID=wxNewId();
const int CLIENTSOCKET_ID=wxNewId();
class MainFrame;
class MainFrameApp : public wxApp
{
bool m_AnotherInstanceRunning;
wxSocketServer* m_server;
wxString m_DocDirectoryPath;
wxString m_DocFullName; //including extension
protected:
virtual void OnServerEvent(wxSocketEvent& event);
virtual void OnServerSocketEvent(wxSocketEvent& event);
virtual void OnClientSocketEvent(wxSocketEvent& event);
public:
MainFrame* m_MainFrame;
wxSingleInstanceChecker* m_checker;
};
static const wxCmdLineEntryDesc cmdLineDesc[] =
{
{wxCMD_LINE_PARAM, NULL, NULL, "", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE},
{wxCMD_LINE_NONE},
};
Below is the code for MainFrame.cpp
bool ScienceSuitApp::OnInit()
{
m_AnotherInstanceRunning=false;
m_checker = new wxSingleInstanceChecker;
if(m_checker->IsAnotherRunning()) m_AnotherInstanceRunning=true;
wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
glbStrExeDir=exePath.GetPath()+wxFileName::GetPathSeparator();
wxString strDocFullPath; //This is directory+name
if(argc>1)
{
wxCmdLineParser parser(cmdLineDesc, argc, argv);
parser.Parse();
strDocFullPath=argv[1];
wxFileName docPath(strDocFullPath);
m_DocDirectoryPath=docPath.GetPath()+wxFileName::GetPathSeparator();
m_DocFullName=docPath.GetFullName(); //we needed extension of document as well
}
if(!m_AnotherInstanceRunning)
{
//Some other code
//Here Process #1
m_MainFrame= new MainFrame(0L);
m_MainFrame->Show();
wxIPV4address addr;
addr.Service(3000);
m_server = new wxSocketServer(addr,wxSOCKET_WAITALL);
if (!m_server->Ok())
{
if (m_server->LastError() == wxSOCKET_INVPORT) wxMessageBox(wxT("Port in use!"));
m_server->Destroy();
}
m_server->SetEventHandler(*this, SERVER_ID);
m_server->SetNotify(wxSOCKET_CONNECTION_FLAG);
m_server->Notify(true);
Connect(SERVER_ID,wxID_ANY, wxEVT_SOCKET,wxSocketEventHandler(MainFrameApp::OnServerEvent));
Connect(SERVERSOCKET_ID,wxID_ANY,wxEVT_SOCKET,wxSocketEventHandler(MainFrameApp::OnServerSocketEvent));
}
if(m_AnotherInstanceRunning)
{
//Here is for Process #2
Connect(CLIENTSOCKET_ID,wxID_ANY,wxEVT_SOCKET,wxSocketEventHandler(MainFrameApp::OnClientSocketEvent));
wxIPV4address addr;
addr.Hostname(wxT("localhost"));
addr.Service(3000);
wxSocketClient* Socket = new wxSocketClient();
Socket->SetEventHandler(*this, CLIENTSOCKET_ID);
Socket->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
Socket->Notify(true);
if(!Socket->Connect(addr, true)) wxMessageBox("Failed to connect to server");
return true;
}
return true;
}
void MainFrameApp::OnServerEvent(wxSocketEvent& event) //Process #1
{
wxSocketBase* sock = m_server->Accept(false);
sock->SetEventHandler(*this, SERVERSOCKET_ID);
sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
sock->Notify(true);
}
void MainFrameApp::OnServerSocketEvent(wxSocketEvent& event) //Process #1
{
wxSocketBase *sock = event.GetSocket();
wxCharBuffer buf(256);
switch(event.GetSocketEvent())
{
case wxSOCKET_CONNECTION:
{
break;
}
case wxSOCKET_INPUT:
{
sock->Read(buf.data(), 255);
wxString pathstr=wxString::FromUTF8(buf);
sock->Destroy();
wxFileName docPath(pathstr);
wxString DocDirectoryPath=docPath.GetPath()+wxFileName::GetPathSeparator();
wxString DocFullName=docPath.GetFullName(); //we needed extension of document as well
m_MainFrame->OpenDocument(m_MainFrame,DocDirectoryPath,DocFullName);
break;
}
case wxSOCKET_LOST:
{
sock->Destroy();
break;
}
}
}
void MainFrameApp::OnClientSocketEvent(wxSocketEvent& event) //Process #2
{
wxString str=m_DocDirectoryPath+m_DocFullName;
wxSocketBase* sock = event.GetSocket();
switch(event.GetSocketEvent())
{
case wxSOCKET_CONNECTION:
{
sock->Write(str.mb_str(),str.length());
}
}
//Exit the client process (Process #2) staying in the background, otherwise and no messages are sent
//Also we dont want to spawn background processes
exit(0);
}
If you want to prevent more than one instance of your application running at the same time, you can use wxSingleInstanceChecker to check if another one is running and use the IPC classes to send the name of the document to open to the other instance if it is.
I would like to create a program that shows a question with some answers to the user. My application uses 3 forms: Main Menu, Login Menu and Game Form, and all inherit from an abstract class called Form; I do this because it allows the use of the factory method, which it create a new window when a signal GoFw is emitted by the actual form.
The "loop" that shows the windows is the following: MainMenu -> LoginMenu -> GameForm -> MainMenu...
The problem is when the game is finished (e.g. the count of remaining questions is zero) the GameForm emits the signal GoFw but the application crashes after the show() method (I could see a white window with no buttons before the crash).
The debugger show a messagebox with this error:
The inferior stopped because it triggered an exception.
Stopped in thread 0 by: Exception at 0x723f7b93, code: 0xc0000005: read access
violation at: 0x0, flags=0x0 (first chance).
and QtCreator opens a file called: Disassembler(QHash::findNode)
This is the code of the factory method:
void FormFactory::Init(int formType)
{
///if formType == 1 MainMenu is showed
if(formType == MAIN_MENU_TYPE)
{
//inizializza il puntatore
actualForm = new MainMenu();
}
///else if is == 2 show ProfileMenu
else if(formType == PROFILE_MENU_TYPE)
{
actualForm = new LoginMenu();
}
///else if == 3 show GameForm
else if(formType == GAME_FORM_TYPE)
{
actualForm = new GameForm();
}
///else if there is no match launch an exception
else
throw UnexpectedIdEx();
connect(actualForm, SIGNAL(GoFw(int)), this, SLOT(DisplayForm(int)));
}
void FormFactory::DisplayForm(int i)
{
Reset();
Init(i);
///show the form pointed by actualform
actualForm->show();
}
void FormFactory::Reset()
{
disconnect(actualForm, SIGNAL(GoFw(int)), this, SLOT(DisplayForm(int)));
///if actualform is actually pointing to a form, delete it and set actualForm to zero
if(actualForm!=0)
delete actualForm;
actualForm = 0;
}
And the code of MainMenu.cpp is
MainMenu::MainMenu()
{
setUpGui();
}
void MainMenu::setUpGui()
{
playButton = new QPushButton(tr("Play"));
infoButton = new QPushButton(tr("Info"));
quitButton = new QPushButton(tr("Exit"));
///connect the clicked signal to the related slot
connect(infoButton, SIGNAL(clicked()), this, SLOT(info()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(quit()));
connect(playButton, SIGNAL(clicked()), this, SLOT(emitSig()));
///create the vertical layout
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(playButton);
layout->addWidget(infoButton);
layout->addWidget(quitButton);
setLayout(layout);
setWindowTitle(tr("Menu Principale"));
}
void MainMenu::emitSig()
{
emit GoFw(2);
}
Thank you all for your help,
Luca
I'd suggest rethink your solution, it seems you overcomplicated it with the factory method.
Just use 3 variables for the forms, do the "new" operation once for each, and use show() / hide() methods depending on your signals.
To answer to the crash problem, one reason i see is because you do "delete" in the slot.
From Qt doc:
Warning: Deleting a QObject while pending events are waiting to be delivered can cause a crash. You must not delete the QObject directly if it exists in a different thread than the one currently executing. Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.
I am a beginner in my fourth week learning C++; I had been working on CodeBlocks, but due to my interest in making GUIs I switched to Qt Creator. Back in CodeBlocks I had created a function that would avoid all the repetition in the code below, only changing the "TXT FILE". However, with Qt Creator's "specialized" C++ I am having trouble making sense of how to create a function to avoid all this repetition.
Any ideas? (I'm too far into this Qt project to go back to CodeBlocks.)
The "TXT FILE" changes depending on which RadioButton the user selects.
void MovierRec::on_searchButton_clicked()
{
int randomValue = qrand() % 100;
QList<QString> titles;
if(ui->modernButton->isChecked())
{
QFile myfile(":/classics.txt");
if (myfile.open(QIODevice::ReadOnly))
{
QTextStream in(&myfile);
while (!in.atEnd())
{
QString line = in.readLine();
titles.append(line);
}
myfile.close();
ui->textBrowser->setPlainText (titles[randomValue]);
}
}
else if(ui->romanceButton->isChecked())
{
QFile myfile(":/romance.txt");
if (myfile.open(QIODevice::ReadOnly))
{
QTextStream in(&myfile);
while (!in.atEnd())
{
QString line = in.readLine();
titles.append(line);
}
myfile.close();
ui->textBrowser->setPlainText (titles[randomValue]);
}
}
else if(ui->scifiButton->isChecked())
{
QFile myfile(":/scifi.txt");
if (myfile.open(QIODevice::ReadOnly))
{
QTextStream in(&myfile);
while (!in.atEnd())
{
QString line = in.readLine();
//titles.append(line);
}
myfile.close();
ui->textBrowser->setPlainText (titles[randomValue]);
}
}
This is generic programming issue, could refactor code in a better way:
// I didn't dig into every line of the code. just provide the refactor idea here
void getTitle(const QString& file_name, QList<QString>& titles;)
{
QFile myfile(file_name);
if (myfile.open(QIODevice::ReadOnly))
{
QTextStream in(&myfile);
while (!in.atEnd())
{
QString line = in.readLine();
titles.append(line);
}
myfile.close();
}
}
void MovierRec::on_searchButton_clicked()
{
int randomValue = qrand() % 100;
QList<QString> titles;
if(ui->modernButton->isChecked())
{
getTitle("classics.txt", titles);
}
else if(ui->romanceButton->isChecked())
{
getTitle("romance.txt", titles);
}
else if(ui->scifiButton->isChecked())
{
getTitle("scifi.txt", titles);
}
ui->textBrowser->setPlainText(titles[randomValue]); // move the dup action to the end
}
QT is well known for Signals and Slots. Each button can be connected to a slot. For Example in your case. You can connect each radio button to a slot. in order to do that, Open ur GUI form, right click on the radio button and select "Go To Slot", and select the slot you want to connect to.
This will create an empty function in ur .cpp file.
Now write your code for that button. And this function is called only when that particular Button is pressed/clicked .
example:
void ClassA::on_radioButton_clicked()
{
// write your code inside this function for , when this button is checked
}
I hope this will help you solve your issue. If you have other query , please provide more information.
In my code I have additional thread for printing:
class PrintThread : public QThread {
public:
PrintThread(const QString& text, QPrinter* printer): mText(text), mPrinter(printer) {}
void run()
{
QTextDocument doc;
doc.setHtml(mText);
doc.print(mPrinter);
delete mPrinter;
}
private:
QString mText;
QPrinter *mPrinter;
};
Separate thread is needed to prevent GUI from freezing when printing to pdf.
Sometimes during printing I see such lines in console (many times repeated):
X Error: RenderBadGlyphSet (invalid GlyphSet parameter) 165
Extension: 148 (RENDER)
Minor opcode: 25 (RenderCompositeGlyphs32)
Resource id: 0×0
And any text in GUI disappears. What the problem and how to solve it? Thanks.
I'm using Qt 4.4.3
Thread is created here:
void MyClass::print() {
QPrinter *printer = new QPrinter;
printer->setOrientation(QPrinter::Landscape);
QPrintDialog dialog(printer);
if (dialog.exec() == QDialog::Accepted) {
QString text = dataForPrint();
mPrintThread = new PrintThread(text, printer);
connect(mPrintThread, SIGNAL(finished()), this, SLOT(onPrintingFinished()));
mPrintThread->start();
}
}