Make a QDialog appear in a different screen - c++

The title says it pretty much all:
I have two screens, and each time I create a QDialog it appears in the same screen as its parent.
How can I make it appear in a different screen? Or should I use a different type of top-level widget?
The code I use to create the dialog is:
QDialog my_dialog = new QDialog(this,
Qt::WindowMaximizeButtonHint |
Qt::WindowCloseButtonHint);
...
EDIT:
I have also tried using the QDesktopWidget which gives me a QScreen object that refers to the second screen. But then I don't find how to instruct the QDialog to use that QScreen (setting it as the parent doesn't work).

It is bad, that you edit your question without reading comments :(
// Your screen geometry:
QRect buildScreenGeometry()
{
auto desktop = QApplication::desktop();
QRect virtualRect;
const auto n = desktop->screenCount();
for ( auto i = 0; i < n; i++ )
virtualRect |= desktop->screenGeometry(i);
return virtualRect;
}
// Moving
auto dlg = new QDialog( someParent );
auto newPoint = QPoint( 2000, 0 ); // point on another screen
auto realPos = someParent->mapFromGlobal( newPoint );
dlg->move( realPos );
That's all.
UPDATE:
You should understand, that there are only ONE screen area with COMMON coordinate system, that contains ALL screens.
For example, you have 2 monitors with 800x600 resolution. First (main) monitor is standing left, and second standing right. In this case, coordinate system, that is available for your application is 1600x600. So, if your widget has 100x100 top-left position, on a first monitor, and you want to move it to another, you should call move(900x100); // 900 == screen1.width() + dialog.pos().x(). Then your widget will have 100x100 position on second monitor.
You should read Qt documentation.

You can use move on your QDialog, but be aware that move will set the QDialog position relative to it's parent.
You can get the Main Window's screen position and use that to setup the QDialog position. Just know that you're not guaranteed to have 2 screens on the end user machine.
For more information on move see: http://doc.qt.io/qt-5/application-windows.html#window-geometry

Related

wxStyledTextCtrl - Size of AutoComp

I was just wondering if it is possible to find the size (in pixels) of the autocompletion control shown by the wxStyledTextCtrl.
My goal is to show a help window associated with the entry when a selection happens. Therefore, I need the location and also the width of the autocompletion control. It seems location can be found from m_STC->AutoCompPosStart() but there seems to be no way of finding the width. I am using the following code:
auto StartPos = m_STC->ToPhys(m_STC->PointFromPosition(m_STC->AutoCompPosStart()));
int MaxChars = m_STC->AutoCompGetMaxWidth(); //returns 0 unless set to a fixed value
int w, h;
m_STC->GetTextExtent(wxString("A", MaxChars), &w, &h);
return wxPoint(StartPos.x + w, StartPos.y);
I am using Windows and wxWidgets 3.2.
There is no way to get this information from the styled text control because the autocomp window is completely managed by Scintilla. And unfortunately, Scintilla doesn't make any methods available for getting this info.
As a hack-around, the popup is currently implemented as a child window of the styled text control. So you could do something like this:
const wxWindowList& childred = m_stc->GetChildren();
for ( auto it = childred.begin() ; it != childred.end() ; ++it )
{
// We're assuming the styled text control has at most 1 child -
// namely the autocomp popup. It might be better to check that
// the window found is in fact the auto comp popup somehow.
// win->GetPosition() will return screen coordinates, so to get client
// coordinates, ScreenToClient must be called.
wxPoint psn = m_stc->ScreenToClient(win->GetPosition());
wxSize sz = win->GetSize();
// Do something with size and position here.
}
However, this isn't guaranteed to always work. If in the future, the auto comp popup implementation is changed to use a top level window instead of a child of the control, this method will fail.

Qt GUI application on macOS: how to find the currently active screen?

We are developing a macOS application whose GUI is relying on Qt.
At startup, we want to show() the QMainWindow at a specific location on the currently active screen (with multi screen systems in mind).
Is there a way to get the QScreen representing the currently active screen?
From our test, QGuiApplication::primaryScreen() is the first screen (which is consistent with the name), but we cannot find an equivalent for the active screen.
Qt5 provides functionality to do so, the QWindow::setScreen method sets the screen on which the window should be shown.
Any widget provides access to this pointer via QWidget::windowHandle():
QWidget * widget = new QWidget();
auto screens = qApp->screens();
// compute the index
widget->windowHandle()->setScreen(screens[index]);
widget->showFullScreen();
To get the screen number, you can use the mouse position and assume that the screen with the mouse is the one with the current focus:
QPoint globalCursorPos = QCursor::pos();
int mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
So the final code can be something like that:
QWidget * widget = new QWidget();
const auto globalCursorPos = QCursor::pos();
const auto mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
widget->windowHandle()->setScreen(qApp->screens()[mouseScreen]);
widget->showFullScreen();
Windows
If this approach does not fit your needs, you will need to perform some OS calls.
For instance, on Windows, you can use MonitorFromWindow:
HMONITOR active_monitor_number = MonitorFromWindow(GetActiveWindow(), MONITOR_DEFAULTTONEAREST);
If you need more information about the screen, you can use Qt or GetMonitorInfo.
I am not a Mac OS X developer, but it may exist a similar API
I did it in the following way:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QPoint topLeft = QPoint(0, 0);
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
if (nullptr != currentScreen) {
topLeft = currentScreen->geometry().topLeft();
}
#else
QPoint topLeft =
qApp->desktop()
->screenGeometry(qApp->desktop()->screenNumber(QCursor::pos()))
.topLeft();
#endif
someWidget->move(mapFromGlobal(topLeft) + QPoint(offset, offset));
Note that, sometimes, you may get nullptr for currentScreen (in my case if primary screen is at the bottom and mouse pos at the bottom or left edge on the primary screen).

How to position a QLabel on Qt?

I'm writing a program where I need to create a QLabel by code, instead of drag and drop, but I'm having problems positioning the QLabel as I want, this is the code I have:
if(ui->WorksList->currentItem()->text() == "Work1")
{
ui->InformationLabel->show();
QLabel *label = new QLabel(this);
label->show();
label->setText("Extraction");
label->setMinimumWidth(100);
int x = 2000;
x = label->geometry().x();
int y = 2000;
y = label->geometry().y();
}
With this piece of code my QLabel do not move from the top left corner.
Thank you
Your posted code doesn't move anything; as one of the comments said, you have to use the move method to move the label. If it doesn't show up, then it's either off screen or covered up by something else, or both. The coordinates for the move method are in the parent widget's coordinates, not the screen, and not the window. You've parented this QLabel to this, so the coordinates are relative to the top left corner of this, whatever that is.
Your code doesn't show what this is, nor any mention of how large the area of this is, so it's impossible for us to tell what coordinates might be reasonable.

Qt 5, get the mouse position in a screen

First of all, I'd like to mention that I found that related post How to get the mouse position on the screen in Qt? but it "just didn't work" for me. I made some tests, and the results didn't work as I expected, so I decided to make a new post to talk about the test I made and to find an alternative solution.
That's the code I used to make the test:
QScreen *screen0 = QApplication::screens().at(0);
QScreen *screen1 = QApplication::screens().at(1);
printf("screen0 %s \n", screen0->name().toStdString().c_str());
printf("screen1 %s \n", screen1->name().toStdString().c_str());
// Position on first screen.
QPoint pos0 = QCursor::pos(screen0);
// Position on second screen.
QPoint pos1 = QCursor::pos(screen1);
printf("pos 0: %d, %d \n", pos0.x(), pos0.y());
printf("pos 1: %d, %d \n", pos1.x(), pos1.y());
// Get position without screen.
QPoint pos = QCursor::pos();
printf("pos: %d, %d \n", pos.x(), pos.y());
What I was expecting, is that only one screen would return a valid position, since the cursor is only at one screen, not on both. But it's not the case, the both positions (pos0 and pos1) has the exactly same value, as we can see on the output:
screen0 DVI-D-0
screen1 HDMI-0
pos 0: 1904, 1178
pos 1: 1904, 1178
pos: 1904, 1178
Since the both positions has the same values, I can't know at which screen is the cursor. I don't know if that's a normal behavior or a bug, since the documentation doesn't say what happens when the screen argument isn't the screen where the mouse is.
My idea, is to open/launch an application (executed by a Qt daemon that must detect the selected screen) to the screen where the mouse is. I know that with libX11 it's possible, because I did it in the past, but I need to work with Qt 5, and I can't figure out how to do detect the selected screen with Qt.
I also made other tests, using QApplication and QDesktopWidget classes with no luck.
That's really weird. As a workaround, you could try this:
QPoint globalCursorPos = QCursor::pos();
int mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
Now you know which screen the cursor is in. Then you could find the cursor position within that screen doing this:
QRect mouseScreenGeometry = qApp->desktop()->screen(mouseScreen)->geometry();
QPoint localCursorPos = globalCursorPos - mouseScreenGeometry.topLeft();
This may seem like a trivial solution, but on my KDE it works (I ran into the same problems originally).
If you want to determine the local mouse coordinates with respect to a widget (this will be in device pixels and relative to the top left corner of the widget I believe) you can use
QWidget::mapFromGlobal(QCursor::pos());
i.e. call this->mapFromGlobal.
To figure out on which screen you are, you can iterate throught QGuiApplication::screens() and check whether the cursor fits in the geometry of the screen.
Here is a more complex example to compute the native cursor position (note the additional work needed to work with High DPI screens):
QPoint getNativeCursorPosition()
{
QPoint pos = cursorPosToNative(QCursor::pos());
// Cursor positions from Qt are calculated in a strange way, the offset to
// the origin of the current screen is in device-independent pixels while
// the origin itself is native!
for (QScreen *screen : QGuiApplication::screens()) {
QRect screenRect = screen->geometry();
if (screenRect.contains(pos)) {
QPoint origin = screenRect.topLeft();
return origin + (pos - origin) * screen->devicePixelRatio();
}
}
// should not happen, but try to find a good fallback.
return pos * qApp->devicePixelRatio();
}
This may work for you?
It did for me
QDesktopWidget *widget = QApplication::desktop();
QPosition globalCursorPosition = widget->cursor().pos();
Sice it seems that it can't be done with Qt (at least with my system configuration, and it seems that also in Windows) I decided to use the libX11 to make that implementation, which works like charm.
It's not an ideal solution because I wanted to only use Qt, but it works.
With QML you can use the properties of the Screen QML Type:
Screen.virtualX : The x coordinate of the screen within the virtual desktop.
Screen.virtualY : The y coordinate of the screen within the virtual desktop.
import QtQuick 2.6
import QtQuick.Window 2.2
console.log("Pos x : " + Screen.virtualX )
console.log("Pos y : " + Screen.virtualY )
This work with single screen as well multi-monitor systems.
I recently ran into a similar problem on Qt 5.15 + Windows + mixed DPI and needed this work around within a QWindow object.
QScreen* primaryScreen = QGuiApplication::primaryScreen();
QScreen* thisScreen = screen();
qreal primaryDPR = primaryScreen->devicePixelRatio();
qreal thisDPR = thisScreen->devicePixelRatio();
qreal scale = thisDPR / primaryDPR;
QPoint pos = scale * QCursor::pos();
I'm unsure if this works on other platforms.

QGraphicsRectItem and QGraphicsScene problems at Scene change

what I want to do is the following:
I have a little GUI with a QGraphicsView. In this graphics View I load a picture:
// m_picture is QPixmap
// image is QImage
// m_graphic is QGraphicsScene
// graphicsView is QGraphicsView
m_picture.convertFromImage(image);
m_graphic->addPixmap(m_picture);
ui->graphicsView->setScene(m_graphic);
This doesn't cause any problems and I can always load a new image without problems.
Now in addition to just display the pictures I want to give the user the ability to draw a rectangle on them ( to "focus" on a specific area ). Actually the user just types in the coordinates in four text boxes on the GUI ( x,y, width,heigth). After providing the coordinates the User presses a button and the rectangle at the following coordinates shall be displayed.
I accomplished this with this code:
void tesseract_gui::show_preview_rect()
{
int x,y,h,w;
x = ui->numBox_x->value();
y = ui->numBox_y->value();
h = ui->numBox_h->value();
w = ui->numBox_w->value();
if( rect_initialized )
{
m_graphic->removeItem(m_rect);
}
else
{
rect_initialized = true;
}
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->addItem(m_rect);
return;
}
The remove call is because I always want to display just one rectangle.
Now as I mentioned this works fine. But if the user now loads another picture ( with the calls at the top of my post ) the program crashes when I try to draw a new rectangle. I
get a Segmentation fault at the call of
m_rect->setPen(QPen(Qt::red));
If I call
m_graphic->removeItem(m_rect);
after loading a new picture I get
QGraphicsScene::removeItem: item 0x8c04080's scene (0x0) is different from this scene (0x8c0a8b0)
and then it crashes with the same Error at setPen.
What I don't get is, I don't change the scene. I just add another picture to it ( or overwrite it) .
Well any suggestions how I could do this right?
Best Regards
// edit:
I tried to do it just with everytime a new rectangle like this:
void tesseract_gui::show_preview_rect()
{
int x,y,h,w;
x = ui->numBox_x->value();
y = ui->numBox_y->value();
h = ui->numBox_h->value();
w = ui->numBox_w->value();
m_graphic->clear();
m_graphic->addRect(x,y,h,w);
return;
}
Problem at this is that with the clear() call it also clears the picture itself from my GraphicsView... so no solution there
// edit:
As suggested I got rid of the Warning like this:
if( m_rect->scene() != 0 )
{
m_graphic->removeItem(m_rect);
}
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->addItem(m_rect);
I know it's not the best way but I tried it also this way ( did not work for me ):
I added the item in the constructor:
m_graphic->addItem(m_rect);
and then
m_rect->setPen(QPen(Qt::red));
m_rect->setRect(x,y,h,w);
m_graphic->update();
and I get the "same" error as always ( Program crashes at m_rect->setPen() )
So it seems the problem always occurs when I already added the rectangle to the graphic, THEN changed the image of m_graphic and then did any operation with m_rect. ( Actually I guess m_graphic takes ownership of m_rect and so this causes the segmentation fault ... ? )
The message QGraphicsScene::removeItem: item 0x8c04080's scene (0x0) is different from this scene (0x8c0a8b0) tells you m_rect is not in any scene at the time you call it. It's probably removed somewhere else in your code or you have 2 variables with the same name in the class hierarchy.
Also, you don't need to remove it from scene to change it. Just change it while it's in the scene. It will get repainted with the new color and geometry in the next paint event.
Even if you REALLY want to remove it before changing it, just check if it's in a scene by calling QGraphicsItem::scene(). There's no need for the init check variable.