Code - qt custom input dialog

From NoskeWiki
Jump to: navigation, search

About

The Qt framework is an versatile cross-platform application framework which can help you design beautiful graphical user interfaces which should (with no or minimum alterations) compile onto any type of computer (Windows/Mac/Linux/etc) or even several mobile devices. Qt includes many classes for common form elements and windows, but in many of the GUIs I make I find I need dozen of little modal input forms which pop-up, require the user to enter several key values, and then update these value if the user clicks "Okay". Sound familiar? To write separate Window GUIs for each input form can get very tedious, so here I've written a class which can help you reduce the code required to generate custom modal input dialogs into just a few lines. Here's an example where I generate a form with five main "form elements" in just over five lines of code:

string  name = "First Last";            // NOTE: these lines of code (the variables you wish to change) 
bool    student  = true;                //  probably exist in your program already and so it is only 
int     age      = 20;                  //  the seven lines below needed to "create and display"
int     sportIdx = 1;                   //  your custom dialog.
 
CustomDialog d("Registration", this);                            // We want our custom dialog called "Registration".
d.addLabel    ("Please enter your details below ...");           // The first element is just a text label (non interactive).
d.addLineEdit ("name:  ", &name, "No middle name!");             // Here's a line edit.
d.addCheckBox ("current student", &student, "my tooltip");       // Here's a checkbox with a tooltip (optional last argument).
d.addSpinBox  ("your age: ", 1, 120, &age, 1);                   // Here's a number spin box for age.
d.addComboBox ("sport: ", "tennis|soccer|cricket", &sportIdx);   // And here's a combo box with three options (separated by '|').
 
d.exec();                             // Execution stops here until user closes dialog
 
if(d.wasCancelled())                // If the user hit cancel, your values stay the same
  return;                           // and you probably don't need to do anything
cout << "Thanks " << name << end;   // and here it's up to you to do stuff with your new values!


The class works is to build up a vector of DialogElement value, then create the corresponding form. If the user hits "Okay", the variables you passed as pointers will get changed from their previous ("default") value to whatever you specify. Generating the entire form elements "on the fly" like this will take a little longer in processing time than hard coding every form as a separate class, but (a) nobody wants to program hundreds of separate classes/files for all their forms and (b) we're talking a fraction of a millisecond here - so you won't even notice. Instead you really only need to write one line of code to create an empty dialog, then a single line for each element you need to add.  :)

All elements you add will be in a "normal" form layout which is pretty much a "two column" layout with a label on the left, then the actual input element (eg: combobox/text edit/etc) on the right.

Disclaimer:

  • This code works with Qt4.6.3... it may require a few minor modification to work with later versions. Between 4.6 and 4.7 there weren't TOO many changes, but one thing you probably will have to change is the "ColorButton" class by going to the .cpp and swapping some of the commented out lines.
  • The only drawback of this CustomDialog is that it will only work as a modal dialog, meaning you won't be able to see your window change as you drag sliders in your custom form. Values only change after you click enter, but this is what you'll want 90% of the time, and for the other 10% you can program your own class - I don't really see much way around this unless I somehow found a nice way to use an "observer" class to call a user-specified function whenever the user changes an element's value.... maybe in the future.


Functionality

My "CustomDialog" class here started pretty simple, just allowing labels, checkboxes, comboboxes and single line text input.... but now it includes a much larger collection including a "color picker" button, html enabled labels to allow hyperlinks, and even lets you add group boxes and you own custom buttons down the bottom. Most recently I've allowed you to set certain elements like checkboxes to show/hide other elements and other functions to change the stylesheet of elements. Below I've listed the functions you can call to show what's possible.

  • Basic dialog elements:
    • addLabel (caption, bold, tooltip)
    • addHtmlLabel (caption, tooltip)
    • addCheckBox (caption, *checked, tooltip)
    • addLineEdit (caption, *stringValue, tooltip)
    • addReadOnlyLineEdit (caption, text, tooltip)
    • addLineEditF (caption, min, max, *value, decimals, tooltip, unitsStr)
    • addSpinBox (caption, min, max, *value, step, tooltip)
    • addDblSpinBoxF (caption, min, max, *value, decimals, step, tooltip)
    • addComboBox (caption, barSepList, *selIdx, tooltip)
    • addRadioGrp (caption, barSepList, *selIdx, tooltip, tooltipArr, checkable, *checked)
    • addColorSel (caption, *color, tooltip)
    • addMinMaxSpinBoxPair (caption, middleCaption, min, max, *minValue, *maxValue, step, tooltip)
    • addTextEdit (string *text, richText, readOnly, minHeight, tooltip)
    • addReadOnlyTextEdit (text, richText, minHeight, tooltip)
    • addProgressBar (caption, percent, width, showValue, tooltip)
    • addPercentBar (caption, valueLabel, percent, width, colorBar, tooltip,shape, shadow)
    • addVSpacer (minHeight)
  • Other methods:
    • beginGroupBox (caption, flat, tooltip, checkable, *checked)
    • endGroupBox ()
    • addCheckPrev (caption, *checked, chkBeh, removeLabel, tooltip)
    • addAutoCompletePrev (wordList, caseSensitive)
    • setStyleElem (idx, styleStr, bold)
    • setStylePrev (styleStr, bold)
    • setEnabledElem (idx, enabled)
    • setEnabledPrev (enabled)
    • setEnabledAll (enabled)
    • addCustomButton (buttonStr, buttonBehav, tooltip);


