Radio button selection change in MFC - mfc

Using MFC I have created a dialog, which is having 5 radio buttons. I want to get notification when some other radio-button gets selected.
For now I get notification whenever any of the radio button is clicked. But I need to gets these notification only when there is change in the radio button.
ON_BN_CLICKED(IDC_RADIO1, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO1, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO1, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO1, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO1, &CMyDlg::OnRadioButtonClicked)
Thanks

If I understand your question correctly, you want to know when the checked state of one particular radio button (IDC_RADIO1) changes.
Then store this state as a member variable in your dialog class.
int m_radio1Checked; Initialise in the constructor to 0 or 1 as you want, and use SetCheckRadioButton() appropriately in your OnInitDialog().
Then handle the clicking of each radio button in your message map:
ON_BN_CLICKED(IDC_RADIO1, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO2, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO3, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO4, &CMyDlg::OnRadioButtonClicked)
ON_BN_CLICKED(IDC_RADIO5, &CMyDlg::OnRadioButtonClicked)
and in the handler check for a change.
void CMyDlg::OnRadioButtonClicked()
{
int oldState = m_radio1Checked;
int newState = GetDlgItem(IDC_RADIO1)->GetChecked();
m_radio1Checked = newState;
if (oldState != newState)
// do something ...
}

One can use ON_CONTROL_RANGE(BN_CLICKED, firstctrlid, lastcntrlid, memberfx)

First create group of all radio buttons.
Using wizard add member variable of the integer type.
In the common handler call UpdateData() and check the value of the variable. It should be set to the value of the button in the group (zero based), starting with THE FIRST BUTTON IN THE GROUP AS 0.
Make sure that buttons are set in consecutive Z-order (tab order).

Have a separate message map for each radio button, but have a private method that all of them call to check if the selected radio button has changed.
void CDlg::OnRadioBtn1Clicked()
{
if (!RadioSelectionChanged())
return;
//Add other code here
}
//Returns true if the selected radio button has changed
Boolean CDlg::RadioSelectionChanged()
{
int previousBtnSelected = m_selectedRadioButton;
UpdateData();
if (previousBtnSelected != m_selectedRadioButton)
return true;
else
return false;
}

I suppose the simplest way to do that is storing the current button status (bool) as a member variable for the dialog class (one for each button) and then in OnRadioButtonClicked just check if the status has changed or not.
If you need to do that in multiple places you can create your own CButton derived class to encapsulate this functionality.

Related

Close button only on active tab of QTabWidget

