ARGB-Widgets

From QtCentreWiki

Jump to:navigation, search

Here you can learn how to build ARGB-Widgets under Windows. Under Linux this is easy done by compositing, but under Windows at first there is no solution. If you search longer in WinAPI, you'll find a window class, called WS_LAYERED_EX. With this window class and some help of Qt4 you can realize a ARGB-Widget: Arbgn.png

To make a layered window from a normal QWidget, you have to do two steps:

1. Include windows.h. Some functions can't be used by Qt normally, but with these defines it becomes possible:

#define _WIN32_WINNT 0x0500
#define WINVER 0x0500
#include <windows.h>

2. replace WindowLong.

setWindowFlags(Qt::FramelessWindowHint | Qt::Widget);
SetWindowLong(winId(), GWL_EXSTYLE, GetWindowLong(winId(), GWL_EXSTYLE) | WS_EX_LAYERED);

Normal layered windows can't have control elements, like pushbuttons or lineedits. Because of this we render all child widgets into a pixmap. This pixmap is the picture we want to render on our ARGB-Window. All this we do in the paintEvent:

void QAlphaWidget::paintEvent(QPaintEvent *event)
{
  QPixmap widgetMask = QPixmap("bg.png");
  QPainter p(&widgetMask);
  QObjectList ol = children();
  foreach(QObject *o, ol) {
    if (o->isWidgetType() && !o->objectName().isEmpty()) {
      QWidget* child = qobject_cast<QWidget*>(o);  
      p.drawPixmap(child->geometry(),drawCtrl(child ));
    }
  }
  p.end();
  updateAlpha(widgetMask);
 }
 
 QPixmap QAlphaWidget::drawCtrl(QWidget * widget)
 {
  if (!widget) {
    return QPixmap();
  }
 
  QRect r(widget->rect());
  QPixmap res(r.size());
  widget->render(&res, 
                 -r.topLeft(), 
                 r, 
                 QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);
 
  return res;
}

So now we have a pixmap with all control elements and a nice semi-transparent background. The thing we have to do next, is to bring up this pixmap to our widget. This we do in updateAlpha(const QPixmap&):

void QAlphaWidget::updateAlpha(const QPixmap &background)
{	
  HBITMAP oldBitmap;
  HBITMAP hBitmap;	
  SIZE size;
  size.cx = background.width();
  size.cy = background.height();
  HDC screenDc = GetDC(NULL);
  POINT pointSource;
  pointSource.x = 0;
  pointSource.y = 0; 
  POINT topPos;
  topPos.x = x();
  topPos.y = y();	
  HDC memDc = CreateCompatibleDC(screenDc);
  BLENDFUNCTION blend;
  blend.BlendOp             = AC_SRC_OVER;
  blend.BlendFlags          = 0;
  blend.SourceConstantAlpha = alpha;
  blend.AlphaFormat         = AC_SRC_ALPHA;
  hBitmap = background.toWinHBITMAP(QPixmap::PremultipliedAlpha); 
  oldBitmap = (HBITMAP)SelectObject(memDc, hBitmap);
  UpdateLayeredWindow(winId(), 
                      screenDc,  
                      &topPos,  
                      &size, 
                      memDc,  
                      &pointSource, 
                      0, 
                      &blend, 
                      ULW_ALPHA);
  ReleaseDC( NULL, screenDc);
  if (hBitmap!=NULL) {
    SelectObject(memDc, oldBitmap);
    DeleteObject(hBitmap);
  }
  DeleteDC(memDc); 
}

This is all we need to make nice ARGB-Widgets under Windows

Here the complete code:

QAlphaWidget.h

/********************************************************************************************************
* PROGRAM      : QAlphaWidget
* DATE - TIME  : Samstag 11 Augus 2007 
* AUTHOR       :  (Markus Künkler  )
* FILENAME     : QAlphaWidget.h
* LICENSE      : 
* COMMENTARY   : 
 ********************************************************************************************************/
#ifndef QAlphaWidget_H
#define QAlphaWidget_H

#include <QtGui>
#define _WIN32_WINNT 0x0500
#define WINVER 0x0500
#include <windows.h>
class  QAlphaWidget : public QWidget
{
 Q_OBJECT
     
public:
 QAlphaWidget(QWidget* wgtParent = 0);
 ~QAlphaWidget() {};
 QSize sizeHint() const;

public slots:
 void updateAlpha();
 void setLayered();

protected:
 void mouseMoveEvent(QMouseEvent *event);
 void mousePressEvent(QMouseEvent *event);
 void paintEvent(QPaintEvent *event);
 void closeEvent(QCloseEvent *event);
 QPixmap drawCtrl(QWidget * widget);
 QRegion childRegion;

private:
 QPoint dragPosition;
 QPixmap widgetMask;
 BYTE alpha;	
};

