I have a Windows & Mac program that switches into full-screen mode on multiple monitors. In Qt 4, it seems (I can't find explicit documentation on how to do this) like the 'correct' way to go about this is by creating N QMainWindow
's for the N monitors on the machine, calling QWidget::move()
to the N monitor's top-left x,y coordinates, and then calling QWidget::setWindowState(Qt::WindowFullScreen)
. I don't know whether this is The Right Thing To Do - again, I can't find any documentation or examples anywhere that do this in Qt.
This seems to be 'broken' (if it was ever the Right Thing To Do in the first place) in Qt 5.4.1, especially on Windows 7. I'm still trying to isolate the problem, but it seems like the QMainWindow
s are dropping out of full-screen mode.
Just so I'm clear about this, what is the right way to do this? I found this forum post which seems to suggest that I should be setting the QScreen
on the underlying QWindow
objects that are held by the QMainWindow
s, but this doesn't seem to work in my tests. Here's an example program that I wrote:
app.h:
#include <vector>
#include <QObject>
class QMainWindow;
class app : public QObject
{
Q_OBJECT
public:
int run(int argc, char** argv);
public slots:
void add_window();
void remove_window();
void windows_go_to_screens();
void windows_go_to_screens_old();
void windows_go_to_primary_screen();
void windows_fullscreen();
void windows_nonfullscreen();
private:
QMainWindow * create_window(const char * id);
void init_menus( QMainWindow * w );
std::vector<QMainWindow *> m_windows;
};
app.cpp:
#include <assert.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include <QObject>
#include <QMainWindow>
#include <QApplication>
#include <QMenubar>
#include <QAction>
#include <QScreen>
#include <QWindow>
#include <QLayout>
#include <QLabel>
#include <QStyle>
#include "app.h"
using namespace std;
int app::run(int argc, char** argv)
{
QApplication a(argc, argv);
QMainWindow * w = create_window("0");
m_windows.push_back(w);
w->show();
return a.exec();
}
void app::add_window()
{
static const char * nums[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
m_windows.push_back(create_window(nums[m_windows.size()]));
m_windows.back()->show();
}
void app::remove_window()
{
if (m_windows.size() > 1)
{
QMainWindow * w = m_windows.back();
m_windows.pop_back();
w->close();
w->deleteLater();
}
}
void app::windows_go_to_screens()
{
QList<QScreen*> screens = qApp->screens();
for (unsigned i = 0; i < std::min((unsigned)m_windows.size(), (unsigned)screens.size()); ++i)
{
QMainWindow * mw = m_windows[i];
QScreen * screen = screens[i];
QWindow * wh = mw->windowHandle();
wh->setScreen(screen);
}
}
void app::windows_go_to_screens_old()
{
QList<QScreen*> screens = qApp->screens();
for (unsigned i = 0; i < std::min((unsigned)m_windows.size(), (unsigned)screens.size()); ++i)
{
QMainWindow * mw = m_windows[i];
QScreen * screen = screens[i];
mw->move(screen->geometry().left(), screen->geometry().top());
}
}
void app::windows_go_to_primary_screen()
{
QList<QScreen*> screens = qApp->screens();
for (unsigned i = 0; i < std::min((unsigned)m_windows.size(), (unsigned)screens.size()); ++i)
{
QMainWindow * mw = m_windows[i];
QScreen * screen = screens[0];
QWindow * wh = mw->windowHandle();
wh->setScreen(screen);
}
}
void app::windows_fullscreen()
{
for (unsigned i = 0; i < m_windows.size(); ++i)
{
QMainWindow * mw = m_windows[i];
mw->showFullScreen();
}
}
void app::windows_nonfullscreen()
{
for (unsigned i = 0; i < m_windows.size(); ++i)
{
QMainWindow * mw = m_windows[i];
mw->showNormal();
}
}
QMainWindow * app::create_window(const char * id)
{
QMainWindow * w = new QMainWindow(NULL);
init_menus(w);
QWidget * cw = new QWidget(w);
w->setCentralWidget(cw);
QHBoxLayout * l = new QHBoxLayout(cw);
cw->setLayout(l);
QLabel * lab = new QLabel(id, cw);
QPalette pal(lab->palette());
pal.setColor(QPalette::Background, Qt::red);
lab->setAutoFillBackground(true);
lab->setPalette(pal);
lab->setScaledContents(true);
lab->setAlignment(Qt::AlignCenter);
l->addWidget( lab );
return w;
}
void app::init_menus( QMainWindow * w )
{
QMenuBar * menubar = w->menuBar();
QMenu * view_menu = new QMenu(tr("View"), w);
view_menu->addAction("Add Window", this, SLOT(add_window()));
view_menu->addAction("Remove Window", this, SLOT(remove_window()));
view_menu->addAction("Windows Go To Screens", this, SLOT(windows_go_to_screens()));
view_menu->addAction("Windows Go To Screens (old method)", this, SLOT(windows_go_to_screens_old()));
view_menu->addAction("Windows Go To Primary Screen", this, SLOT(windows_go_to_primary_screen()));
view_menu->addAction("Windows Fullscreen", this, SLOT(windows_fullscreen()));
view_menu->addAction("Windows Non-Fullscreen", this, SLOT(windows_nonfullscreen()));
menubar->addMenu(view_menu);
}
main.cpp:
#include "app.h"
int main(int argc, char** argv)
{
app a;
return a.run(argc, argv);
}
When I run this program on OS X, the "Windows Go To Screens" function does nothing - none of the windows move. Neither does the "Windows Go To Primary Screen" (poorly named - should be 0 screen?). Creating more than N windows on an N window Mac is interesting - in that case calling "Windows Fullscreen" several times will actually switch the QMainWindows into fullscreen mode one at a time?!
Even more interesting is what happens on a multi-monitor OS X machine when you do this: "Add Window" until you have as many windows as displays. "Windows Go To Screens (old method)" will send each window to the top-left of each monitor. "Windows Fullscreen" will make all windows go full-screen on all monitors. "Remove Window" until you have only 1 window left. Then "Windows Non-FullScreen", and you'll get an interesting surprise. Go into Mission Control to see what's going on.
Can anyone tell me what the RIGHT way of doing this is? I've looked through the Qt5 examples - there's the player application that seems to be thoroughly broken (it can play a video in full-screen mode once, and then subsequent plays are in a separate desktop window), the sub game only maximizes to a single display, and none of the other examples seem to utilize full-screen mode, and certainly not on multiple monitors.
One way of doing it in Qt5 is to use QWindow::setScreen
to set the screen on which the window should be shown. QWidget
has a windowHandle()
that returns the pointer to the QWindow
. So you can get that pointer for each window and set a different screen.
Here is how to show your widget in the last screen in full-screen mode :
QWidget * widget = new QWidget();
widget->show();
widget->windowHandle()->setScreen(qApp->screens().last());
widget->showFullScreen();
Or in the second screen :
QWidget * widget = new QWidget();
widget->show();
widget->windowHandle()->setScreen(qApp->screens()[1]);
widget->showFullScreen();