QT Signal and Slot between windows - qt

I want to connect to windows in QT.
I know how to connect two widgets in same window.
using QObject::connect() in ui_a.h;
But now, I have two windows.
Six files.
For example:
ui_a.h(Window A)
a.h a.cpp (Widget in window A)
ui_b.h(Window B)
b.h b.cpp (Widget in window B)
How to connect two widget which are in different windows?
Thank you.

You have to find the scope of both window object and connect them there. Perhaps connecting them from main.cpp would make it easier. For example,
QApplication a(argc, argv);
A a;
B b;
QObject::connect(&a, SIGNAL(someSingal()), &b, SLOT(someSlot()));
QObject::connect(&b, SIGNAL(anotherSingal()), &a, SLOT(anotherSlot()));
a.show();
b.show();
return a.exec();

Do the two windows know about each other or are they completly independent? I mean is maybe Window A created by Window B or vice versa? But even if they are independent I assume you have a top parent-object wich creates the two windows right? If so, you should get access to the two widgets in this top parent object. There you can connect them:
QObject::connect(A->getWidgetInA(), SIGNAL(mySignal()), B->getWidgetInB(), SLOT(mySlot()));
getWidgetInA(), getWidgetInB() just return the pointer to your widgets in Window A and B.
For example in your class Window A: This is how getWidgetInA() would look like.
#include "a.h"
class WindowA: public QMainWindow
{
Q_OBJECT
public:
WindowA();
~WindowA();
a* getWidgetInA()
{
return widget_a;
}
private:
a *widget_a; //in WindowA.cpp you have then widget_a = new a;
}

Related

Application crashing when searching for QLineEdit in QFormLayout

I've experienced a weird crash when trying to find a QLineEdit in a QFormLayout.
Firstly, I created a QFormLayout and set a name for it:
QFormLayout *charColLayout = new QFormLayout; charColLayout->setObjectName("charColLayout");
Then, I created a QLineEdit, modified it a bit and add it in to the layout, and I also give it a name:
QLineEdit *delim = new QLineEdit;
delim->setMaxLength(1);
delim->setMaximumWidth(100);
delim->setText("/");
delim->setObjectName("delEdit");
charColLayout->addRow("Delimiter", delim);
Afterward, in a completely different function, I re-searched that layout with findChild():
QFormLayout *charcoal = secondHoriField->findChild<QFormLayout *>("charColLayout", Qt::FindChildrenRecursively);
It should be noted that secondHoriField is just a normal QLayout which my QFormLayout is located in.
Finally, I attempted to find that QLineEdit:
QLineEdit *delimEdit = charcoal->findChild<QLineEdit*>("delEdit", Qt::FindChildrenRecursively);
if (delimEdit == nullptr) {cerr << "error\n";} //debug
string curDelim = qStrToStr(delimEdit->text());
And it surprisingly came down with a crash, and as the output shown, it's because the delimEdit is null.
18:06:10: Starting D:\...\build-cryptog-Desktop_Qt_5_15_2_MinGW_64_bit-Debug\debug\cryptog.exe ...
error
18:06:17: The program has unexpectedly finished.
18:06:17: The process was ended forcefully.
18:06:17: D:\...\build-cryptog-Desktop_Qt_5_15_2_MinGW_64_bit-Debug\debug\cryptog.exe crashed.
But when I switched the findChild() function for this rather bruteforce-y line:
QLineEdit *delimEdit = dynamic_cast<QLineEdit*>(charcoal->itemAt(1)->widget());
cerr << qStrToStr(delimEdit->objectName()) << endl; //debug line
The program ran fine, and it showed the same name I set for the QLineEdit:
18:12:02: Starting D:\...\build-cryptog-Desktop_Qt_5_15_2_MinGW_64_bit-Debug\debug\cryptog.exe ...
delEdit
18:12:11: D:\...\build-cryptog-Desktop_Qt_5_15_2_MinGW_64_bit-Debug\debug\cryptog.exe exited with code 0
Why did this happened?
Another note: qStrToStr() is a function I implement to convert QString to std::string, and I have hand-checked it.
While findChild is a QObject method the itemAt is a QFormLayout method.
QObject::findChild
QFormLayout::itemAt
With addRow you add an item to the QFormLayout. This does not make it a child in the context of the QObject.
QFormLayout::addRow
The purpose of the QFormLayout is to organize the positioning of QWidgets, it is not meant to serve as a container. Maybe you could check whether the top level QWidget (e.g. QMainWindow) holding the QFormLayout would be a better choice as a parent for the QLineEdit.
Assuming you have some kind of QMainWindow:
QMainWindow myMainWindow;
// ...
QLineEdit *delim = new QLineEdit(&myMainWindow);
delim->setObjectName("delEdit");
//...
In another location:
auto delimEdit = myMainWindow.findChild<QLineEdit*>("delEdit");