Note that I have omitted the data types above (to make it easier to read), but if you scroll down you'll see the functions themselves in the code below. In "addLineEdit" and "addTextEdit" I use the STL "string" class to input/outupt a pointer to a string, but for almost every other string I've used Qt's QString. If you look below you'll also notice I include several little Qt helper functions:

  • MsgBox (str) ........... // just a shorter function call to open a message box
  • MsgBoxYesNo (parent,str) .... // lets you display a "yes or no" message box in one line (returns true for yes)
  • InputBoxString (parent,title,label,defaultStr) ...... // to get an input string in one line
  • QStr (number) ........................... // will convert different types of numbers to a QString
  • qStringToString (qstr) .................. // will convert a QString to c++ string - let me know if there's a better method!
  • setBold (wid, bold) ....................... // makes the specified widget bold (or not bold)
  • setTextColor (wid,r,g,b) ................. // sets the text color of the specified widget
  • setDefaultColorAndFont (wid) ....... // sets the text color to black and background white
  • openUrl (urlString,addFilePrefix) ....... // can be used to open the specified or any file on your machine with whatever the default program is (eg: .xls in Excel)




CustomDialog.h

Below are the contents of "customdialog.h"

#ifndef CUSTOMDIALOG_H
#define CUSTOMDIALOG_H
 
//############################################################
//  
//  This file contains a special class, "CustomDialog", for generating
//  a modal dialog box with multiple form elements/inputs including:
//  labels, checkboxes, line edit boxes, number edit boxes, combo boxes,
//  radio buttons, spin boxes (for ints or floats), group boxes (to group elements)
//  and even "pick color" buttons (letting you chose a color).
//  
//  NOTES:
//    
//    > Each form element is created by a single function only, includes an optional tooltip,
//      and a pointer to the variable you wish to modify.
//      
//    > The value of the variable is updated ONLY if and when hits "Ok", and will change only
//      if the user has changed it from the original/default/starting value in that variable.
//
//    > This class if fantastic for making modal dialogs (i.e. dialogs where you can't click 
//      anything else until you click okay or cancel), but isn't designed for times when you 
//      want/need the parent dialog to dymaically update as the user changes values
//       (i.e. interactive changes).
//    
//    > This method of generating dialog elements on-the-fly by using a vectors of structs is 
//      obviously less versatile than creating a seperate class which extend QDialog for EACH
//      dialog you need. The huge advantage, however, is you can can greatly reduce your code 
//      and avoid the tedium of creating a seperate .h and .cpp file everytime you wants to 
//      prompt the user for multiple pieces of info in a single modal dialog!
//    
//    > This file also includes several "convinience" functions such as "MsgBoxYesNo" and 
//      "setBold" for quickly displaying dialogs/returning results and modifying strings.
//      These convinience functions are at the bottom of this file (and defined in the .cpp).
//      
//  -----------------
//  
//  An example of how to use this dialog follows:
//  
//    void GetDetailsFromUser()
//    {
//      string  name     = "First Last";
//      bool    student  = true;
//      int     age      = 20;
//      int     sportIdx = 1;
//      
//      CustomDialog d("Registration", this);
//      d.addLabel    ("Please enter your details below ...");
//      d.addLineEdit ("name:  ", &name, "No middle name!");
//      d.addCheckBox ("current student", &student);
//      d.addSpinBox  ("your age: ", 1, 120, &age, 1);
//      d.addComboBox ("sport: ", "tennis|soccer|cricket", &sportIdx);
//      
//      d.exec();                    // Execution stops here until user closes dialog
//      
//      if(d.wasCancelled()) {
//        cout << "You have cancelled your registration" << endl;
//        return;
//      }
//      
//      cout << "Dear " << name << " thank you for registering..." << endl;
//      
//      ... CODE ...
//    }
//  
//  -----------------
//  
//  And produces a dialog which looks like this:
//  
//    +----------------------------------------+
//    | Registration                       ? X |
//    +----------------------------------------+
//    | Please enter your details below ...    |
//    |        _____________________________   |
//    | name: |__First_Last_________________|  |
//    |                                        |
//    | [X] current student                    |
//    |                                _____   |
//    | your age:                     [_22_^]  |
//    |                       ______________   |
//    | sport:               |_soccer_____[V]  |
//    |                                        |
//    |   +-------------+    +-------------+   |
//    |   |     Ok      |    |   Cancel    |   |
//    |   +-------------+    +-------------+   |
//    +----------------------------------------+
//  
//
// 
//  > author:       Andrew Noske
//  > last updated: 6-June-2012
// 
// http://www.andrewnoske.com/wiki/index.php?title=Code_-_qt_custom_input_dialog
//############################################################
 
 
#include <iostream>
#include <vector>
#include <string>
 
