Is it possible to display translucent and/or irregular-shaped windows with Qt?
(I'm assuming it ultimately depends on the capabilities of the underlying GUI system, but let's assume at least Windows XP / Mac OS X)
If so, how does one accomplish this?
Yes, it is possible. The key is the Qt::WA_TranslucentBackground attribute of QWidget
Here is a simple class that draws a round translucent window with a red background 50% alpha.
TranslucentRoundWindow.h:
#include <QWidget>
class TranslucentRoundWindow : public QWidget
{
public:
TranslucentRoundWindow(QWidget *parent = 0);
virtual QSize sizeHint() const;
protected:
virtual void paintEvent(QPaintEvent *paintEvent);
};
TranslucentRoundWindow.cpp:
#include <QtGui>
#include "TranslucentRoundWindow.h"
TranslucentRoundWindow::TranslucentRoundWindow(QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint)
{
setAttribute(Qt::WA_TranslucentBackground);
}
QSize TranslucentRoundWindow::sizeHint() const
{
return QSize(300, 300);
}
void TranslucentRoundWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255, 0, 0, 127));
painter.drawEllipse(0, 0, width(), height());
}
If you want to be able to move this window with the mouse, you will have to override mousePressEvent, mouseMoveEvent and mouseReleaseEvent.
It certainly is possible. Qt ships with the "Shaped Clock" demonstration. The documentation of which is here.
It creates a top-level window with an odd shape. Should be all you need.
Related
I am creating a simple Win32/MFC application with a main window, and a child-window that uses AnimateWindow() to show (slide up) and hide (slide down) the window. When running the application on 100% dpi scaling, everything behaves normally.
I have overridden WM_ERASEBKGND to render a random red color, to demonstrate the effect. As the window slides-down (hide) on every "step" of the animation the "update rectangle" of the background is repainted, exactly where the child-window disappeared, and the background is to become visible again.
However, when changing the dpi-scaling via Windows Settings (in this case to 125%), an incorrect area is redrawn. It appears as if the area passed into OnEraseBkgnd() is still using the 100% dpi-scaling size, but also the position is off: it appears as if the x/y position of the upper-left corner is passed in screen-space instead of client-space. So the redrawn area looks different, depending on where the on the screen the window is positioned.
The white area is where the child window was actually positioned, and where the redraw of the background should actually have happened.
I have confirmed this effect on Win10 (1803 and 1809) and on Win8.1
Is this a bug in the OS, or is there something I can do to avoid the problem - other than not using AnimateWindow()? ShowWindow() (with SW_SHOW or SW_HIDE) works just fine, btw.
Update: added the full source-code to reproduce the issue.
The problem occurs when using no dpi-aware manifest at all, but it also occurs when using <gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
class CDialogTestApp : public CWinApp
{
virtual BOOL InitInstance();
};
CDialogTestApp theApp;
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {}
};
class CDialogTestDlg : public CDialogEx
{
public:
CDialogTestDlg(CWnd* pParent = nullptr) : CDialogEx(IDD_DIALOGTEST_DIALOG, pParent) {}
protected:
virtual BOOL OnInitDialog();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
private:
CAboutDlg mDialog;
};
BEGIN_MESSAGE_MAP(CDialogTestDlg, CDialogEx)
ON_WM_ERASEBKGND()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
BOOL CDialogTestApp::InitInstance()
{
CWinApp::InitInstance();
CDialogTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
return FALSE;
}
BOOL CDialogTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
mDialog.Create(IDD_ABOUTBOX, this);
return TRUE;
}
BOOL CDialogTestDlg::OnEraseBkgnd(CDC* pDC)
{
COLORREF color = RGB(rand() & 255, 20, 40);
CRect rect;
GetClipBox(pDC->m_hDC, &rect); // retrieve the update-rectangle
CBrush brush(color);
FillRect(pDC->m_hDC, &rect, (HBRUSH)brush.m_hObject);
return TRUE;
}
void CDialogTestDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if (mDialog.IsWindowVisible())
{
mDialog.AnimateWindow(200, AW_HIDE | AW_SLIDE | AW_VER_POSITIVE);
}
else
{
mDialog.SetWindowPos(&CWnd::wndTop, 0, 50, 0, 0, SWP_NOSIZE);
mDialog.AnimateWindow(200, AW_ACTIVATE | AW_SLIDE | AW_VER_NEGATIVE);
}
CDialogEx::OnLButtonUp(nFlags, point);
}
I am working on a screen capture program using C++11, MinGW, and the Windows API. I am trying to use SDL2 to watch how my screen capture program works in real time.
The window opens fine, and the program seems to run well so long as I do nothing more than move the mouse cursor. But iff I click in the window, its menu bar, outside the window, or press any keys, the SDL window freezes.
I have set up some logging for the events to figure out what is happening. I never receive any events other than SDL_WINDOW_FOCUS_GAINED, SDL_TEXTEDITING, and SDL_WINDOWEVENT_SHOWN in that order. All of these are received at the start.
I have tried to find tutorials on SDL event handling since that's my best guess as to the source of the problem. I have found nothing more than basic event handling to watch for SDL_QUIT, basic mouse and keyboard events, and one on SDL_WINDOWEVENTs that does not seem to help. I have found nothing in-depth on what the events mean and best practices for handling them. That may not matter, because that might not be the source of the problem. For all I know, SDL is throwing a fit because there are other threads running.
Can anyone see any cause for this hanging in my code and provide an explanation as to how to fix it?
A quick explanation for the structure of my program is in order to cover the code I have omitted. The Captor class starts and runs a thread to grab a screenshot to pass to the Encoder. The Encoder starts a variable number of threads that receive a screenshot from the Captor, encode the screenshot, then passes the encoding to the Screen. The passing mechanism is the SynchronousQueue<T> class that provides paired methods put(const T&) and T get() to allow a producer and a consumer to synchronize using a resource; these methods time out to allow the the system to be responsive to kill messages.
Now for the source files (hopefully without too much bloat). While I would appreciate any comments on how to improve the performance of the application, my focus is on making the program responsive.
main.cpp
#include "RTSC.hpp"
int main(int argc, char** argv) {
RTSC rtsc {
(uint32_t) stoi(argv[1]),
(uint32_t) stoi(argv[2]),
(uint32_t) stoi(argv[3]),
(uint32_t) stoi(argv[4]),
(uint32_t) stoi(argv[5]),
(uint32_t) stoi(argv[6])
};
while (rtsc.isRunning()) {
SwitchToThread();
}
return 0;
}
RTSC.hpp
#ifndef RTSC_HPP
#define RTSC_HPP
#include "Captor.hpp"
#include "Encoder.hpp"
#include "Screen.hpp"
#include <iostream>
using namespace std;
class RTSC {
private:
Captor *captor;
Encoder *encoder;
SynchronousQueue<uint8_t*> imageQueue {1};
SynchronousQueue<RegionList> regionQueue {1};
Screen *screen;
public:
RTSC(
uint32_t width,
uint32_t height,
uint32_t maxRegionCount,
uint32_t threadCount,
uint32_t divisionsAlongThreadWidth,
uint32_t divisionsAlongThreadHeight
) {
captor = new Captor(width, height, imageQueue);
encoder = new Encoder(
width,
height,
maxRegionCount,
threadCount,
divisionsAlongThreadWidth,
divisionsAlongThreadHeight,
imageQueue,
regionQueue
);
screen = new Screen(
width,
height,
width >> 1,
height >> 1,
regionQueue
);
captor->start();
}
~RTSC() {
delete screen;
delete encoder;
delete captor;
}
bool isRunning() const {
return screen->isRunning();
}
};
#endif
Screen.hpp
#ifndef SCREEN_HPP
#define SCREEN_HPP
#include <atomic>
#include <SDL.h>
#include <windows.h>
#include "Region.hpp"
#include "SynchronousQueue.hpp"
using namespace std;
class Screen {
private:
atomic_bool running {false};
HANDLE thread;
SynchronousQueue<RegionList>* inputQueue;
uint32_t inputHeight;
uint32_t inputWidth;
uint32_t screenHeight;
uint32_t screenWidth;
SDL_Renderer* renderer;
SDL_Surface* surface;
SDL_Texture* texture;
SDL_Window* window;
void run() {
SDL_Event event;
while (running) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_CLOSE:
running = false;
break;
default:
break;
}
}
try {
RegionList rl = inputQueue->get();
SDL_RenderClear(renderer);
SDL_LockSurface(surface);
SDL_FillRect(surface, nullptr, 0);
for (uint32_t i = 0; i < rl.count; ++i) {
Region &r = rl.regions[i];
SDL_Rect rect {
(int) r.getX(),
(int) r.getY(),
(int) r.getWidth(),
(int) r.getHeight()
};
uint32_t color =
(r.getRed() << 16) +
(r.getGreen() << 8) +
r.getBlue();
SDL_FillRect(surface, &rect, color);
}
SDL_UnlockSurface(surface);
SDL_UpdateTexture(
texture,
nullptr,
surface->pixels,
surface->pitch
);
SDL_RenderCopyEx(
renderer,
texture,
nullptr,
nullptr,
0,
nullptr,
SDL_FLIP_VERTICAL
);
} catch (exception &e) {}
SDL_RenderPresent(renderer);
SwitchToThread();
}
}
static DWORD startThread(LPVOID self) {
((Screen*) self)->run();
return (DWORD) 0;
}
public:
Screen(
uint32_t inputWidth,
uint32_t inputHeight,
uint32_t windowWidth,
uint32_t windowHeight,
SynchronousQueue<RegionList> &inputQueue
): inputQueue {&inputQueue}, inputHeight {inputHeight} {
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow(
"RTSC",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
windowWidth,
windowHeight,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE |
SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS
);
renderer = SDL_CreateRenderer(window, -1, 0);
surface = SDL_CreateRGBSurface(
0,
inputWidth,
inputHeight,
24,
0xFF << 16,
0xFF << 8,
0xFF,
0
);
texture = SDL_CreateTexture(
renderer,
surface->format->format,
SDL_TEXTUREACCESS_STREAMING,
inputWidth,
inputHeight
);
running = true;
thread = CreateThread(nullptr, 0, startThread, this, 0, nullptr);
}
~Screen() {
running = false;
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
SDL_FreeSurface(surface);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
bool isRunning() const {
return running;
}
};
#endif
I have no experience in using SDL API in a multithreaded environment but this isn't a big problem as you will see later. I've checked your code and there is at least one thing you should change in my opinion.
Generally, in case of GUI systems (and partly SDL is also a gui system) you should always access the gui only from the main thread and expect the gui events to come from the main thread. Most GUI APIs are single threaded and I wouldn't be surprised if this would apply to SDL too. Note that many gui systems run on the main thread of your process by default and you can't choose your own thread. Don't run the code of your Screen class on a worker thread, run it on your main thread and make EVERY SDL API call from the main thread.
If you are writing the game or a similar software then (first) write it as if it was single threaded. The subsystems of your engine (physics simulation, this-and-that-system, game logic, rendering) should be executed serially one-after-the-other on your main thread (from your main loop). If you want to make use of multithreading that do that in "another dimension": Convert some of the subsystems or a smaller unit of work (like merge sort) to multithreaded, for example a physics system tasks can often be split into several small tasks so when the physics system is updated by the main thread then the physics system can burn all of your cores...
Doing most of your tasks on the main thread has another advantage: It makes your code much more easy to port to any platform. Optionally if you write your code so that it can execute in single threaded mode then it can make debugging easier in many cases and then you also have a "reference build" to compare the multithreaded build to performancewise.
Qt developers!
Is there are way to add image on the background of my midArea like on picture below?
I know I can use something like this
QImage img("logo.jpg");
mdiArea->setBackground(img);
But I don't need any repeat of my image on the background.
Thank you!
As I said in my comment above, you can sub-class the QMdiArea, override its paintEvent() function and draw your logo image yourself (in the bottom right corner). Here is the sample code that implements the mentioned idea:
class MdiArea : public QMdiArea
{
public:
MdiArea(QWidget *parent = 0)
:
QMdiArea(parent),
m_pixmap("logo.jpg")
{}
protected:
void paintEvent(QPaintEvent *event)
{
QMdiArea::paintEvent(event);
QPainter painter(viewport());
// Calculate the logo position - the bottom right corner of the mdi area.
int x = width() - m_pixmap.width();
int y = height() - m_pixmap.height();
painter.drawPixmap(x, y, m_pixmap);
}
private:
// Store the logo image.
QPixmap m_pixmap;
};
And finally use the custom mdi area in the main window:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow mainWindow;
QMdiArea *mdiArea = new MdiArea(&mainWindow);
mainWindow.setCentralWidget(mdiArea);
mainWindow.show();
return app.exec();
}
I am trying to implement zoom in / out of a image using Image Element in QML.
I want the Pixel scale to be modified if i double click / pinch zoom.
How can i implement this without using QImage::scaled(), QPixmap::scaled().
Basically i dont want to involve Qt logic into my application.
I want Similar effect to hat is happening in the following tutorial
http://harmattan-dev.nokia.com/docs/library/html/qt4/widgets-imageviewer.html
But without Qt Logic in the app.
i know it's not the best answer but i cannot write a comment (i got less then 50 reps...) but zooming in/out qml is easy using PinchArea if you add a MouseArea you can also use onClicked or onDoubleClicked.... there is a useful example for pinch to zoom here(qt.git).
The ImageViewer example you posted got features like print save and so on, and you don't want to use "qt Logic", so i think you will need to use "qt Logic". I would wirte one class for each feature and implement it where i need it.
first of all i think this could help you(Extending QML Functionalities using C++).
Here a (not tested) example for saveing and reading a file:
fileio.h
#ifndef FILEIO_H
#define FILEIO_H
#include <QObject>
#include <QVariant>
class FileIO : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString source
READ source
WRITE setSource
NOTIFY sourceChanged)
explicit FileIO(QObject *parent = 0);
Q_INVOKABLE QString source();
Q_INVOKABLE QVariant read();
Q_INVOKABLE bool write(const QVariant& data);
public slots:
void setSource(const QString& source) ;
signals:
void sourceChanged(const QString& source);
void error(const QString& msg);
private:
QString mSource;
};
#endif // FILEIO_H
and fileio.cpp
#include "fileio.h"
#include <QFile>
#include <QDataStream>
#include <QString>
#include <QDebug>
FileIO::FileIO(QObject *parent) :
QObject(parent){
}
QString FileIO::source(){
return mSource;
}
QVariant FileIO::read()
{
if (mSource.isEmpty()){
emit error("source is empty");
return QVariant();
}
QFile file(mSource);
QVariant fileContent; // i dont know if you can use QImage but i think you cann't
if ( file.open(QIODevice::ReadOnly) ) {
QDataStream t( &file );
fileContent << t //you may have to reimplement "<<" operator
file.close();
} else {
emit error("Unable to open the file");
return QVariant();
}
return fileContent;
}.....
and register that in main.cpp like
qmlRegisterType<FileIO, 1>("FileIO", 1, 0, "FileIO");
so you can import it in your qml like
import FileIO 1.0
Rectangle{
id: someId
...
FileIO{
id: yourAccessToYourFileIOclass
}
}
i've not tested that code yet, i hope it helps.
for better answers post what you want to do exacly save, print , any filters....
p.s. I also would create a model in qt and bring it to qml...
greez Matthias
I am desperately failing at the supposedly simple problem to obtain the position and geometry of a Qt window on the screen. I originally stumbled upon this problem in a Python/PyQt context, but have since tracked it to Qt itself, where the QWidget::pos() property is not updated when the user resizes the window using any of the top or left edges or corners, i.e., resizes that involve the origin at the top left corner, hence the position of the window.
The following Qt/C++ program is the minimum to reproduce the problem (name as Qt_test.cc and build with qmake -project Qt_test.cc; qmake; make):
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QDebug>
class PrintingQWidget : public QWidget {
Q_OBJECT
public slots:
void print() {
qDebug() << pos() << width() << height();
}
};
#include "Qt_test.moc"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
PrintingQWidget window;
QPushButton btn("Print position", &window);
window.connect(&btn, SIGNAL(clicked()), SLOT(print()));
window.show();
return app.exec();
}
Click on "Print position" to obtain the coordinates of the window, then resize the window using the top or left edges, and click again. On my system (MacOSX), the window position in the QWidget::pos() property was not updated. I clearly understand from the documentation that QWidget::pos() should always yield the coordinates of the top-left corner of the widget, which is not the case.
Anyone able to reproduce this problem? Any workarounds?
OK, it appears that this is actually a bug in Qt 4.8. My example program, as well as Merlin069's version, behave perfectly fine with Qt 5. Both Qt 4.8.2 and Qt 4.8.4 are affected by the bug. (For Qt 4.8.4, I tested both the homebrew and dmg installations, which both behave incorrectly). My test system was MacOSX 10.7.5.
Maybe some of you with different systems/Qt versions can still run the test program and report back, so that I can file a better bug report?
Looking closer at your code, you should not be including the moc file. Anyhow, I decided to code this myself and I get the correct, expected coordinates in screen space. Here's the code: -
Header (widget.h)
#include <QWidget>
#include <QDebug>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget* parent = NULL)
: QWidget(parent)
{
}
virtual ~MyWidget()
{
}
public slots:
void PrintPos() const
{
qDebug() << pos() << width() << height();
}
};
Main.cpp
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget debugWidget(NULL);
QPushButton btn("print position", &debugWidget);
QObject::connect(&btn, &QPushButton::released, &debugWidget, &MyWidget::PrintPos);
debugWidget.show();
return app.exec();
}