How do I disable special handling of & on Qt button labels?

I have inherited a virtual keyboard system with an array of buttons, one for each key. The label for each button is a single QChar. When showing the "symbols" keyboard, the code uses an '&' QChar for a key label, but the key shows as blank. I'm sure Qt is processing the '&' as a shortcut key prefix. Similarly, the entered text is shown on another, longer, button label; this label, as well, handles '&' character as an accelerator. Entering "ABC&DEF" is shown as "ABCDEF" with the 'D' underlined.
I have tried building with QT_NO_SHORTCUT #defined, but that made no difference.
Does anyone know of an easy way to disable this special handling of '&'?
The answer is found in Qt doc. QAbstractButton::text:
If the text contains an ampersand character ('&'), a shortcut is automatically created for it. The character that follows the '&' will be used as the shortcut key. Any previous shortcut will be overwritten or cleared if no shortcut is defined by the text. See the QShortcut documentation for details. To display an actual ampersand, use '&&'.
(Emphasize by me.)
QPushButton is derived from QAbstractButton inheriting this behavior.
Sample testQPushButtonAmp.cc:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QPushButton qBtn("Text with &&");
qBtn.show();
return app.exec();
}
testQPushButtonAmp.pro:
SOURCES = testQPushButtonAmp.cc
QT = widgets
Compiled and tested on cygwin64 on Windows 10:
$ qmake-qt5 testQPushButtonAmp.pro
$ make
$ ./testQPushButtonAmp
Qt Version: 5.9.4
Concerning how to disable this default behavior:
I had a look at woboq.org QAbstractButton::setText().
void QAbstractButton::setText(const QString &text)
{
Q_D(QAbstractButton);
if (d->text == text)
return;
d->text = text;
#ifndef QT_NO_SHORTCUT
QKeySequence newMnemonic = QKeySequence::mnemonic(text);
setShortcut(newMnemonic);
#endif
d->sizeHint = QSize();
update();
updateGeometry();
#ifndef QT_NO_ACCESSIBILITY
QAccessibleEvent event(this, QAccessible::NameChanged);
QAccessible::updateAccessibility(&event);
#endif
}
So, QT_NO_SHORTCUT disables to retrieve the shortcut out of text but it has to be defined when Qt library is built from source. Actually, I'm afraid even with disabled shortcuts, the single & will still become invisible in output.
I digged deeper in woboq.org and found some promising candidates e.g.:
qt_set_sequence_auto_menmonic()
Specifies whether mnemonics for menu items, labels, etc., should be honored or not. On Windows and X11, this feature is on by default; on macOS, it is off. When this feature is off (that is, when b is false), QKeySequence::mnemonic() always returns an empty string.
Note: This function is not declared in any of Qt's header files. To use it in your application, declare the function prototype before calling it.
and a sample in QProxyStyle
#include "textedit.h"
#include <QApplication>
#include <QProxyStyle>
class MyProxyStyle : public QProxyStyle
{
public:
int styleHint(StyleHint hint, const QStyleOption *option = 0,
const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const override
{
if (hint == QStyle::SH_UnderlineShortcut)
return 0;
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
int main(int argc, char **argv)
{
Q_INIT_RESOURCE(textedit);
QApplication a(argc, argv);
a.setStyle(new MyProxyStyle);
TextEdit mw;
mw.resize(700, 800);
mw.show();
//...
}
which I tried in my sample.
Finally, nothing of them achieved the desired effect, i.e. only "&&" was rendered as & but "&" never.
__

Qt: Multiple QListWidgets and only a single entry selected

I have several QListWidgets and would like to only allow a single row to be selected for all of these lists. So for example if I select a row in one of the lists any other selection in the other lists would be cleared. How can I achieve this?
Is there a built-in way to do this (similar to QButtonGroup for buttons)? If not, what approach would you recommend that I take when trying to implement this myself?
Grateful for help and with kind regards,
Tord
AFAIK, there is no ready built-in feature to provide a single selection in multiple list views.
Instead, it can be done by a respective signal handler for the QSelectionModel::selectionChanged signal which does this whenever the selection of one of the involved list views changes.
Thereby, you have to consider that clearing the selection will emit a selectionChanged signal as well. (Otherwise, you may end up in a recursive call of your signal handler until a stack overflow occurs.)
Unfortunately, I use Qt in C++. (My Python knowledge is rather limited.)
Thus, all I can provide for now is my "proof of concept" in C++:
#include <QtWidgets>
void singleSel(QListView *pLstView, const QList<QListView*> &pLstViews)
{
for (QListView *pLstViewI : pLstViews) {
if (pLstViewI == pLstView) continue; // skip sender
// the check is necessary to prevent recursions...
if (pLstView->selectionModel()->hasSelection()) {
// ...as this causes emission of selectionChanged() signal as well:
pLstViewI->selectionModel()->clearSelection();
}
}
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
QApplication app(argc, argv);
// build contents
QStandardItemModel tblModel(0, 1);
for (int i = 0; i < 10; ++i) {
tblModel.appendRow(
new QStandardItem(QString::fromUtf8("Entry %0").arg(i + 1)));
}
// build some GUI
QWidget win;
QHBoxLayout qHBox;
QListView lstView1;
lstView1.setModel(&tblModel);
qHBox.addWidget(&lstView1);
QListView lstView2;
lstView2.setModel(&tblModel);
qHBox.addWidget(&lstView2);
QListView lstView3;
lstView3.setModel(&tblModel);
qHBox.addWidget(&lstView3);
win.setLayout(&qHBox);
win.show();
// install signal handlers
QList<QListView*> pLstViews = { &lstView1, &lstView2, &lstView3 };
QObject::connect(lstView1.selectionModel(),
&QItemSelectionModel::selectionChanged,
[&lstView1, &pLstViews](const QItemSelection&, const QItemSelection &)
{
singleSel(&lstView1, pLstViews);
});
QObject::connect(lstView2.selectionModel(),
&QItemSelectionModel::selectionChanged,
[&lstView2, &pLstViews](const QItemSelection&, const QItemSelection &)
{
singleSel(&lstView2, pLstViews);
});
QObject::connect(lstView3.selectionModel(),
&QItemSelectionModel::selectionChanged,
[&lstView3, &pLstViews](const QItemSelection&, const QItemSelection &)
{
singleSel(&lstView3, pLstViews);
});
// exec. application
return app.exec();
}
I compiled and tested on Windows 10 (64 bit). This is how it looks:
Update:
I tried to port the C++ application to Python/PyQt5:
#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QListView
from PyQt5.QtGui import QStandardItemModel, QStandardItem
def singleSelect(lstView, lstViews):
for lstViewI in lstViews:
if lstViewI == lstView:
continue
# the check is necessary to prevent recursions...
if lstViewI.selectionModel().hasSelection():
# ...as this causes emission of selectionChanged() signal as well:
lstViewI.selectionModel().clearSelection()
if __name__ == '__main__':
app = QApplication(sys.argv)
# build contents
tblModel = QStandardItemModel(0, 1)
for i in range(0, 10):
tblModel.appendRow(QStandardItem("Entry %d" % (i + 1)))
# build GUI
win = QWidget()
hBox = QHBoxLayout()
lstView1 = QListView()
lstView1.setSelectionMode(QListView.SingleSelection)
lstView1.setModel(tblModel)
hBox.addWidget(lstView1)
lstView2 = QListView()
lstView2.setSelectionMode(QListView.SingleSelection)
lstView2.setModel(tblModel)
hBox.addWidget(lstView2)
lstView3 = QListView()
lstView3.setSelectionMode(QListView.SingleSelection)
lstView3.setModel(tblModel)
hBox.addWidget(lstView3)
win.setLayout(hBox)
win.show()
# install signal handlers
lstViews = [lstView1, lstView2, lstView3]
lstView1.selectionModel().selectionChanged.connect(lambda sel, unsel: singleSelect(lstView1, lstViews))
lstView2.selectionModel().selectionChanged.connect(lambda sel, unsel: singleSelect(lstView2, lstViews))
lstView3.selectionModel().selectionChanged.connect(lambda sel, unsel: singleSelect(lstView3, lstViews))
# exec. application
sys.exit(app.exec_())
From what I saw in the test, it behaves similar like the one written in C++.
The only exception is that I have to click twice on an entry when I change the list view.
As I already said: my experiences in PyQt are very limited. (Actually, I started today by making this sample.) Thus, I may have overseen something. Please, take it with a grain of salt...

QEventLoop usage(signals and slots)

I have an application which contains such lines.
emit WindowAdded(settings->WindowType);//!!!!!!!!!!!!!!
MyWindow *widget = new MyWindow(0,settings,currentWindowIndex);
The signal changes value of currentWindowIndex, but it didn't work because of slot, it doesn't change its value in time. Some one advices me to use QEventLoop, but i don't understand how to do this. Give me an example, please.
Another part of the code:
connect(area,SIGNAL(WindowAdded(QString)),this,SLOT(addWindowOnTab(QString)));
void WorkSpace::addWindowOnTab(QString title)
{
qint32 i = TabsList->addTab(title);/////!!!!!!!!!!!!!!!!!!!!!!!!!
emit addedWindowIndex(i);
TabsList->setVisible(true);
}
connect(this,SIGNAL(addedWindowIndex(qint32)),area,SLOT(WindowIndexChanged(qint32)));
void MyMdiArea::WindowIndexChanged(qint32 index)
{
currentWindowIndex=index;
}
I think it can help.
MyMdArea is сlass inherited from QMdiArea, WorkSpace is a QWidget, TabsList is a QTabBar.
And there is another fact: I tried to understand execution sequence of slots and added some lines to code:
QLabel *n= new QLabel("1");
n->show();
after emiting WindowAdded signal
QLabel *n= new QLabel("2");
n->show();
after emiting addedWindowIndex signal
and
QLabel *n= new QLabel("3");
n->show();
after changing currentWindowIndex's value
and that is what i saw "1 2 3" and its exploded my brain. Maybe i don't understand something?

Qt: monitor global cursor click event with X11?

I want to capture global mouse click event in X11 , now
I tried to install a x11event filter , but it just doesn't work globally.
class XApplication: public QApplication
{
public:
XApplication (int & argc, char **argv):
QApplication (argc , argv)
{
}
protected:
bool x11EventFilter (XEvent *e)
{
qDebug() << "X11 Event: " << e->type;
return QApplication::x11EventFilter(e);
}
};
UPDATE
I mean outside the window , the code above works when I click on the window.
You can query X11 info from Qt using the QX11Info class. See its documentation. Then you can use raw Xlib from it.
You can use XGrabPointer(). If you use it, other apps won't receive the pointer events while the pointer is grabbed. man XGrabPointer will help you.
The "normal" way of subscribing for events is to use XSelectInput() on a window, but the problem is that you'll have to call XSelectInput on every single existing window. See its man page...
I know the xxf86dga extension has some calls related to mouse, but I'm not sure what they do.
XQueryPointer() is another way to query the pointer state without stealing events from other windows.
The only other place I can think of is the XInput extension, but I'm not sure it will help you either.
See the xev source code for a good reference on handling X11 events: http://cgit.freedesktop.org/xorg/app/xev
Sample code using XGrabPointer:
#include <stdio.h>
#include <assert.h>
#include <X11/Xlib.h>
int main(void)
{
Display *d;
Window root;
d = XOpenDisplay(NULL);
assert(d);
root = DefaultRootWindow(d);
XGrabPointer(d, root, False, ButtonPressMask | ButtonReleaseMask |
PointerMotionMask, GrabModeAsync, GrabModeAsync, None,
None, CurrentTime);
XEvent ev;
while (1) {
XNextEvent(d, &ev);
switch (ev.type) {
case ButtonPress:
printf("Button press event!\n");
break;
case ButtonRelease:
printf("Button release event!\n");
break;
case MotionNotify:
printf("Motion notify event!\n");
break;
default:
printf("Unknown event...\n");
}
}
XCloseDisplay(d);
return 0;
}
Compiled using: gcc x11mouse.c -o x11mouse -lX11

Resources