How to properly display a picture ?

Moderator: Rathinagiri

Post Reply
Ricci
Posts: 255
Joined: Thu Nov 19, 2009 2:23 pm

How to properly display a picture ?

Post by Ricci » Mon Oct 10, 2011 8:11 pm

Handling pictures seems to be a weak point in Qt or I didn´t get the clue.

Assumed I define a BITMAP control with 300x300 points.

Now I want to display a picture with 400*600 points (portrait) and I want to see the whole picture. In this case SCALEDTOHEIGHT = .T. would be the right commands, the picture would be scaled down to 200*300 points.
If the picture has 600*400 (landscape) points I have to use SCALEDTOWIDTH = .T. to scale it down to 300*200 points.

What if I don´t know the size of the picture before (portrait or landscape)? I first thought SCALEDTOWIDTH = .T. and SCALEDTOHEIGHT = .T. would do it but in this case SCALEDTOWIDTH "wins" and portrait pictures are cropped.

Ricci
Posts: 255
Joined: Thu Nov 19, 2009 2:23 pm

Post by Ricci » Mon Oct 10, 2011 9:03 pm

I found the solution and it´s much better then I expected.
I wanted a picture that is smaller than the bitmap control not to be scaled up, but a picture, who is bigger to be scaled down to the size of the control (without stretching it).

My way was to overload the BITMAP class with my own MYBITMAP class.

To define my bitmap I use this code:

Code: Select all

        With Object MYBITMAP():New( "oBitmap" )
                 :Row := 100
                 :Col := 100
                 :Width := 300
                 :Height := 3000
                 :Picture := <cImagefile>
                 :Alignment := LBL_CENTER            // smaller pictures should be centered
                 :VAlignment := LBL_VCENTER
        end
And here is my MYBITMAP class:

Code: Select all

/*==============================================================================
   MYBITMAP class
==============================================================================*/
CLASS MYBITMAP FROM BITMAP

   DATA cClass                                    INIT   "MYBITMAP"

// Methods
   METHOD New
   METHOD Picture                                 SETGET

ENDCLASS

/*..............................................................................
   New
..............................................................................*/
METHOD New( cName, oParent ) CLASS MYBITMAP

   Super:New( cName, oParent )
   
RETURN Self

/*..............................................................................
   Picture
..............................................................................*/
METHOD Picture( cValue ) CLASS MYBITMAP

   LOCAL oIMG

   IF Pcount() == 0
      RETURN ::cPicture
   ELSEIF Pcount() == 1
      ::cPicture := cValue
      IF ::lCreated
         oImg := QPixmap( cValue )
         IF oImg:width() == 0 .and. left( cValue, 1 ) != ":"
            cValue := ":" +cValue
            oImg := QPixmap( cValue )
            IF oImg:width() > 0 
               ::cPicture := cValue
            ENDIF
         ENDIF
			   
      IF oImg:width() > ::oQTObject:width .AND. oImg:width() > oImg:height()       // picture too width and landscape?
         oIMG := oIMG:scaledToWidth( ::oQTObject:width )
		 
      ELSEIF oImg:height() > ::oQTObject:height .AND. oImg:height() > oImg:width()     // picture too height and portrait?
         oIMG := oIMG:scaledToHeight( ::oQTObject:height )
      ENDIF
      
      ::oQTObject:setPixmap( oIMG )
      ENDIF
   ENDIF

RETURN NIL

mrduck
Posts: 497
Joined: Fri Sep 10, 2010 5:22 pm

Post by mrduck » Tue Oct 11, 2011 7:40 am

GOOD GOOD GOOD !!!

The power of real OOP !

Do you need to expand/modify the behaviour of a class ? Derive from it and change the code... sometimes it is easy, like here, sometimes is a bit more complicated since the method may be long and complex, but it is definitively the way to go !!!


Since in New() you are just calling super:New() I think you can remove the definition of New() method.

Francesco

Ricci
Posts: 255
Joined: Thu Nov 19, 2009 2:23 pm

Post by Ricci » Tue Oct 11, 2011 8:03 am

mrduck wrote: Since in New() you are just calling super:New() I think you can remove the definition of New() method.
That´s right, thank you.

User avatar
l3whmg
Posts: 694
Joined: Mon Feb 23, 2009 8:46 pm
Location: Italy
Contact:

Post by l3whmg » Tue Oct 11, 2011 1:12 pm