#endif

QAlphaWidget.cpp

/********************************************************************************************************
* PROGRAM      : QAlphaWidget
* DATE - TIME  : Samstag 11 Augus 2007 
* AUTHOR       :  (Markus Künkler )
* FILENAME     : QAlphaWidget.cpp
* LICENSE      : 
* COMMENTARY   : 
 ********************************************************************************************************/
#include <QtGui>
#include "QAlphaWidget.h"

QAlphaWidget::QAlphaWidget( QWidget* wgtParent)
 : QWidget(wgtParent)   
{
 setWindowFlags(Qt::FramelessWindowHint | Qt::Widget);
 setWindowTitle("AlphaWidget");
 widgetMask = QPixmap("bg.png");
 alpha = 255;
 resize(widgetMask.width(), widgetMask.height());
 setMouseTracking(true);
 SetWindowLong(winId(), 
               GWL_EXSTYLE, 
               GetWindowLong(winId(), GWL_EXSTYLE) | WS_EX_LAYERED));	
}

void QAlphaWidget::setLayered()
{
 SetWindowLong(winId(), 
               GWL_EXSTYLE, 
               GetWindowLong(winId(), GWL_EXSTYLE) | WS_EX_LAYERED); 
}

void QAlphaWidget::mousePressEvent(QMouseEvent *event)
{	
 childRegion = childrenRegion();
 if (!childRegion.contains(event->pos())) {
   if (event->button() == Qt::LeftButton) {			
     dragPosition = event->globalPos() - frameGeometry().topLeft();
     event->accept();
   }
 }
}

void QAlphaWidget::mouseMoveEvent(QMouseEvent *event)
{
 if (event->buttons() & Qt::LeftButton) {		 
   move(event->globalPos() - dragPosition);
   event->accept();
 }	 
}

QSize QAlphaWidget::sizeHint() const
{
 return QSize(widgetMask.width(), widgetMask.height());
}

void QAlphaWidget::closeEvent(QCloseEvent *event)
{
 qApp->quit();
}

void QAlphaWidget::updateAlpha()
{	
 HBITMAP oldBitmap;
 HBITMAP hBitmap;	
 SIZE size;
 size.cx = widgetMask.width();
 size.cy = widgetMask.height();
 HDC screenDc = GetDC(NULL);
 POINT pointSource;
 pointSource.x = 0;
 pointSource.y = 0; 
 POINT topPos;
 topPos.x = x();
 topPos.y = y();	
 HDC memDc = CreateCompatibleDC(screenDc);
 BLENDFUNCTION blend;
 blend.BlendOp             = AC_SRC_OVER;
 blend.BlendFlags          = 0;
 blend.SourceConstantAlpha = alpha;
 blend.AlphaFormat         = AC_SRC_ALPHA;
 hBitmap = widgetMask.toWinHBITMAP(QPixmap::PremultipliedAlpha); 
 oldBitmap = (HBITMAP)SelectObject(memDc, hBitmap);
 UpdateLayeredWindow(winId(), screenDc,  &topPos,  &size, memDc,  &pointSource, 0, &blend, ULW_ALPHA);
 ReleaseDC( NULL, screenDc);
 if (hBitmap!=NULL) {
   SelectObject(memDc, oldBitmap);
   DeleteObject(hBitmap); 
   DeleteObject(hBitmap);
 }
 DeleteDC(memDc); 
}

void QAlphaWidget::paintEvent(QPaintEvent *event)
{
 QPainter p(&widgetMask);
 QObjectList ol = children();
 foreach(QObject *o, ol) {
   if (o->isWidgetType()) {
     QWidget* child = qobject_cast<QWidget*>(o);  
     p.drawPixmap(child->geometry(),drawCtrl(child ));
   }			
 }
 p.end();
 updateAlpha();   
}
  
QPixmap QAlphaWidget::drawCtrl(QWidget * widget)
{
 if (!widget) {
   return QPixmap();
 }

 QRect r(widget->rect());
 QPixmap res(r.size());
 widget->render(&res, 
                -r.topLeft(), 
                r, 
                QWidget::DrawWindowBackground | QWidget::DrawChildren | QWidget::IgnoreMask);

 return res;
}
ARGB-Skin (can be found on the forum in Qt Software section)





Kernel_Panic 21:27, 13 August 2007 (CEST)