using namespace std;
 
#include <qdialog.h>
#include <qobject.h>
#include <qvariant.h>
#include <qaction.h>
#include <qapplication.h>
#include <qpushbutton.h>
#include <qcheckbox.h>
#include <qlabel.h>
#include <qcombobox.h>
#include <qbuttongroup.h>
#include <qradiobutton.h>
#include <qdialog.h>
#include <qspinbox.h>
#include <qlayout.h>
#include <qgroupbox.h>
#include <qtextedit.h>
#include <qprogressbar.h>
#include <qtooltip.h>
#include <qstringlist.h>
#include <qmessagebox.h>
#include <qinputdialog.h>
#include <qcompleter.h>
#include <QHBoxLayout>
#include <QLabel>
#include <QVBoxLayout>
 
#include <QDesktopServices>
#include <QDir>
#include <QUrl>
 
//############################################################
 
//## CONSTANTS:
 
enum DlgType { DLG_LABEL, DLG_CHECKBOX, DLG_LINEEDIT, DLG_FLOATEDIT,
  DLG_SPINBOX, DLG_DBLSPINBOX, DLG_MINMAXSPIN,
  DLG_COMBOBOX, DLG_RADIOGRP, DLG_GRPBOX, DLG_COLOR, DLG_TEXTEDIT, DGL_ALL };
 
enum chkbehav { CB_NONE, CB_DISABLE, CB_ENABLE, CB_HIDE, CB_SHOW };
 
enum btnset   { BS_CANCEL_OKAY, BS_OKAY_ONLY, BS_NO_YES, BS_CUSTOM };
 
enum btnbehav { BB_ACCEPT, BB_REJECT, BB_DISABLED, BB_POPUP, BB_OPENURL };
 
//############################################################
 
//## SMALL CLASSES:
 
class ColorButton : public QPushButton    // Used to create a "pick color" button.
{
  Q_OBJECT      // NOTE: To compile on my version of IMOD I have to comment these out.
 
public:
  QColor color;
  ColorButton(QColor _color, QWidget *parent=0);
  void setColor(QColor _color);
  QColor getColor();
  public slots:
  void pickColor();
  //public signals:
  //  void valueChanged() {};
};
 
//############################################################
 
//************************
//** DialogElement is used to store gui widgets in the array of 
//** widgets displayed in GuiDialogCustomizable
 
struct DialogElement
{
  DlgType type;                 // The "type" of dialog element displayed this row.
  bool    extraChkAdded;        // Set true if a special extra checkbox is added.
  //  using CustomDialog.addCheckPrev()
 
  //** POINTERS USE TO PASS BACK ANY CHANGED VALUES:
 