Hi Ricci, could you please do a test with this source code about BITMAP.

Code: Select all

#include "hbclass.ch"
#include "common.ch"
#include "hbqtgui.ch"
#include "hmg_qtimage.ch"
#include "hmg.ch"

/*==============================================================================
   BITMAP class
==============================================================================*/
CLASS BITMAP FROM LABEL

   DATA cClass                         INIT   "BITMAP"

// data: please preserve alphabetic order.
   DATA oImage                         INIT  NIL      PROTECTED
   DATA cPicture                       INIT  ''       PROTECTED
   DATA lScaledToHeight                INIT  .F.      PROTECTED
   DATA lScaledToWidth                 INIT  .F.      PROTECTED
   DATA lStretch                       INIT  .T.      PROTECTED
   DATA nAspectRatio                   INIT  1        PROTECTED

// Methods: please preserve alphabetic order. Methods "New" and "Create" must be the first if present
   METHOD New
   METHOD Create
   METHOD AspectRatio                  SETGET
   METHOD Picture                      SETGET
   METHOD ScaledToHeight               SETGET
   METHOD ScaledToWidth                SETGET
   METHOD Stretch                      SETGET

   METHOD __HmgConnectEv               PROTECTED
   METHOD __HmgDisConnectEv            PROTECTED
   METHOD __HmgOnSizeExec              PROTECTED
   METHOD __HmgUpdateImage             PROTECTED

ENDCLASS

/*..............................................................................
   New
..............................................................................*/
METHOD New( cName, oParent ) CLASS BITMAP

   Super:New( cName, oParent )

/* this lines are already present within LABEL.class */
//   IF hb_IsNil( ::oParent ) == .T.
//      ::oQTObject := QLabel()
//   ELSE
//      ::oQTObject := QLabel( ::oParent:QtObject )
//   ENDIF
   
RETURN Self

/*..............................................................................
   Create
..............................................................................*/
METHOD Create() ClASS BITMAP
   
   ::lCreated := .T.

   ::__HmgUpdateImage()

   ::__HmgConnectEv()

RETURN Self

