I have three controls, two QTextLine and one QPushButton.
When startup the program, the add button will be disabled, and must be two QTextLine not empty for enable the add button.
I have the following code, but it does not works fine:
void Question_Answer::on_newQuestion_txt_textChanged(const QString &arg1)
{
if(arg1.isEmpty())
{
ui->addNewQuestion_btn->setEnabled(false);
}
else
{
ui->addNewQuestion_btn->setEnabled(true);
}
}
void Question_Answer::on_newAnswer_txt_textChanged(const QString &arg1)
{
if(ui->newAnswer_txt->text().isEmpty())
{
ui->addNewQuestion_btn->setEnabled(false);
}
else
{
ui->addNewQuestion_btn->setEnabled(true);
}
}
Now, How to check if the two QTextLine are not empty, and also if any of them is empty, how to disable the add button.
Just connect a single slot to handle textChanged signals of both LineEdits
void Question_Answer::onTextChanged(const QString &arg1){
if(ui->newAnswer_txt->text().isEmpty() || ui->newQuestion_txt->text().isEmpty()){
ui->addNewQuestion_btn->setEnabled(false);
}else{
ui->addNewQuestion_btn->setEnabled(true);
}
}
In the header class:
// ...
private slots:
void onTextChanged();
// ...
In the source file:
// Setup the connections in the constructor.
void Question_Answer::onTextChanged()
{
const bool editable1 = ui->newAnswer_txt->text().size() > 0;
const bool editable2 = ui->newQuestion_txt->text().size() > 0;
ui->addNewQuestion_btn->setEnabled( editable1 && editable2 );
}
Related
I'm calling an undo command from within QML:
cppClass.undoHandler.createCommand(option)
The C++ undo-handler pushes undo command into undo stack:
void UndoHandler::createCommand(
const QString & option
)
{
m_undoStack->push(new Command(
option
));
}
The actual C++ undo command is:
class Command : public QUndoCommand
{
// ...
virtual void undo();
virtual void redo();
// ...
Logic *m_logic;
}
void Command::redo()
{
m_outputName = m_logic->run(m_option);
}
And the logic runs like this:
QString Logic::run(const QString option)
{
if ( /* some condition here */ ) {
// ** What I want to achieve:
// If this condition is met
// abort the current undo-command
// I mean pop the current undo-command which is already pushed into stack
}
}
How can I abort/pop/break the undo-command by the above condition inside the logic? I'm not sure how it should be designed, any idea?
You can use QUndoCommand::setObsolete() and make the command obsolete.
The boolean is used for the automatic removal of commands that are not necessary in the stack anymore. The isObsolete function is checked in the functions QUndoStack::push(), QUndoStack::undo(), QUndoStack::redo(), and QUndoStack::setIndex().
I used the if condition before pushing undo command into stack like this:
void UndoHandler::createCommand(
const QString & option
)
{
Command *command = new Command(option);
// Check validity before pushing undo command into undo stack
bool isValid = command->doubleCheck();
if (isValid) {
m_undoStack->push(command);
} else {
delete command;
}
}
Undo command has a new method to check the if condition:
class Command : public QUndoCommand
{
// ...
virtual void undo();
virtual void redo();
// ...
// New method to check validity
bool doubleCheck();
// ...
Logic *m_logic;
const QString m_option;
}
bool Command::doubleCheck() {
return m_logic->doubleCheck(m_option);
}
void Command::redo()
{
// Redo method has to be modified accordingly
}
The if condition is inside a separate method in logic:
bool Logic::doubleCheck(const QString option)
{
if ( /* some condition here */ )
return true;
else
return false;
}
I have an overloaded QTreeWidget class, with my SIGNALS: I have promoted it in my UI and when I listen promoted QTreeWidget object with a lambda syntax I have an error.
QObject::connect: signal not found in CustomTreeWidget.
MY CustomTreeWidget looks like:
.h
class CustomTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
explicit CustomTreeWidget(QWidget *parent = 0);
~CustomTreeWidget() {
}
signals:
void currentNodeChanged(QSet<int> uids);
void deleteRequest(QVector<int> uids);
}
.cpp
CustomTreeWidget::CustomTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
setAnimated(true);
connect(this, &CustomTreeWidget::customContextMenuRequested, this, [=](const QPoint &pos) {
this->m_bCustomMenuOpen = true;
const auto &&item = this->itemAt(pos);
QMenu myMenu;
bool ok = !(item) ? false : true;
if (ok) {
//თუ topLevelItem -ია მხოლოდ დამატების action -ი უნდა იყოს ჩართული.
if (item == this->topLevelItem(0) || item == this->topLevelItem(0)->child(0)) {
ok = false;
}
}
QAction *Removecnt = myMenu.addAction(tr("&წაშლა"), this, SLOT(DeleteNode()));
Removecnt->setIcon(QIcon(":/global_res/delete.png"));
Removecnt->setEnabled(ok);
myMenu.exec(this->mapToGlobal(pos));
});
}
void CustomTreeWidget::BFS(QTreeWidgetItem *item, QSet<int> &out)
{
std::queue<QTreeWidgetItem *> Q;
Q.push(item);
while (!Q.empty()) {
QTreeWidgetItem *now = Q.front(); Q.pop();
out.insert(this->m_mapUids[now]);
for (int i = 0; i < now->childCount(); i++) {
Q.push(now->child(i));
}
}
}
QSet<int> CustomTreeWidget::GetCurrentNodeUids()
{
QSet<int> uids;
if (!this->currentItem())
return uids;
this->BFS(this->currentItem(), uids);
return uids;
}
void CustomTreeWidget::DeleteNode()
{
QSet<int> nodes = this->GetCurrentNodeUids();
QVector<int> uids;
for (auto it : nodes) {
uids.push_back(it);
}
emit deleteRequest(uids);
}
My lambda looks like:
connect(ui->productTree, &CustomTreeWidget::deleteRequest, this, [=](QVector<int> uids) {
//logic
});
But this signal works with old syntax.
connect(ui->productTree, SIGNAL(deleteRequest(QVector<int>)), this, SLOT(checkSlot(QVector<int>)));
And this slot is.
void ProductForm::checkSlot(QVector<int> uids)
{
qDebug() << uids.size();
}
So what is problem lambda syntax?
This smells like the violation of the one definition rule (ODR) - perhaps due to a stale build folder. The problem is that the address of deleteRequest passed to the connect method is not the same as the address of the method visible from moc_CustomTreeWidget.cpp. Remove the build folder and try again. If it still doesn't work, start reducing your problem:
Create a minimization branch in the repository (if you're not using version control, you won't go anywhere with minimization).
Copy-paste the contents of the ui_CustomTreeWidget.h file into the CustomTreeWidget.cpp file, remove the #include line.
Inspect the pasted contents for instantiation of the productTree object with the correct type.
If that's correct, then remove everything else from the code, step-by-step, rebuilding and committing to the repository at each step that still reproduces. You should end up with a test case that is 20-30 lines long at most. And it'll be either obvious what's wrong, or you can modify the question with the test case.
I need to connect some Checkboxes, so when I click one it becomes checked and other become unchecked. My code right now looks like this.
Connect in class constructor:
connect(cb_thickness1,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness1_isChecked()));
connect(cb_thickness2,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness2_isChecked()));
connect(cb_thickness3,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness3_isChecked()));
and slots
void MainWind::cb_thickness1_isChecked()
{
if(cb_thickness2->isChecked())
cb_thickness2->setChecked(false);
if(cb_thickness3->isChecked())
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness2_isChecked()
{
if(cb_thickness1->isChecked())
cb_thickness1->setChecked(false);
if(cb_thickness3->isChecked())
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness3_isChecked()
{
if(cb_thickness1->isChecked())
cb_thickness1->setChecked(false);
if(cb_thickness2->isChecked())
cb_thickness2->setChecked(false);
}
Code doesn't work as expected. When I click to any ChBx first time, everything is OK, but when I click to other next time it only uncheck previous and does nothing with itself. Only on second click it become chekced.
Also I found one more bug, when I check to ChBox, and then uncheck it by clicking it againg, I can check 2 ChBxes. [pic 2]
Radio button is a great idea.
But if you really want to use check box, you can explicitly set cb_thickness1 checked in cb_thickness1_isChecked(), and do the same for other two check boxes.
void MainWind::cb_thickness1_isChecked()
{
cb_thickness1->setChecked(true);
cb_thickness2->setChecked(false);
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness2_isChecked()
{
cb_thickness1->setChecked(false);
cb_thickness2->setChecked(true);
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness3_isChecked()
{
cb_thickness1->setChecked(false);
cb_thickness2->setChecked(false);
cb_thickness3->setChecked(true);
}
I suggest to derive a class from QCheckBox (lets call it CustomCheckBox) and add a signal, private slot and public slot
signal:
void enabled();
private slot:
void CustomCheckBox::checkEnable(bool state)
{
if(state)
{
emit enabled();
}
}
public slot:
void CustomCheckBox::uncheck()
{
setChecked(false);
}
In the constructor add:
connect(this,SIGNAL(toggled(bool)),this,SLOT(checkEnable(bool)));
This way you can use simple connects.
CustomCheckBox *box1 = new CustomCheckBox();
CustomCheckBox *box2 = new CustomCheckBox();
connect(box1,SIGNAL(enabled()),box2,SLOT(uncheck()));
Feel free to improve this answer. :)
Verify that the state of the button is checked in the slot and then deactivate the other checkboxes like you already did. You can use the parameter of the stateChanged method by passing it to the slots.
Here is code that works:
Variant I:
connect(ui->checkBoxA, SIGNAL(stateChanged(int)), this, SLOT(checkBoxAChanged(int)));
connect(ui->checkBoxB, SIGNAL(stateChanged(int)), this, SLOT(checkBoxBChanged(int)));
connect(ui->checkBoxC, SIGNAL(stateChanged(int)), this, SLOT(checkBoxCChanged(int)));
void MainWindow::checkBoxAChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxB->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxBChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxA->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxCChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxB->setChecked(false);
ui->checkBoxA->setChecked(false);
}
}
Variant II:
connect(ui->checkBoxA, SIGNAL(clicked(bool)), this, SLOT(checkBoxAClicked(bool)));
connect(ui->checkBoxB, SIGNAL(clicked(bool)), this, SLOT(checkBoxBClicked(bool)));
connect(ui->checkBoxC, SIGNAL(clicked(bool)), this, SLOT(checkBoxCClicked(bool)));
void MainWindow::checkBoxAClicked(bool val)
{
if (val == true) {
ui->checkBoxB->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxBClicked(bool val)
{
if (val == true) {
ui->checkBoxA->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxCClicked(bool val)
{
if (val == true) {
ui->checkBoxB->setChecked(false);
ui->checkBoxA->setChecked(false);
}
}
I am trying to undo/redo in multi document interface. I have different entities. Each entity has its own class. I have used UndoGroup but when I unable to push them to undoStack dont know whats's wrong there. Can anyone help me to solve the issue.
cadgraphicscene.cpp
CadGraphicsView::CadGraphicsView()
{
undoStack = new QUndoStack(this);
}
QUndoStack *CadGraphicsView::m_undoStack() const
{
return undoStack;
}
void CadGraphicsView::showUndoStack()
{
undoView = 0;
// shows the undoStack window
if (undoView == 0)
{
undoView = new QUndoView(undoStack);
undoView->setWindowTitle("Undo Stack");
}
undoView->show();
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_undoGroup = new QUndoGroup(this);
QAction *undoAction = m_undoGroup->createUndoAction(this);
undoAction->setShortcut(QKeySequence::Undo);
QAction *redoAction = m_undoGroup->createRedoAction(this);
redoAction->setShortcut(QKeySequence::Redo);
menuEdit->insertAction(menuEdit->actions().at(1), undoAction);
menuEdit->insertAction(undoAction, redoAction);
menuEdit->addAction(undoAction);
menuEdit->addAction(redoAction);
undoAction->setEnabled(true);
redoAction->setEnabled(true);
}
void MainWindow::updateActions()
{
CadGraphicsView *view = currentDocument();
m_undoGroup->setActiveStack(view == 0 ? 0 : view->m_undoStack());
}
void MainWindow::addDocument(CadGraphicsView *view)
{
m_undoGroup->addStack(view->m_undoStack());
connect(view->m_undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
connect(view->m_undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
setCurrentDocument(view);
}
void MainWindow::setCurrentDocument(CadGraphicsView *view)
{
mdiArea->currentSubWindow();
}
CadGraphicsView *MainWindow::currentDocument() const
{
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
}
I am confused with why I am not able to push entities to undoStack. Please help me to solve this issue
I think the problem is in these two functions (see the inline comments):
void MainWindow::setCurrentDocument(CadGraphicsView *view)
{
// The view argument is not used at all. You do not set anything here.
mdiArea->currentSubWindow();
}
CadGraphicsView *MainWindow::currentDocument() const
{
// mdiArea->parentWidget() returns the MainWindow, so this function
// always returns 0.
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
// You should write it as
// return qobject_cast<CadGraphicsView *>(mdiArea->activeSubWindow()->widget());
}
I have two menu items. When item 1 is disabled, I want item 2 to be disabled as well. In the OnUpdate handler of menu item 1, I have tried to use "t_pMenu = pCmdUI->m_pMenu;", "t_pMenu = pCmdUI->m_pSubMenu;" and "t_pMenu = pCmdUI->m_pParentMenu;" but I always get NULL t_pMenu. How can I achieve this purpose?
void CDummyView::OnUpdateMenuItem1(CCmdUI* pCmdUI)
{
if(m_bShowMenuItem1)
{
pCmdUI->SetText("Hide Features")
CMenu * t_pMenu = pCmdUI->m_pSubMenu;
if(t_pMenu != NULL)
t_pMenu->EnableMenuItem(ID_MENU_ITEM2, MF_ENABLED);
}
else
{
pCmdUI->SetText("Show Features")
CMenu * t_pMenu = pCmdUI->m_pParentMenu;
if(t_pMenu != NULL)
t_pMenu->EnableMenuItem(ID_MENU_ITEM2, MF_GRAYED);
}
}
void CDummyView::OnUpdateMenuItem2(CCmdUI* pCmdUI)
{
...
}
Never handle to different command IDs in one handler.
Each handler is called more than once if there are buttons and menu items. Also you don't know the sequence. When you alter the item2 in Item1 handler it may be enabled again when the handler for Item2 is called later.
When you have a flag named m_bShowMenuItem1 just use it.
void CDummyView::OnUpdateMenuItem1(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowMenuItem1);
}
void CDummyView::OnUpdateMenuItem2(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowMenuItem1);
}
I got it work. Below is the code I tried. Two flag variables m_bShowFeatures and m_bShowSmallFetures are initialized to be TRUE.
void CDummyView::OnMenuItem1()
{
m_bShowFeatures = !m_bShowFeatures;
m_pDoc->UpdateAllViews(NULL, SHOW_HIDE_ALL_FEATURES);
}
void CDummyView::OnUpdateMenuItem1(CCmdUI* pCmdUI)
{
if(m_bShowFeatures)
pCmdUI->SetText("Hide Features")
else
pCmdUI->SetText("Show Features")
}
void CDummyView::OnMenuItem2()
{
m_bShowSmallFetures= !m_bShowSmallFetures;
m_pDoc->UpdateAllViews(NULL, SHOW_HIDE_SMALL_FEATURES);
}
void CDummyView::OnUpdateMenuItem2(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowFetures)
if(m_bShowSmallFetures)
pCmdUI->SetText("Hide Small Features")
else
pCmdUI->SetText("Show Small Features")
}
So OnUpdateMenuItem2() does get invoked after OnMenuItem1() is called when Menu Item 1 is clicked. I didn't expect that.