To save space in a QTabWidget, I would like to show the close icon only for the current tab, like e.g. Firefox is doing:
Is there a simple way using a style sheet, some thing like (not working like this)
QTabBar::tab::!selected::close-button {visible: false;}
or do I have to subclass QTabWidget to get the desired behavior?
You won't need to subclass anything, you can use QTabWidget::tabBar() method to obtain a reference (i.e. QTabBar *) to the tab bar associated with your QTabWidget. (Note that this method is no longer protected, so it can be accessed without subclassing the class)
QTabBar *tabBar = tabWidget->tabBar();
You can now use tabBar reference to hide close buttons on non-current tabs. For example to hide ith button, you can do:
tabBar->tabButton(i, QTabBar::RightSide)->hide();
So a simple workflow could be as follows:
Connect QTabWidget::currentChanged(int index) signal to a slot.
In that slot hide all close buttons other than the button at index.
You can subclass QTabWidget to get access to the QTabBar widget using protected method QTabWidget::tabBar. Then you can connect to the QTabBar::currentChanged signal and hide close button for not selected tabs manually:
QTabBar::ButtonPosition closeSide =
(QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
for (int i = 0; i < toolbar->count(); ++i)
{
if (i != toolbar->currentIndex())
{
QWidget *w = toolbar->tabButton(i, closeSide);
w->hide();
}
}
hide() leaves empty space for the invisible close button. This looks funny.
Set the width to 0 instead.

Do I need to check one by one to know which radiobutton is checked in a group in Qt

I know that I can use this kind of code to know which radio button is checked in Qt:
int checkButton;
if( ui->radioButton_0->isChecked() ){
checkButton = 0;
}else if(ui->radioButton_1->isChecked()){
checkButton = 1;
}else if
...
Are there any easier way to know which radio button is checked in a group in Qt. I think it is really helpful if there is such kind of easier way when the group of radio button is large. Code may look like that:
int checkbutton = groupName.getCheckButtonIngroup();
Also we can put a few radiobuttons in groupbox in Qt Designer and after this find children of groupbox, add children to buttonGroup and use the checkedId or checkedButton methods.
void MainWindow::on_pushButton_15_clicked()
{
QButtonGroup group;
QList<QRadioButton *> allButtons = ui->groupBox->findChildren<QRadioButton *>();
qDebug() <<allButtons.size();
for(int i = 0; i < allButtons.size(); ++i)
{
group.addButton(allButtons[i],i);
}
qDebug() << group.checkedId();
qDebug() << group.checkedButton();
}
First of all you need to add all radio buttons to a button group. There are two ways to do that:
In Qt Designer select all radio buttons you want to add and select Assign to button group from the context menu.
From the code. Create a new QButtonGroup and add the buttons there with QButtonGroup::addButton.
Then anytime you can get to know what button is checked:
If you need a pointer of a checked button use QButtonGroup::checkedButton.
If you need a number of the button you need to add buttons to the group manually with addButton(QAbstractButton* button, int id). After this use QButtonGroup::checkedId to get an identifier of the checked button.

How to make a QPushButton pressable for enter key?

I want to make my app laptop friendly. I can tab to everywhere, but if I tab to a QPushButton I can't press it with Enter, only with space.
What's the problem? How to make it pressable for Enter?
tl;dr
In the Qt Creator's UI view, select the button you want to make pressable for Enter.
In the right side at the Property Editor scroll down to the blue part titled QPushButton.
Check the checkbox by autoDefault or default.
Most of the cases the main different between autoDefault and default is how the button will be rendered. But there are cases where it can cause unexpected things so for more information read more below.
Full review
Overview
Every QPushButton has 3 properties that are not inherited. From these, two (default and autoDefault) have a major role when we place buttons on QDialogs, because these settings (and the focus on one of the buttons) decides which button will be pressed if we hit Enter.
All of these properties are set false by default. Only expection is autoDefault that will be true if the button has a QDialog parent.
Every time you press space the button with the focus on it will be pressed. The followings will describe what happens if you press Enter.
Default property
If this is set true, the button will be a default button.
If Enter is pressed on the dialog, than this button will be pressed, except when the focus is on an autoDefault button.
There should be only one default button. If you add more then the last one added will be the default button.
AutoDefault property
If this is set true, the button will be an autoDefault button.
If Enter is pressed on the dialog, than this button will be pressed if the focus is on it.
If the focus is not on an autoDefault button and there is no default button than the next autoDefault button will be pressed for Enter.
Flat property
If this is set true, then the border of the button will not be raised.
Example tables
The following tables show which button will be pressed with different buttons on different focus. The buttons are added from left to right.
Test code
The following code is a way to add buttons to a dialog. It can be used for testing by changing the boolean values at setDefault() and/or setAutoDefault().
You just need to create a window, add a QPushButton called pushButton and a QLabel called label (that is the default naming). Don't forget to #include <QMessageBox>. Then copy this code to the button's clicked() signal:
void MainWindow::on_pushButton_clicked()
{
QMessageBox msgBox;
QPushButton button("Button");
button.setDefault(false);
button.setAutoDefault(false);
msgBox.addButton(&button, QMessageBox::ActionRole);
QPushButton autodefaultbutton("AutoDefault Button");
autodefaultbutton.setDefault(false);
autodefaultbutton.setAutoDefault(true);
msgBox.addButton(&autodefaultbutton, QMessageBox::ActionRole);
QPushButton autodefaultbutton2("AutoDefault Button2");
autodefaultbutton2.setDefault(false);
autodefaultbutton2.setAutoDefault(true);
msgBox.addButton(&autodefaultbutton2, QMessageBox::ActionRole);
QPushButton defaultbutton("Default Button");
defaultbutton.setDefault(true);
defaultbutton.setAutoDefault(false);
msgBox.addButton(&defaultbutton, QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == &button) {
ui->label->setText("Button");
} else if (msgBox.clickedButton() == &defaultbutton) {
ui->label->setText("Default Button");
} else if (msgBox.clickedButton() == &autodefaultbutton) {
ui->label->setText("AutoDefault Button");
} else if (msgBox.clickedButton() == &autodefaultbutton2) {
ui->label->setText("AutoDefault Button2");
}
}
Display
If you compile the code you can get this window. You doesn't even have to click to the buttons because the way they are rendered by the OS shows which one will be pressed if you hit Enter or space.
Official documentation
Most of this answer was made according to the official documentation.
The QPushButton documentation made by Qt states these:
Default and autodefault buttons decide what happens when the user
presses enter in a dialog.
A button with this property set to true (i.e., the dialog's default
button,) will automatically be pressed when the user presses enter,
with one exception: if an autoDefault button currently has focus, the
autoDefault button is pressed. When the dialog has autoDefault buttons
but no default button, pressing enter will press either the
autoDefault button that currently has focus, or if no button has
focus, the next autoDefault button in the focus chain.
In a dialog, only one push button at a time can be the default button.
This button is then displayed with an additional frame (depending on
the GUI style).
The default button behavior is provided only in dialogs. Buttons can
always be clicked from the keyboard by pressing Spacebar when the
button has focus.
If the default property is set to false on the current default button
while the dialog is visible, a new default will automatically be
assigned the next time a pushbutton in the dialog receives focus.
It's also worth to check QDialog and QMessageBox.
According to Qt's documentation Enter should work
Command buttons in dialogs are by default auto-default buttons, i.e. they become the default push button automatically when they receive the keyboard input focus. A default button is a push button that is activated when the user presses the Enter or Return key in a dialog. You can change this with setAutoDefault().
http://qt-project.org/doc/qt-4.8/qpushbutton.html
totymedli's answer is great. I added a small program to test various combinations of isDefault, autoDefault, setDefault and setAutoDefault functions.
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
autoDefaultInitialState = True
defaultInitialState = False
self.lineEdit1 = QLineEdit(self)
self.lineEdit2 = QLineEdit(self)
self.lineEdit3 = QLineEdit(self)
# if we create a new button (e.g. "Print state"), with the same function,
# it doesn't work, because adding a new button (apart from our 3 buttons)
# produces total mess, so we use this lineedit for this purpose
self.lineEdit1.returnPressed.connect(self.printState)
#------------------------------------
self.chkAutoDefaultOk = QCheckBox('OK setAutoDefault', self)
self.chkAutoDefaultCancel = QCheckBox('Cancel setAutoDefault', self)
self.chkAutoDefaultApply = QCheckBox('Apply setAutoDefault', self)
self.chkDefaultOk = QCheckBox('OK setDefault', self)
self.chkDefaultCancel = QCheckBox('Cancel setDefault', self)
self.chkDefaultApply = QCheckBox('Apply setDefault', self)
self.chkAutoDefaultOk.setChecked(autoDefaultInitialState)
self.chkAutoDefaultCancel.setChecked(autoDefaultInitialState)
self.chkAutoDefaultApply.setChecked(autoDefaultInitialState)
self.chkDefaultOk.setChecked(defaultInitialState)
self.chkDefaultCancel.setChecked(defaultInitialState)
self.chkDefaultApply.setChecked(defaultInitialState)
#------------------------------------
self.pushButtonOk = QPushButton(self)
self.pushButtonOk.setText("Ok")
self.pushButtonOk.clicked.connect(lambda : print('ok'))
self.pushButtonCancel = QPushButton(self)
self.pushButtonCancel.setText("Cancel")
self.pushButtonCancel.clicked.connect(lambda : print('cancel'))
self.pushButtonApply = QPushButton(self)
self.pushButtonApply.setText("Apply")
self.pushButtonApply.clicked.connect(lambda : print('apply'))
#------------------------------------
self.pushButtonOk.setAutoDefault(autoDefaultInitialState)
self.pushButtonCancel.setAutoDefault(autoDefaultInitialState)
self.pushButtonApply.setAutoDefault(autoDefaultInitialState)
self.pushButtonOk.setDefault(defaultInitialState)
self.pushButtonCancel.setDefault(defaultInitialState)
self.pushButtonApply.setDefault(defaultInitialState)
#------------------------------------
self.chkAutoDefaultOk.stateChanged.connect(self.chkChangeState)
self.chkAutoDefaultCancel.stateChanged.connect(self.chkChangeState)
self.chkAutoDefaultApply.stateChanged.connect(self.chkChangeState)
self.chkDefaultOk.stateChanged.connect(self.chkChangeState)
self.chkDefaultCancel.stateChanged.connect(self.chkChangeState)
self.chkDefaultApply.stateChanged.connect(self.chkChangeState)
#------------------------------------
self.layout = QGridLayout(self)
self.layout.addWidget(self.lineEdit1, 0, 0, 1, 3)
self.layout.addWidget(self.lineEdit2, 1, 0, 1, 3)
self.layout.addWidget(self.lineEdit3, 2, 0, 1, 3)
self.layout.addWidget(self.chkAutoDefaultOk, 3, 0)
self.layout.addWidget(self.chkAutoDefaultCancel, 3, 1)
self.layout.addWidget(self.chkAutoDefaultApply, 3, 2)
self.layout.addWidget(self.chkDefaultOk, 4, 0)
self.layout.addWidget(self.chkDefaultCancel, 4, 1)
self.layout.addWidget(self.chkDefaultApply, 4, 2)
self.layout.addWidget(self.pushButtonOk, 5, 0)
self.layout.addWidget(self.pushButtonCancel, 5, 1)
self.layout.addWidget(self.pushButtonApply, 5, 2)
def chkChangeState(self):
obj = self.sender()
if (obj == self.chkAutoDefaultOk):
self.pushButtonOk.setAutoDefault(self.chkAutoDefaultOk.isChecked())
elif (obj == self.chkAutoDefaultCancel):
self.pushButtonCancel.setAutoDefault(self.chkAutoDefaultCancel.isChecked())
elif (obj == self.chkAutoDefaultApply):
self.pushButtonApply.setAutoDefault(self.chkAutoDefaultApply.isChecked())
elif (obj == self.chkDefaultOk):
self.pushButtonOk.setDefault(self.chkDefaultOk.isChecked())
elif (obj == self.chkDefaultCancel):
self.pushButtonCancel.setDefault(self.chkDefaultCancel.isChecked())
elif (obj == self.chkDefaultApply):
#print('sender: self.chkDefaultApply')
#print('before: ', self.pushButtonApply.isDefault())
self.pushButtonApply.setDefault(self.chkDefaultApply.isChecked())
#print('after: ', self.pushButtonApply.isDefault())
def printState(self):
print(self.pushButtonOk.autoDefault(), self.pushButtonCancel.autoDefault(), self.pushButtonApply.autoDefault())
print(self.pushButtonOk.isDefault(), self.pushButtonCancel.isDefault(), self.pushButtonApply.isDefault())
print('-' * 50)
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Set the tab order for your widgets. This will allow usage of return key for clicking. Its in there by default inside Qt.

Qt - Remembering the last checked Radiobutton

I have 3 radio buttons and a spinbox. Each radio button is supposed to chnage the spinboxes value to inches, feet or meters respectively. How would i do that? I would have to know which radio button was active last, right? Could someone give me some example code please?
Radio Button and check boxes hold their state as value, and expose it to your user. You can call isChecked() to know the state of your member variable radio button. Something like
void some_button_hit(bool checked)
{
if (inches.isChecked()) {...}
else if (feet.isChecked()) {...}
else if (meters.isChecked()) {...}
}
and 'wire' the event toggled from any of these. This is usually done in initialization,
using the form editor, or via connect.
Using the form editor, right click on a radio button, and follow goto slot...
The editor build and connect the event handler:
void GroupBox::on_feet_toggled(bool checked)
{
ui->label->setText("feet");
}
void GroupBox::on_inches_toggled(bool checked)
{
ui->label->setText("inches");
}
void GroupBox::on_meters_toggled(bool checked)
{
ui->label->setText("meters");
}

how to use ribbon category to switch view?

I want to use two ribbon category buttons to switch between two different views, but I found it can't add event handler to the button.
Is there any hint to solve this problem? Better if there is some sample, actually I'm new to MFC.
You could try hooking into this event?
AFX_WM_ON_CHANGE_RIBBON_CATEGORY
An option I have found successful was to subclass CMFCRibbonBar and override PreTranslateMessage and check for mouse clicks. Below are the steps I took which have thus far worked well.
Subclass CMFCRibbon - in my example I created CCustomRibbonBar
Override PreTranslateMessage, and add an int value to keep track of the tab
Create a custom windows message that your applications MainForm handles -WM_ACTIVE_RIBBON_TAB_CHANGED in my example
Inside of PreTranslateMessage check for Left Mouse Up event
In the event of a left mouse button up, let the Ribbon finish handling the message and then query the Active Category.
Post the active category to MainForm (or other form)
In your MainForm, handle the category and take into account that with most events the category will not have changed.
Then in my override I check for the mouse up event and retrieve the Active category
Inside Class Declaration
virtual BOOL PreTranslateMessage(MSG* pMsg);
int m_LastActiveCategory;
Inside Class Definition
BOOL CCustomRibbonBar::PreTranslateMessage(MSG* pMsg)
{
//If command was finishing a click
if(pMsg->message == WM_LBUTTONUP && pMsg->wParam == 0)
{
//Allow ribbon to handle itself first
BOOL result = CMFCRibbonBar::PreTranslateMessage(pMsg);
//Get new active tab
int activeTab = GetCategoryIndex(GetActiveCategory());
//If tab has changed, forward message
if(activeTab != m_LastActiveCategory)
{
//forward message to parent
::SendMessage(GetParentFrame()->GetSafeHwnd(),WM_ACTIVE_RIBBON_TAB_CHANGED,activeTab,0);
m_LastActiveCategory = activeTab;
}
return result;
}
//Otherwise handle like normal
return CMFCRibbonBar::PreTranslateMessage(pMsg);
}