/*..............................................................................
   AspectRatio
..............................................................................*/
METHOD AspectRatio( nValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN   ::nAspectRatio
   ELSEIF hb_IsNumeric( nValue ) == .T.
      IF nValue == Qt_IgnoreAspectRatio .OR. nValue == Qt_KeepAspectRatio .OR. nValue == Qt_KeepAspectRatioByExpanding
         ::nAspectRatio := nValue
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN Self

/*..............................................................................
   Picture
..............................................................................*/
METHOD Picture( cValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::cPicture
   ELSEIF PCOUNT() == 1
      ::cPicture := cValue
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   ScaledToHeight
..............................................................................*/
METHOD ScaledToHeight( lValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::lScaledToHeight
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lScaledToHeight := lValue
      IF ::lScaledToHeight == .T.
         ::lStretch  := .F.
         ::lScaledToWidth := .F.
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   ScaledToWidth
..............................................................................*/
METHOD ScaledToWidth( lValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::lScaledToWidth
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lScaledToWidth := lValue
      IF ::lScaledToWidth == .T.
         ::lStretch  := .F.
         ::lScaledToHeight := .F.
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   Stretch
..............................................................................*/
METHOD Stretch( lValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::lStretch       // RETURN ::oQTObject:hasScaledContents()
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lStretch := lValue
//      ::oQTObject:setScaledContents( ::lStretch )
      IF ::lStretch == .T.
         ::lScaledToWidth  := .F.
         ::lScaledToHeight := .F.
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   __HmgConnectEv
   ATTENTION: this is the most generic connector. Pay attention if you want add other event
..............................................................................*/
METHOD __HmgConnectEv() CLASS BITMAP

   ::oQtObject:connect( QEvent_Close,   { |oEv| ::__HmgOnCloseExec(oEv) } )
   ::oQTObject:connect( QEvent_Resize , { || ::__HmgOnSizeExec() } )

RETURN Self

/*..............................................................................
   __HmgDisconnectEv
   ATTENTION: this is the most generic DISconnector. Pay attention if you want add other event
..............................................................................*/
METHOD __HmgDisconnectEv() CLASS BITMAP

   ::oQtObject:disconnect( QEvent_Close )
   ::oQTObject:disconnect( QEvent_Resize )

RETURN Self

/*..............................................................................
   __HmgOnSizeExec
..............................................................................*/
METHOD __HmgOnSizeExec() CLASS BITMAP

   LOCAL lQtEventStop   := .F.      // .F. means don't stop event hanlder, else .T. STOP see Harbour Changelog

   IF ::IsCreated()
      ::__HmgUpdateImage()
   ENDIF

RETURN lQtEventStop

/*..............................................................................
   __HmgUpdateImage
..............................................................................*/
METHOD __HmgUpdateImage() CLASS BITMAP

   LOCAL oQtImage, oQtWidgetSize, oQtPainter, oQtCenterPoint, oQtNewImage, nImageWidth, nImageHeight

   IF ::IsCreated() == .T.

      oQtImage := QPixmap()
      oQtImage:load( ::cPicture )

      IF oQtImage:IsNull()
         // nothing to do: image is empty
         RETURN NIL
      ENDIF

      // user want ImageWidth as the same of QLabel canvas
      IF ::ScaledToWidth() == .T. .AND. ::Stretch() == .F.
         oQtImage := oQtImage:scaledToWidth( ::oQTObject:width() )     // this keep aspect ratio
         IF oQtImage:IsNull() == .F.
            ::oQTObject:setPixmap( oQtImage )
         ENDIF
         RETURN NIL
      ENDIF

      // user want ImageWidth as the same of QLabel canvas
      IF ::ScaledToHeight() == .T. .AND. ::Stretch() == .F.
         oQtImage := oQtImage:scaledToHeight( ::oQTObject:height() )     // this keep aspect ratio
         IF oQtImage:IsNull() == .F.
            ::oQTObject:setPixmap( oQtImage )
         ENDIF
         RETURN NIL
      ENDIF

      // user want to fit as the same of QLabel canvas
      IF ::Stretch() == .T.
         nImageWidth := ::oQTObject:width()
         nImageHeight := ::oQTObject:height()
      ELSE
         nImageWidth := IIF( oQtImage:width() > ::oQTObject:width(), ::oQTObject:width(),  oQtImage:width() )
         nImageHeight := IIF( oQtImage:height() > ::oQTObject:height(), ::oQTObject:height(),  oQtImage:height() )
      ENDIF


      // this code to center image within QLabel canvas
      oQtWidgetSize  := ::oQtObject:size()
      // start painter
      oQtPainter     := QPainter()
      oQtNewImage    := QImage( oQtWidgetSize:width(), oQtWidgetSize:height(), QImage_Format_ARGB32_Premultiplied )
      oQtPainter:begin( oQtNewImage )
      oQtCenterPoint := QPoint(0,0)
      // reload and scaling image to the width and height desired
      oQtImage:load( ::cPicture )
      oQtImage := oQtImage:scaled( nImageWidth, nImageHeight, ::AspectRatio() )  //oQtImage := oQtImage:scaled( oQtWidgetSize, ::AspectRatio() )
      IF oQtImage:IsNull()
         // nothing to do: image is out of scaled
         RETURN NIL
      ENDIF
      // Calculate image center position into screen
      oQtCenterPoint:setX( ( oQtWidgetSize:width() - oQtImage:width() ) / 2 )
      oQtCenterPoint:setY( ( oQtWidgetSize:height() - oQtImage:height() ) / 2 )
      // Draw image
      oQtPainter:drawPixmap( oQtCenterPoint, oQtImage )
      // end painter
      oQtPainter:end()
      // set object PixMap
      ::oQTObject:setPixMap( QPixMap():fromImage( oQtNewImage ) )

      oQtImage       := NIL
      oQtWidgetSize  := NIL
      oQtPainter     := NIL
      oQtCenterPoint := NIL
      oQtNewImage    := NIL
   ENDIF

RETURN Self
Luigi from Italy
www.L3W.it

Ricci
Posts: 255
Joined: Thu Nov 19, 2009 2:23 pm

Post by Ricci » Tue Oct 11, 2011 6:15 pm

Luigi, your code is working well but ....

... due to the double :load() it is 50% slower.

Ricci
Posts: 255
Joined: Thu Nov 19, 2009 2:23 pm

Post by Ricci » Tue Oct 11, 2011 6:38 pm

Luigi, this modified code will work the same but as fast as before and the user has more possibilities with scaling.

The center commands can be defined with
:Alignment := LBL_CENTER
:VAlignment := LBL_VCENTER
in the user program. In this way every alignment is possible.

Code: Select all

#include "hbclass.ch"
#include "common.ch"
#include "hbqtgui.ch"
#include "hmg_qtimage.ch"
#include "hmg.ch"

/*==============================================================================
   BITMAP class
==============================================================================*/
CLASS BITMAP FROM LABEL

   DATA cClass                         INIT   "BITMAP"

// data: please preserve alphabetic order.
   DATA oImage                         INIT  NIL      PROTECTED
   DATA cPicture                       INIT  ''       PROTECTED
   DATA lScaledToHeight                INIT  .F.      PROTECTED
   DATA lScaledToWidth                 INIT  .F.      PROTECTED
   DATA lStretch                       INIT  .T.      PROTECTED
   DATA nAspectRatio                   INIT  1        PROTECTED

// Methods: please preserve alphabetic order. Methods "New" and "Create" must be the first if present
   METHOD Create
   METHOD AspectRatio                  SETGET
   METHOD Picture                      SETGET
   METHOD ScaledToHeight               SETGET
   METHOD ScaledToWidth                SETGET
   METHOD Stretch                      SETGET

   METHOD __HmgConnectEv               PROTECTED
   METHOD __HmgDisConnectEv            PROTECTED
   METHOD __HmgOnSizeExec              PROTECTED
   METHOD __HmgUpdateImage             PROTECTED

ENDCLASS

/*..............................................................................
   Create
..............................................................................*/
METHOD Create() ClASS BITMAP
   
   ::lCreated := .T.

   ::__HmgUpdateImage()

   ::__HmgConnectEv()

RETURN Self

/*..............................................................................
   AspectRatio
..............................................................................*/
METHOD AspectRatio( nValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN   ::nAspectRatio
   ELSEIF hb_IsNumeric( nValue ) == .T.
      IF nValue == Qt_IgnoreAspectRatio .OR. nValue == Qt_KeepAspectRatio .OR. nValue == Qt_KeepAspectRatioByExpanding
         ::nAspectRatio := nValue
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN Self

/*..............................................................................
   Picture
..............................................................................*/
METHOD Picture( cValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::cPicture
   ELSEIF PCOUNT() == 1
      ::cPicture := cValue
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   ScaledToHeight
..............................................................................*/
METHOD ScaledToHeight( lValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::lScaledToHeight
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lScaledToHeight := lValue
      IF ::lScaledToHeight == .T.
         ::lStretch  := .F.
//         ::lScaledToWidth := .F.
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   ScaledToWidth
..............................................................................*/
METHOD ScaledToWidth( lValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::lScaledToWidth
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lScaledToWidth := lValue
      IF ::lScaledToWidth == .T.
         ::lStretch  := .F.
//         ::lScaledToHeight := .F.
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   Stretch
..............................................................................*/
METHOD Stretch( lValue ) CLASS BITMAP

   IF PCOUNT() == 0
      RETURN ::lStretch       // RETURN ::oQTObject:hasScaledContents()
   ELSEIF hb_IsLogical( lValue ) == .T.
      ::lStretch := lValue
      ::oQTObject:setScaledContents( ::lStretch )
      IF ::lStretch == .T.
         ::lScaledToWidth  := .F.
         ::lScaledToHeight := .F.
      ENDIF
      ::__HmgUpdateImage()
   ENDIF

RETURN NIL

/*..............................................................................
   __HmgConnectEv
   ATTENTION: this is the most generic connector. Pay attention if you want add other event
..............................................................................*/
METHOD __HmgConnectEv() CLASS BITMAP

   ::oQtObject:connect( QEvent_Close,   { |oEv| ::__HmgOnCloseExec(oEv) } )
   ::oQTObject:connect( QEvent_Resize , { || ::__HmgOnSizeExec() } )

RETURN Self

/*..............................................................................
   __HmgDisconnectEv
   ATTENTION: this is the most generic DISconnector. Pay attention if you want add other event
..............................................................................*/
METHOD __HmgDisconnectEv() CLASS BITMAP

   ::oQtObject:disconnect( QEvent_Close )
   ::oQTObject:disconnect( QEvent_Resize )

RETURN Self

/*..............................................................................
   __HmgOnSizeExec
..............................................................................*/
METHOD __HmgOnSizeExec() CLASS BITMAP

   LOCAL lQtEventStop   := .F.      // .F. means don't stop event hanlder, else .T. STOP see Harbour Changelog

   IF ::IsCreated()
      ::__HmgUpdateImage()
   ENDIF

RETURN lQtEventStop

/*..............................................................................
   __HmgUpdateImage
..............................................................................*/
METHOD __HmgUpdateImage() CLASS BITMAP

   LOCAL oQtImage, oQtWidgetSize, oQtPainter, oQtCenterPoint, oQtNewImage, nImageWidth, nImageHeight

   IF ::IsCreated() == .T.

      oQtImage := QPixmap()
      oQtImage:load( ::cPicture )

      IF oQtImage:IsNull()
         // nothing to do: image is empty
         RETURN NIL
      ENDIF

	// user want streched image
      IF ::Stretch() == .T.
         ::oQTObject:setPixmap( oQtImage )
         RETURN NIL
      ENDIF

      // user always want to scale, depending on the orientation
      IF ::ScaledToWidth() == .T. .AND. ::ScaledToHeight() == .T.
         IF oQtImage:width() > oQtImage:height() 
            oQtImage := oQtImage:scaledToWidth( ::oQTObject:width() )     
         ELSE
            oQtImage := oQtImage:scaledToHeight( ::oQTObject:height() )    
         ENDIF
         IF oQtImage:IsNull() == .F.
            ::oQTObject:setPixmap( oQtImage )
         ENDIF
         RETURN NIL
      ENDIF

      // user want ImageWidth as the same of QLabel canvas
      IF ::ScaledToWidth() == .T.
         oQtImage := oQtImage:scaledToWidth( ::oQTObject:width() )     // this keep aspect ratio
         IF oQtImage:IsNull() == .F.
            ::oQTObject:setPixmap( oQtImage )
         ENDIF
         RETURN NIL
      ENDIF

      // user want ImageWidth as the same of QLabel canvas
      IF ::ScaledToHeight() == .T.
         oQtImage := oQtImage:scaledToHeight( ::oQTObject:height() )     // this keep aspect ratio
         IF oQtImage:IsNull() == .F.
            ::oQTObject:setPixmap( oQtImage )
         ENDIF
         RETURN NIL
      ENDIF

      // user want to fit as the same of QLabel canvas
      IF oQtImage:width() > ::oQTObject:width .AND. oQtImage:width() > oQtImage:height() 
         oQtImage := oQtImage:scaledToWidth( ::oQTObject:width() )     
      ELSEIF oQtImage:height() > ::oQTObject:height .AND. oQtImage:height() > oQtImage:width()
         oQtImage := oQtImage:scaledToHeight( ::oQTObject:height() )    
      ENDIF

      IF oQtImage:IsNull() == .F.
         ::oQTObject:setPixmap( oQtImage )
      ENDIF
   ENDIF

RETURN Self
Last edited by Ricci on Tue Oct 11, 2011 8:58 pm, edited 1 time in total.

Ricci
Posts: 255
Joined: Thu Nov 19, 2009 2:23 pm

Post by Ricci » Tue Oct 11, 2011 8:58 pm

Explanation about the effect of the parameters:

:Stretch := .F.
the picture ist scaled up/down in hor. and vert. direction until it fits the size of the control, proportions are usually lost, the full picture is displayed,alignment/valignment is ineffective

:Stretch := .F. + :ScaledToHeight := .T. + :ScaledToWidth := .T.
the picture ist scaled up/down in hor. or vert. direction (depending on its orientation) until the bigger size fits the size of the control, the full picture is displayed, proportions remains, alignment/valignment is effective

:Stretch := .F. + :ScaledToHeight := .T. + :ScaledToWidth := .F.
the picture ist scaled up/down in vert. direction, proportions remains, alignment is effective, hor. parts are lost if the picture is landscape

:Stretch := .F. + :ScaledToHeight := .F. + :ScaledToWidth := .T.
the picture ist scaled up/down in hor. direction, proportions remains, valignment is effective, ver. parts are lost if the picture is portrait

:Stretch := .F. + :ScaledToHeight := .F. + :ScaledToWidth := .F.
the picture ist scaled down in hor. or vert. direction (depending on its orientation) until the bigger size fits the size of the control, the full picture is displayed, proportions remains, alignment/valignment is effective, pictures that are smaller than the size of the control are not scaled up

Post Reply