  string  *returnString;        // For DLG_LINEEDIT.
  int     *returnInt;           // For DLG_SPINBOX, DLG_COMBOBOX & DLG_RADIOGRP.
  int     *returnInt2;          // For DLG_DBLSPINBOX.
  bool    *returnBool;          // For DLG_CHECKBOX.
  float   *returnFloat;         // For DLG_FLOATEDIT & DLG_DBLSPINBOX.
  QColor  *returnColor;         // For DLG_COLOR.
  bool    *returnChkExtra;      // Fsed if extraChkAdded is true.
 
  bool readOnly;                // if set to true, user cannot change the text.
 
  //** FORM ELEMENTS TO DISPLAY (DEPENDING ON TYPE):
 
  QWidget        *wid;
  QHBoxLayout    *layout;
 
  QLabel         *label;        
  QLabel         *label2;
  QCheckBox      *chkBox;
  QLineEdit      *lineEdit;
  QSpinBox       *spnBox;
  QSpinBox       *spnBox2;
  QDoubleSpinBox *dblSpnBox;
  QComboBox      *cmbBox;
  ColorButton    *btnColor;
  vector<QRadioButton*> radBtn;
  QGroupBox      *grpBox;
  QTextEdit      *textEdit;
 
  QCheckBox      *chkExtra;
};
 
//############################################################
 
//************************
//** GuiDialogCustomizable is used to present a customizable gui
//** dialog and retrieve user input with minimal code!
 
class CustomDialog : public QDialog                                             
{
  Q_OBJECT
 
public:     //## METHODS:
 
  CustomDialog(QString title, QWidget *parent = 0, btnset=BS_CANCEL_OKAY);
  ~CustomDialog() {};
  bool setDialogElements();
  bool wasCancelled();
 
  bool addCustomButton(QString buttonStr, btnbehav buttonBehav=BB_ACCEPT, QString tooltip="");
 
 
  DialogElement& addNewElement(DlgType _type, QString caption, QString tooltip, bool makeLabel);
  int addLabel(QString caption, bool bold=false, QString tooltip="");
  int addHtmlLabel(QString caption, QString tooltip="");
  int addCheckBox(QString caption, bool *checked, QString tooltip="");
  int addLineEdit(QString caption, string *stringValue, QString tooltip="");
  int addReadOnlyLineEdit(QString caption, QString text, QString tooltip="");
  int addLineEditF(QString caption, float min, float max, float *value, float decimals,  QString tooltip="", QString unitsStr="");
  int addSpinBox(QString caption, int min, int max, int *value, int step, QString tooltip="");
  int addDblSpinBoxF(QString caption, float min, float max, float *value, int decimals, float step=0.1, QString tooltip="");
  int addComboBox(QString caption, QString barSepList, int *selIdx, QString tooltip="");
  int addRadioGrp(QString caption, QString barSepList, int *selIdx, QString tooltip="", QString tooltipArr="", bool checkable=false, bool *checked=0);
  int addColorSel(QString caption, QColor *color, QString tooltip="");
  int addMinMaxSpinBoxPair(QString caption, QString middleCaption, int min, int max, int *minValue, int *maxValue, int step=1, QString tooltip="");
  int addTextEdit(string *text, bool richText, bool readOnly, int minHeight=90, QString tooltip="");
  int addReadOnlyTextEdit(QString text, bool richText, int minHeight=90, QString tooltip="");
  int addProgressBar(QString caption, int percent, int width, bool showValue, QString tooltip="");
  int addPercentBar(QString caption, QString valueLabel, float percent, int width, QColor colorBar, QString tooltip="", QFrame::Shape shape = QFrame::StyledPanel, QFrame::Shadow shadow = QFrame::Sunken);
  int addVSpacer(int minHeight=0);
 
  int beginGroupBox(QString caption, bool flat=false, QString tooltip="", bool checkable=false, bool *checked=0);
  void endGroupBox();
 
  int addCheckPrev(QString caption, bool *checked, chkbehav chkBeh, bool removeLabel, QString tooltip="");
  int addAutoCompletePrev(QStringList wordList, bool caseSensitive=false);
  bool setStyleElem(int idx, string styleStr, bool bold=false);
  void setStylePrev(string styleStr, bool bold=false);
 
  bool setEnabledElem(int idx, bool enabled);
  void setEnabledPrev(bool enabled);
  void setEnabledAll(bool enabled);
 
 
 
public:       //## DATA:
 
