Im having trouble figuring out how to use variables across slots. I have read the signals and slots page a few times over and haven't been able to figure out exactly how to use the connect function. I'm trying to have one button to select the directory, and then have it sent over to the other slot for when I hit print, so it can use that directory. Thanks for the help.
void MainWindow::on_pushButton_clicked()
{
QApplication app(int argc, char** argv());
int n = 107;
for (int q = 1; q <= n; q++)
{
QString fileName =(folderName + "/batch_%1.jpg").arg(q);
// QPrinter printer;
//QPrintDialog *dlg = new QPrintDialog(&printer,0);
//if(dlg->exec() == QDialog::Accepted) {
QPrinter printer(QPrinter::HighResolution);
printer.setResolution(300);
printer.setCopyCount(1);
printer.setDoubleSidedPrinting(false);
printer.setDuplex(QPrinter::DuplexNone);
printer.setColorMode(QPrinter::Color);
printer.setPageSize(QPrinter::Letter);
printer.setPaperSize(QPrinter::Letter);
printer.setPaperSource(QPrinter::Auto);
printer.setOrientation(QPrinter::Portrait);
printer.setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Inch);
QString outputFileName = QStringLiteral("/Users/alexdotzler/Desktop/testing/test%1.pdf").arg(q);
printer.setOutputFileName(outputFileName);
printer.setOutputFormat(QPrinter::PdfFormat);
QImage img(fileName);
QPainter painter(&printer);
painter.drawImage(QPoint(0,0),img);
painter.end();
}
//delete dlg;
}
void MainWindow::on_pushButton_2_clicked()
{
QString folderName = QFileDialog::getExistingDirectory(0, ("Select file"), QDir::currentPath());
}
At first, remember about Minimal reproducible example. This is not only about example, but also about minimal. In this case, your printing code is not nessessary.
Following your question, slot is just a function of class. And in your case, you can jist create printing function, that will be called from both pushButton 1 and 2.
Also, don't be await of real magic from sig/slots. It looks like, but not. Connect slot to signal is just virtual call to function B when function A called.
Then let's go a bit deeply into the world of signals and slots.
You can connect (slots or signals) to signals . No other way.
Basically, signals and slots used to pass data between different objects or to inform them about something.
// foo.h
class First {
...
signals:
void newData(QVariant val);
};
// bar.h
class Another {...
public slots:
void getNewData(QVariant val);
};
// main.cpp
Another another1;
First first1;
QObject::connect(first1, &First::newData, another1, &Another::getNewData);
You can also create something can be called "Connections tree", when signal [Another::sig1] connected to [Third::sig2], which connected to signal [Fourth::sig3] and slot [Fifth::slot1], and so on. I'm not sure someone really need it, but be free to play with.
AFAIK, the only reason to use signal/slot connection inside single class, is when you need to pass some data to GUI thread from another thread running in GUI. Please, don't do that in real projects.
Make folderName a member of your MainWindow class so it will be accessible by your pushButton clicked slots. Then you can read/write it as necessary from those slots.
Related
I have a basic doubt about how signals and slots actually work. Here's my code segment.
finddialog.cpp :
#include "finddialog.h"
#include <QtGui>
#include <QHBoxLayout>
#include <QVBoxLayout>
FindDialog::FindDialog(QWidget *parent) : QDialog(parent) {
//VAR INITIALIZATIONS
label = new QLabel(tr("Find &what:"));
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox = new QCheckBox(tr("Match &case"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
findButton = new QPushButton("&Find");
findButton->setDefault(true);
findButton->setEnabled(false);
closeButton = new QPushButton(tr("&Quit"));
//SIGNALS & SLOTS
connect (lineEdit, SIGNAL(textChanged(const QString&)),this, SLOT(enableFindButton(const QString&)));
connect (findButton, SIGNAL(clicked()), this, SLOT(findClicked()));
connect (closeButton,SIGNAL(clicked()), this, SLOT(close()));
//Layout
QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(backwardCheckBox);
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(findButton);
rightLayout->addWidget(closeButton);
rightLayout->addStretch();
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
//Complete window settings
setLayout(mainLayout);
setWindowTitle(tr("Find"));
setFixedHeight(sizeHint().height());
}
//Function Definition
void FindDialog::findClicked() {
QString text = lineEdit->text();
Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;
if(backwardCheckBox->isChecked())
emit findPrevious(text, cs);
else
emit findNext(text,cs);
}
void FindDialog::enableFindButton(const QString &text1) {
findButton->setEnabled(!text1.isEmpty());
}
With this code, how does the compiler know what is passed to enableFindButton(QString &) function. There are no function calls to enableFindButton(). In the connect statement there is a reference to enableFindButton() but isn't that more like a prototype because we are not providing the name of the variables to work with in its arguments?
connect (lineEdit, SIGNAL(textChanged(const QString&)),this, SLOT(enableFindButton(const QString&)));
Here only (const QString &) is the argument and a variable is not given. How does the application know what its argument is without passing it explicitly?
void FindDialog::enableFindButton(const QString &text1) {
findButton->setEnabled(!text1.isEmpty());
}
Here also &text1 is a reference argument. But to what ? I don't understand anything now after typing this all!
:-|
Qt is generating the code that makes it work when you build the project.
SIGNAL, SLOT, etc are preprocessor macros defined in qobjectdefs.h
These are then picked up by moc in QT when you build your project, and all the code required is generated, then compiled.
A decent page that explains this in more detail can be found here
C++ source code is processed by Qt meta object compiler (moc). It generates 'signatures' strings for QObject slots. Signatures contain method name and arguments (types, arguments' names do not matter in signatures). Whenever signal is emitted a signature match is performed directly (in case of direct connections) or in the event loop (for queued connections), and corresponding method is called. Moc compiler generates all necessary code that will match signatures and call the methods. If interested, look inside of one of those *.cxx files generated.
My GUI consists of a LineEdit and a PushButton. When the PushButton is clicked, the slot clicked() is called. I want to setup a signal-slot relationship between clicked() as the signal and doSomething() as the slot. The issue is that doSomething() does not have access to the UI and doSomething() relies on the text of LineEdit to work.
I see two solutions:
Grant doSomething() access to the UI.
I want to be able to pass a QString to doSomething() as an argument. clicked() does not accept any arguments. Qsignalmapper might be what I'm looking for. However, it seems like the mapper only passes arguments based on what the signal is. The arguments therefore need to be setup in advance and it seems like it does not help me here.
How would I do solution 2?
Assuming a C++ solution, you might consider using an intermediate signal like the following for #2:
connect(thisWidget, SIGNAL(clicked()),
thisWidget, SLOT(slotClickHandler()));
then:
void slotClickHandler()
{
QString s = "my variable string here";
emit sigDoSomething(s);
}
and them:
void slotDoSomething(const QString &s)
{
// Do something interesting with s here
}
Note that this snippet assumes you've defined a new signal sigDoSomething(const QString &) and a private slot void slotDoSomething(const QString &).
I have looked at several different tutorials and all the code looks the same but when I try to compile it I get two errors. They are no match for call to '(QFile) (QString&)' no match for call to '(QTextSteam) (Qfile)'. Could you please explain why? The following is part of my code if you need more just ask.
my header chunk of code
private:
QLabel *label;
QTextEdit *left;
QTextEdit *right;
QLineEdit *user;
QTextStream file;
QFile namefile;
QString name;
QString n;
my source code
n ="name.txt";
namefile(n);
if (!namefile.open(QFile::ReadOnly))
{
return;
}
file(&namefile);
name=file.readLine();
right->setText(name);
Your question seems to have more to do with C++ than with Qt. In particular, it would be worthwhile for you to look into C++ initialization lists, and how class members are constructed in general.
In any event, I am assuming that the code you've listed as "my source code" is in the constructor of a class. One option is to first re-order the private members such that they are listed in order of initialization:
private:
QString n;
QFile namefile;
QTextStream file;
QString name;
Then to initialize them in the initialization list of the class constructor:
MyClass::MyClass()
:n("name.txt")
,namefile(n)
,file(&namefile)
{
if (!namefile.open(QIODevice::ReadOnly))
{
return;
}
name = file.readLine();
// Rest of your code goes here.
}
There are other ways of accomplishing this, but I'll leave it to you to research what I've suggested above.
Just change
file.setDevice(&namefile);
I am having some difficulty fully grasping how signals and slots are used in Qt. I am sure it is really basic but I'm just no getting it today.
I have a set of widgets a bit like this:
MainWindow
-->StackedWidget
-->ChildForms
Now the idea is that there are some actions on the Child widgets that will cause the stacked widget to display a different page.
So if I understand it properly I thought the way to connect signals and slots is to use the connect() at the scope that knows about the objects but what I have managed to get working doesn't do it this way. At the moment in my child form I use parentWidget() to access the slot of the StackedWidget but I am not very happy with really because it is giving the child information about the parent which it shouldn't have:
void TaskSelectionForm::setButtonMappings()
{
// Set up a mapping between the buttons and the pages
QSignalMapper *mapper = new QSignalMapper(this);
connect(mapper, SIGNAL(mapped(int)), parentWidget(), SLOT(setCurrentIndex(int)));
mapper->setMapping(ui->utilitiesButton, 2); // Value of the index
connect(ui->utilitiesButton, SIGNAL(clicked()), mapper, SLOT(map()));
}
But I am not really sure how I should do this and connect it up. Do I need to have signals at each level and emit through the tree?
A Bit of Signal-Slot Theory
The signal-slot connections are oblivious to parent-child relationships between QObjects, and any such relationship doesn't matter. You're free to connect objects to their children, to their siblings, to their parents, or even to QObjects that are in a separate hierarchy, or to lone QObjects that have neither parents nor children. It doesn't matter.
A signal-slot connection connects a signal on a particular instance of QObject to slot on another instance of QObject. To use the connect method, you need the pointers to the instance of sender QObject and the instance of receiver QObject. You then use the static QObject::connect(sender, SIGNAL(...), receiver, SLOT(...)). Those connections have nothing to do with any hierarchy there is between the sender and receiver.
You can also connect a signal to a signal, to forward it -- for example from a private UI element to a signal that's part of the API of the class. You cannot connect a slot to a slot, because it'd incur a bit of runtime overhead for a rarely-used case. The overhead would be an extra bool member in QObjectPrivate, plus a failed if (bool) test. If you want to forward slots to slots, there are at least two ways to do it:
Emit a signal in the source slot and connect that signal to the destination slot.
Obtain a list of all signals connected to the source slot, iterate on it and connect them to to the target slot. There's no easy way to maintain such connections when further signals are connected or disconnected from the source slot. Unfortunately, QObject only has a connectNotify(const char*) protected function, but not a signal -- so you can't hook up to it unless you would modify src/corelib/kernel/qobject[.cpp,_p.h,.h] to emit such a signal. If you truly need it, just modify the Qt source, you have access it for a reason, after all. Hacking the vtable without modifying Qt is possible, but discouraged for obvious reasons.
The Answer
Below is a self contained example that shows how to do what you want. Turns out I have answers to quite a few questions from my various experiments I've done in Qt in the past. I'm a packrat when it comes to test code. It's all SSCCE to boot :)
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-slot-hierarchy-10783656
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class Window : public QWidget
{
QSignalMapper m_mapper;
QStackedLayout m_stack{this};
QWidget m_page1, m_page2;
QHBoxLayout m_layout1{&m_page1}, m_layout2{&m_page2};
QLabel m_label1{"Page 1"}, m_label2{"Page 2"};
QPushButton m_button1{"Show Page 2"}, m_button2{"Show Page 1"};
public:
Window(QWidget * parent = {}) : QWidget(parent) {
// the mapper tells the stack which page to switch to
connect(&m_mapper, SIGNAL(mapped(int)), &m_stack, SLOT(setCurrentIndex(int)));
// Page 1
m_layout1.addWidget(&m_label1);
m_layout1.addWidget(&m_button1);
// tell the mapper to map signals coming from this button to integer 1 (index of page 2)
m_mapper.setMapping(&m_button1, 1);
// when the button is clicked, the mapper will do its mapping and emit the mapped() signal
connect(&m_button1, SIGNAL(clicked()), &m_mapper, SLOT(map()));
m_stack.addWidget(&m_page1);
// Page 2
m_layout2.addWidget(&m_label2);
m_layout2.addWidget(&m_button2);
// tell the mapper to map signals coming from this button to integer 0 (index of page 1)
m_mapper.setMapping(&m_button2, 0);
connect(&m_button2, SIGNAL(clicked()), &m_mapper, SLOT(map()));
m_stack.addWidget(&m_page2);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
Connect(stackedwidget->currentactivewidget,SIGNAL(OnAction()),this,SLOT(PrivateSlot()));
PrivateSlot() is a slot declared privately. So in this function, you can add your code to change the page of stackedwidget corresponding to the action produced by currentactivewidget.
Again if you really want to pass the signal up the heirarchy, emit a publicsignal() at the end of private slot function.
Connect(this,SIGNAL(publicsignal()),Parentwidgetofstackedwidget(here mainwindow),SLOT(mainwindow_slot()));
I have the MainWindow w windows and TestThread testThread as a member of w. I know it i simple, but I cannot run the testThread.foo() method in testThread thread (not in window thread). In another words: I don't understand the QThread behavior.
Please help correct the next test application. There is a QProgressBar *MainWindow::ui::progressBar and QPushButton *MainWindow::ui::startButton (write simply). I want to start (by startButton click) TestThread::foo(int* progress) which will increment int progress each second.
MainWindow:
MainWindow::MainWindow(QWidget *parent) : // ...
{
// ...
ui->progressBar->setRange(0, 5);
progress = 0; // int MainWindow::progress
this->connect(ui->startButton, SIGNAL(clicked()), SLOT(startFoo()));
connect(this, SIGNAL(startFooSignal(int*)), &testThread, SLOT(foo(int*)));
// TestThread MainWindow::testThread
testThread.start();
}
// ...
void MainWindow::timerEvent(QTimerEvent *event)
{
ui->progressBar->setValue(progress);
}
void MainWindow::startFoo() // this is a MainWindow SLOT
{
startTimer(100);
emit startFooSignal(&progress);
// startFooSignal(int*) is a MainWindows SIGNAL
}
TestThread:
void TestThread::foo(int *progress) // this is a TestThread SLOT
{
for (unsigned i = 0; i < 5; ++i) {
sleep(1);
++*progress; // increment MainWindow::progress
}
}
I know, this is simple. I am doing something wrong :)
P.S. I want to run the simpliest (as possible) example to understand the QThread behavior.
Thanks!
The critical issue is to have the object containing the foo()-function be owned by that thread, so that slot calls are dispatched from the right thread's event-loop.
(Note that there's no need to actually have foo() on the TestThread object. You can use separate objects for QThread and WhatEver::foo() function. It might be easier too, I'm not sure..)
IIUC, this is what you have to do:
Use QObject::moveToThread() to assign the object containing the foo-function to TestThread (that means that Qt::AutoConenction (the default) signal/slots calls will run correctly across thread, being dispatched from each thread's own event loop).
By having the object "owned" by the right thread, slots calls will be scheduled on that thread's event loop, rather than executed directly.
Hope it helps. :)
One alternative solution: If you just want to run a function in another thread, and don't insist using QThread, you should check out the QT Concurrent Namespace.
The following example will run the function foo() in separate thread and will not block on the line where calling the function. Of course there are mechanisms to understand when a function ends, to get a result, to wait for it, to control execution.
void foo(int &progress) {...}
int progress;
QtConcurrent::run(foo, progress);
Hope this helps
See QThread::start and QThread::run.