mainwindow.cpp Example File | Qt SCXML 5.9

Qt 5.9 | Qt SCXML | Qt SCXML Sudoku Example | mainwindow.cpp Example File

Qt 5.9.3 Reference Documentation

# mainwindow.cpp Example File

sudoku/mainwindow.cpp

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the QtScxml module of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** Commercial License Usage
      ** Licensees holding valid commercial Qt licenses may use this file in
      ** accordance with the commercial license agreement provided with the
      ** Software or, alternatively, in accordance with the terms contained in
      ** a written agreement between you and The Qt Company. For licensing terms
      ** and conditions see https://www.qt.io/terms-conditions. For further
      ** information use the contact form at https://www.qt.io/contact-us.
      **
      ** BSD License Usage
      ** Alternatively, you may use this file under the terms of the BSD license
      ** as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      #include "mainwindow.h"

      #include <QStringListModel>
      #include <QScxmlStateMachine>
      #include <QComboBox>
      #include <QToolButton>
      #include <QLabel>
      #include <QGridLayout>
      #include <QFile>
      #include <QDir>
      #include <QTextStream>

      static int Size = 9;

      QT_USE_NAMESPACE

      static QVariantList emptyRow()
      {
          QVariantList row;
          for (int i = 0; i < Size; i++)
              row.append(QVariant(0));
          return row;
      }

      static QVariantMap readSudoku(const QString &fileName)
      {
          QFile input(fileName);
          input.open(QIODevice::ReadOnly | QIODevice::Text);
          QTextStream str(&input);
          const QString data = str.readAll();

          QVariantList initRowsVariant;
          const QStringList rows = data.split(QLatin1Char('\n'));
          for (int i = 0; i < Size; i++) {
              if (i < rows.count()) {
                  QVariantList initRowVariant;
                  const QStringList row = rows.at(i).split(QLatin1Char(','));
                  for (int j = 0; j < Size; j++) {
                      const int val = j < row.count()
                              ? row.at(j).toInt() % (Size + 1) : 0;
                      initRowVariant.append(val);
                  }
                  initRowsVariant.append(QVariant(initRowVariant));
              } else {
                  initRowsVariant.append(QVariant(emptyRow()));
              }
          }

          QVariantMap dataVariant;
          dataVariant.insert(QStringLiteral("initState"), initRowsVariant);

          return dataVariant;
      }

      MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
          QWidget(parent),
          m_machine(machine)
      {
          const QVector<QToolButton *> initVector(Size, nullptr);
          m_buttons = QVector<QVector<QToolButton *> >(Size, initVector);

          QGridLayout *layout = new QGridLayout(this);

          for (int i = 0; i < Size; i++) {
              for (int j = 0; j < Size; j++) {
                  QToolButton *button = new QToolButton(this);
                  button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
                  layout->addWidget(button, i + i / 3, j + j / 3);
                  m_buttons[i][j] = button;
                  connect(button, &QToolButton::clicked, [this, i, j] () {
                      QVariantMap data;
                      data.insert(QStringLiteral("x"), i);
                      data.insert(QStringLiteral("y"), j);
                      m_machine->submitEvent("tap", data);
                  });
              }
          }

          for (int i = 0; i < 3; i++) {
              for (int j = 0; j < 2; j++) {
                  QFrame *hFrame = new QFrame(this);
                  hFrame->setFrameShape(QFrame::HLine);
                  layout->addWidget(hFrame, 4 * j + 3, 4 * i, 1, 3);

                  QFrame *vFrame = new QFrame(this);
                  vFrame->setFrameShape(QFrame::VLine);
                  layout->addWidget(vFrame, 4 * i, 4 * j + 3, 3, 1);
              }
          }

          m_startButton = new QToolButton(this);
          m_startButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
          m_startButton->setText(tr("Start"));
          layout->addWidget(m_startButton, Size + 3, 0, 1, 3);

          connect(m_startButton, &QAbstractButton::clicked,
                  [this] {
              if (m_machine->isActive("playing"))
                  m_machine->submitEvent("stop");
              else
                  m_machine->submitEvent("start");
          });

          m_label = new QLabel(tr("unsolved"));
          m_label->setAlignment(Qt::AlignCenter);
          layout->addWidget(m_label, Size + 3, 4, 1, 3);

          m_undoButton = new QToolButton(this);
          m_undoButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
          m_undoButton->setText(tr("Undo"));
          m_undoButton->setEnabled(false);
          layout->addWidget(m_undoButton, Size + 3, 8, 1, 3);

          connect(m_undoButton, &QAbstractButton::clicked,
                  [this] {
              m_machine->submitEvent("undo");
          });

          m_chooser = new QComboBox(this);
          layout->addWidget(m_chooser, Size + 4, 0, 1, 11);

          QDir dataDir(QLatin1String(":/data"));
          QFileInfoList sudokuFiles = dataDir.entryInfoList(QStringList() << "*.data");
          foreach (const QFileInfo &sudokuFile, sudokuFiles)
              m_chooser->addItem(sudokuFile.completeBaseName(), sudokuFile.absoluteFilePath());

          connect(m_chooser, QOverload<int>::of(&QComboBox::currentIndexChanged),
                  [this] (int index) {
              const QString sudokuFile = m_chooser->itemData(index).toString();
              const QVariantMap initValues = readSudoku(sudokuFile);
              m_machine->submitEvent("setup", initValues);
          });

          const QVariantMap initValues = readSudoku(m_chooser->itemData(0).toString());
          m_machine->setInitialValues(initValues);

          m_machine->connectToState("playing", [this] (bool playing) {
              if (playing) {
                  m_startButton->setText(tr("Stop"));
                  m_undoButton->setEnabled(true);
                  m_chooser->setEnabled(false);
              } else {
                  m_startButton->setText(tr("Start"));
                  m_undoButton->setEnabled(false);
                  m_chooser->setEnabled(true);
              }
          });

          m_machine->connectToState("solved", [this] (bool solved) {
              if (solved)
                  m_label->setText(tr("SOLVED !!!"));
              else
                  m_label->setText(tr("unsolved"));
          });

          m_machine->connectToEvent("updateGUI", [this] (const QScxmlEvent &event) {
              const QVariant data = event.data();

              const QVariantList currentRows = data.toMap().value("currentState").toList();
              for (int i = 0; i < currentRows.count(); i++) {
                  const QVariantList row = currentRows.at(i).toList();
                  for (int j = 0; j < row.count(); j++) {
                      const int value = row.at(j).toInt();
                      const QString text = value ? QString::number(value) : QString();
                      m_buttons[i][j]->setText(text);
                  }
              }

              const bool active = m_machine->isActive("playing");

              const QVariantList initRows = data.toMap().value("initState").toList();
              for (int i = 0; i < initRows.count(); i++) {
                  const QVariantList row = initRows.at(i).toList();
                  for (int j = 0; j < row.count(); j++) {
                      const int value = row.at(j).toInt();
                      const bool enabled = !value && active; // enable only zeroes from initState
                      m_buttons[i][j]->setEnabled(enabled);
                  }
              }
          });

          setLayout(layout);
      }

      MainWindow::~MainWindow()
      {
      }