  vector<DialogElement> elements;     // The vector of GUI elements used to display
                                      //   and change the values.
  int customBtnClicked;               // Set to the index of the button
                                      //   "customBtn" clicked.
 
private: 
 
  vector<QPushButton*> customBtn;      // Vector of buttons down the button of the GUI.
  QVBoxLayout *vboxLayout;
  QHBoxLayout *hbtnLayout;
 
  bool addToGroupBox;
  QVBoxLayout *groupBoxLayout;
  QVBoxLayout *layoutNextElement;
 
public slots:   //## SLOTS:
 
  void customBtnAccept();
  void customBtnReject();
  void customBtnMessage();
  void customBtnOpenUrl();
  void updateBtnClicked(QObject *btnClicked);
  void resizeMe();
  int exec();
};
 
 
//############################################################
 
 
//-------------------------------
//## SMALL MESSAGE BOX FUNCTIONS:
 
void MsgBox(string str);
void MsgBox(QWidget *parent, QString title, QString str);
bool MsgBoxYesNo(QWidget *parent, string str);
string InputBoxString(QWidget *parent, string title, string label, string defaultStr);
 
//-------------------------------
//## SMALL INLINE GUI FUNCTIONS:
 
inline QString QStr(int number  );
inline QString QStr(long number );
inline QString QStr(float number);
inline QString QStr(double number);
inline string qStringToString(QString qstr);
inline QString nbsp(int numSpaces);
inline void setBold(QWidget *wid, bool bold);
inline void setTextColor(QWidget *wid, int r, int g, int b);
inline void setDefaultColorAndFont(QWidget *wid);
inline void openUrl(QString urlString, bool addFilePrefix=false);
 
//############################################################
 
//----------------------------------------------------------------------------
//
//          SMALL INLINE GUI FUNCTIONS:
//
//----------------------------------------------------------------------------
 
 
 
 
//---------
//-- Short function name for converting numbers to a QString.
 
inline QString QStr(int number)     {  return QString::number(number);  }
inline QString QStr(long number)    {  return QString::number(number);  }
inline QString QStr(float number)   {  return QString::number(number);  }
inline QString QStr(double number)  {  return QString::number(number);  }
 
//---------
//-- Converts a QString to a standard string
 
inline string qStringToString(QString qstr)
{
  string str = "";
  for(int i=0; i<qstr.length(); i++)
    str +=  qstr.at(i).toLatin1();
  return str;
}
 
//---------
//-- Creates a qstring with the specified number
//-- of "non breaking space" HTML characters
 
inline QString nbsp(int numSpaces)
{
  QString str = "";
  for(int i=0; i<numSpaces; i++)
    str +=  "&nbsp;";
  return str;
}
 
//---------
//-- Short function name for taking any widget and making the text in it bold.
 
inline void setBold(QWidget *wid, bool bold)
{
  QFont font;
  font.setBold(bold);
  wid->setFont(font);
}
 
//---------
//-- Short function name for setting the text (forground) color of a widget.
 
inline void setTextColor(QWidget *wid, int r, int g, int b)
{
  wid->setStyleSheet("color: rgb(" + QStr(r) + "," + QStr(g) + "," + QStr(b) + ");");
}
 
 
//---------
//-- Short function which sets the font to default, the foreground/text color to black
//-- and background to transparent. This function is useful for when you might apply a
//-- stylesheet to a container object, but you don't want those changes to apply to 
//-- (heirarchially) to widgets within it.
 
inline void setDefaultColorAndFont(QWidget *wid)
{
  wid->setFont(QFont());
  wid->setStyleSheet("color: rgb(0, 0, 0); background-color: rgba(255, 255, 255, 0);");
}
 
//---------
//-- Opens the given URL ("urlString") in the default web browser.
//-- If ("addFilePrefix") is true, the string "file://" is added to the
//-- front and, in most operating systems (including OSX), this will
//-- mean the specified file on the computer should be opened
//-- in the default program (eg: a .xls file open in Excel) instead.
 
inline void openUrl(QString urlString, bool addFilePrefix)
{
  if(addFilePrefix)
    urlString = "file://" + urlString;
  QDesktopServices::openUrl(QUrl(urlString));  
}
 
#endif



CustomDialog.cpp

Below are the contents of "customdialog.cpp"

Code_-_qt_custom_input_dialog_cpp



See Also

Links

  • Qt Library - used to be Trolltech, but now owned by Nokia