diff --git a/include/cute.h b/include/cute.h index b3660de565895a5f879a9edaaab24921c467e8b7..1dd22106b8c21fbfff16b3e375e6cc5d953dbdd7 100644 --- a/include/cute.h +++ b/include/cute.h @@ -10,11 +10,16 @@ namespace cute { - struct Point + struct Point ///< Representation of a Point in 2D-Space used by this library. { float x; float y; }; + + /** + * @brief This enum can be used to set different levels of detail in your plotting. + * They are in ascending order, so you can do if(verbosity() >= result). + */ enum Verbosity { silent, @@ -23,11 +28,19 @@ namespace cute verbose, all }; - enum Scale + + enum Scale ///< Representing different axis scalings. { linear, logarithmic }; + + /** + * @brief Representation of RGB colors used when drawing points and lines. + * For the Transparency, 0 is fully visible, 255 is unvisible. + * Think of it as the inverse of the alpha in RGBA.all + * @todo This should just be Alpha, shouldn't it? + */ struct Color { uint8_t red; @@ -35,7 +48,8 @@ namespace cute uint8_t blue; uint8_t transparency; }; - enum LineStyle + + enum LineStyle ///< Different possibilities to draw your lines. { none, solid, @@ -44,41 +58,44 @@ namespace cute dashDot, DashDotDot }; - enum Shape + + enum Shape ///< Draw your Points as circles or rectangles. { circle, rectangle }; - namespace Colors + + namespace Colors ///< A collection of Color presets. Not exhaustive, try them, change them, suggest other ones. { - constexpr Color none = {0, 0, 0, 0xFF}; - constexpr Color white = {0xFF, 0xFF, 0xFF, 0}; - constexpr Color black = {0, 0, 0, 0}; - constexpr Color red = {0xFF, 0, 0, 0}; - constexpr Color darkRed = {0x80, 0, 0, 0}; - constexpr Color green = {0, 0xFF, 0, 0}; - constexpr Color darkGreen = {0, 0x80, 0, 0}; - constexpr Color blue = {0, 0, 0xFF, 0}; - constexpr Color darkBlue = {0, 0, 0x80, 0}; - constexpr Color cyan = {0, 0xFF, 0xFF, 0}; - constexpr Color darkCyan = {0, 0x80, 0x80, 0}; - constexpr Color magenta = {0xFF, 0, 0xFF, 0}; - constexpr Color darkMagenta = {0x80, 0, 0x80, 0}; - constexpr Color yellow = {0xFF, 0xFF, 0, 0}; - constexpr Color darkYellow = {0x80, 0x80, 0, 0}; - constexpr Color gray = {0xA0, 0xA0, 0xA4, 0}; - constexpr Color darkGray = {0x80, 0x80, 0x80, 0}; - constexpr Color lightGray = {0xC0, 0xC0, 0xC0, 0}; + constexpr Color none = {0, 0, 0, 0xFF}; ///< {0, 0, 0, 0xFF} + constexpr Color white = {0xFF, 0xFF, 0xFF, 0}; ///< {0xFF, 0xFF, 0xFF, 0} + constexpr Color black = {0, 0, 0, 0}; ///< {0, 0, 0, 0} + constexpr Color red = {0xFF, 0, 0, 0}; ///< {0xFF, 0, 0, 0} + constexpr Color darkRed = {0x80, 0, 0, 0}; ///< {0x80, 0, 0, 0} + constexpr Color green = {0, 0xFF, 0, 0}; ///< {0, 0xFF, 0, 0} + constexpr Color darkGreen = {0, 0x80, 0, 0}; ///< {0, 0x80, 0, 0} + constexpr Color blue = {0, 0, 0xFF, 0}; ///< {0, 0, 0xFF, 0} + constexpr Color darkBlue = {0, 0, 0x80, 0}; ///< {0, 0, 0x80, 0} + constexpr Color cyan = {0, 0xFF, 0xFF, 0}; ///< {0, 0xFF, 0xFF, 0} + constexpr Color darkCyan = {0, 0x80, 0x80, 0}; ///< {0, 0x80, 0x80, 0} + constexpr Color magenta = {0xFF, 0, 0xFF, 0}; ///< {0xFF, 0, 0xFF, 0} + constexpr Color darkMagenta = {0x80, 0, 0x80, 0}; ///< {0x80, 0, 0x80, 0} + constexpr Color yellow = {0xFF, 0xFF, 0, 0}; ///< {0xFF, 0xFF, 0, 0} + constexpr Color darkYellow = {0x80, 0x80, 0, 0}; ///< {0x80, 0x80, 0, 0} + constexpr Color gray = {0xA0, 0xA0, 0xA4, 0}; ///< {0xA0, 0xA0, 0xA4, 0} + constexpr Color darkGray = {0x80, 0x80, 0x80, 0}; ///< {0x80, 0x80, 0x80, 0} + constexpr Color lightGray = {0xC0, 0xC0, 0xC0, 0}; ///< {0xC0, 0xC0, 0xC0, 0} } // namespace Colors - struct PlotProperties + + struct PlotProperties ///< A structure to bundle all the possibilities to infuelce how your Data should be plotted. { - std::string name = ""; - int size = 10; - Color color = Colors::none; - LineStyle line = solid; - bool onTop = false; - bool legend = true; - Shape shape = circle; + std::string name = ""; ///< The name displayed in the Legend. An empty name will cause it not to be displayed. + int size = 10; ///< The diameter of points and the width of lines. No fix unit sadly, adjust yourself. + Color color = Colors::none; ///< The color of what you want plotted. The default value none will have a color chosen at random. + LineStyle line = solid; ///< The style, you want lines to be drawn in. + bool onTop = false; ///< Wether that Data should always be displayed above other data. So you dont have to redraw points manually all the time. + bool legend = true; ///< Whether you want the data to show up in the legend. + Shape shape = circle; ///< The shape you want points to have. }; void DYNAMIC setPlotVisible(int plot, bool visible = true); void DYNAMIC setPlotTitle(int plot, const std::string& title); @@ -132,15 +149,19 @@ namespace cute std::chrono::microseconds DYNAMIC timerResult(); + /** + * @brief This is only here to facilitate loading and unloading functions, loaded dynamically from a .dll. + * @todo With the current approach or with static libraries, this is unneccessary. + */ namespace core { // These are for loading and unloading libcute.dll at runtime #if !defined(RUNTIME_LOAD) and !defined(EXPORT_CUTE) - inline int initialize() + inline int initialize() ///< In case of not loading at runtime and importing cute as the User, do nothing. { return 0; } - inline void shutdown() {} + inline void shutdown() {} ///< In case of not loading at runtime and importing cute as the User, do nothing. #else int initialize(); void shutdown(); diff --git a/src/control.cpp b/src/control.cpp index c3f9fd16f25fd8f12f814d2e6c297bf63e607284..2fabc210ce0b6e5b085c27882975869dc184f6d1 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -21,7 +21,10 @@ #include <random> #include <variant> - +/** + * @brief Most connections, hopefully all that are more sophisticated than just visuals or inside other Objects + * If something isn't working, you likely forgot to establish the connection here. + */ void Control::makeConnections() { CuteControl* cc = &CuteControl::get(); @@ -57,6 +60,19 @@ void Control::makeConnections() &CuteControl::changeVerbosity); } +/** + * @brief Blocking. Does setup and starts the Application. + * This is the equivalent to the classic minimal main() function of a Qt Widget App. + * MainWindow and QApplication are created, Control's connections established. + * Afterwards the Users setup() is called, so that all functionality is available. + * QApplication::exec() is blocking, so just before that, in the case of autorun being set, + * the appropriate thread is started. + * This works fine, because QApplications event queue is already created on construction, + * and will only processed on execution. + * + * @param userSetup Pointer to the User implemented Setup function. + * @return int return value of QApplication::exec() or -1 if called before exiting. + */ int Control::run(void (*userSetup)()) { static bool running = false; @@ -83,6 +99,12 @@ int Control::run(void (*userSetup)()) return -1; } +/** + * @brief Construct a new Control::Control object + * Here, in a not at all dynamic way, the vectors for processing plot commands + * are created. + * @todo This really should not be hardcoded. + */ Control::Control() { onTopSeries.emplace_back(); @@ -93,42 +115,99 @@ Control::Control() plotHistory.emplace_back(); } +/** + * @brief Appends a QString to MainWindows logging Area / textEdit. + * + * @param message The message to be appended. + */ void Control::logMessage(const QString message) { w->textEdit->append(message); } +/** + * @brief Sets the status bars progress bar to the given percentage. Only integer values are supported. + * + * @param percentage The Percentage 0 - 100, the processbar should show. + */ void Control::setProgress(int percentage) { w->progressBar->setValue(percentage); } +/** + * @brief Displays the given message in MainWindows status bar for duration_s seconds. + * + * @param message The QString to be displayed. + * @param duration_s Time in seconds, the message should stay. 0 will have the message only disappear when overwritten. + */ void Control::setStatusMessage(const QString message, int duration_s) { w->statusBar->showMessage(message, duration_s * 1000); } +/** + * @brief Show or hide the given plot + * + * @param plot The number of the plot to be manipulated. Starting from 0 for the left mose plot. + * @param visible Whether to show or hide. + * @todo ensure every graphing widget can hide + */ void Control::setPlotVisible(int plot, bool visible) { w->graphingWidgets[plot]->setHidden(!visible); } +/** + * @brief Change the given plots title in ChartMenu and in the Plot itself. + * The ChartMenu has a separate memory of the plot titles, so it it can show them without having to look them up, + * which would create complicated dependencies. + * @todo This will break, if other plotting widgets than QChart are added. Most easy thing would likely be, + * to offer setTile() as an interface in the new Widget and adjust the this->charts vector accordingly. + * + * @param plot The number of the plot to be manipulated. Starting from 0 for the left mose plot. + * @param title The new title. + */ void Control::setPlotTitle(int plot, const QString title) { w->charts[plot]->setTitle(title); w->cm->titleChanged(plot, title); } +/** + * @brief Instruct the MainWindow to change the given plots theme to one of the themes built into QtCharts + * This will affect all and only QCharts. + * + * @param themeNumber 0-7 are valid. + */ void Control::setPlotTheme(int themeNumber) { w->changeChartTheme(themeNumber); } +/** + * @brief Whether the legend should be shown or hidden + * + * @param plot The number of the plot to be manipulated. Starting from 0 for the left mose plot. + * @param visible Whether to show or hide. + * @todo dynamic graphs... + */ void Control::setLegendVisible(int plot, bool visible) { w->charts[plot]->legend()->setVisible(visible); } + +/** + * @brief Applies the given Titles to the axes of that plot. Hides the Title if empty. + * Manipulating the axes is a bit tedious, since we can only get a list of all axes from the chart. + * We then have to check it's properties to find the orientation. + * + * @param plot The plot to be manipulated. + * @param x Title for the x-axis. + * @param y Title for the y-axis. + * @todo dynamic graphs... + */ void Control::setAxisTitles(int plot, QString x, QString y) { if ((size_t)plot >= w->charts.size()) @@ -149,6 +228,14 @@ void Control::setAxisTitles(int plot, QString x, QString y) } } +/** + * @brief Show or hide the given plots axes. + * + * @param plot The plot to be manipulated. + * @param x Wether the x-axis should be visible. + * @param y Wether the y-axis should be visible. + * @todo dynamic graphs... + */ void Control::setAxisVisible(int plot, bool x, bool y) { if ((size_t)plot >= w->charts.size()) @@ -167,12 +254,25 @@ void Control::setAxisVisible(int plot, bool x, bool y) } } +/** + * @brief Add or "register" the given function to the Application + * The same order in both locations is crutial, + * as the index will be used to choose the threads function. + * @param func The function pointer to be called by the AlgoThread. + * @param name The name to be displayed in the ConfigPanels QCombobox algoBox. + */ void Control::registerAlgorithm(void (*func)(), const QString name) { algorithms.emplace_back(func, name); w->addAlgorithm(name); } +/** + * @brief To be called when the AlgoThread should end before the Algorithm has ended. + * This will simply emit a signal to the AlgoThread, that it may please terminate. + * The User is responsible for running AlgoThread event loop and cleanly ending their function. + * cute::ok() is provided for that purpose. + */ void Control::stopThread() { if (thread) @@ -181,6 +281,10 @@ void Control::stopThread() } } +/** + * @brief This should be called, when the tread finishes it's execution.abort + * The thread related variables and displays are reset here. + */ void Control::threadEnded() { threadRunning = false; @@ -188,6 +292,18 @@ void Control::threadEnded() thread = nullptr; } +/** + * @brief Sets up and starts an AlgoThread. + * A new thread is created and connected such, that when finished, it notifies Control and flags itself for deletion. + * CuteControl is then handed it's problem and the currently set verbosity, so that it can start execution right away. + * Most importantly CuteControl is then moved to the EventQueue of the newly created thread. + * That has to be done, since CuteControl was created in the main thread and thus is hooked into the main EventLoop. + * This can only be done, pushing from the objects current thread, so it has to be moved back by the AlgoThread. + * When the objects are in different threads, the signals are automatically stored and processed in the receivers EventQueue. + * This way, it is thread safe. + * + * @param func The function/algorithm to be executed by the AlgoThread. + */ void Control::startThread(void (*func)()) { if (!thread) @@ -207,6 +323,10 @@ void Control::startThread(void (*func)()) } } +/** + * @brief Depending on if a thread has already been started, either start or stop a thread. + * The appropriate algorithm is determined here from the current selection of the algoBox. + */ void Control::onRunButton() { if (!thread) @@ -221,18 +341,35 @@ void Control::onRunButton() } } +/** + * @brief Change the problem widget stack to random generation and set the given size. + * The setProblem functions only manipulate the entries in the View.abort + * When a problem is to be generated, the necessary information will be pulled from the View. + * @param n The number to be entered into the QSpinBox. + */ void Control::setProblemSize(int n) { w->configPanel->stackedWidget->setCurrentWidget(w->configPanel->randomWidget); w->configPanel->problemSizeBox->setValue(n); } +/** + * @brief Change the problem widget stack to reading from a file and set the path. + * The setProblem functions only manipulate the entries in the View.abort + * When a problem is to be generated, the necessary information will be pulled from the View. + * @param path The QString to be entered into the QLineEdit. No validity checks are performed here. + */ void Control::setProblemFile(QString path) { w->configPanel->stackedWidget->setCurrentWidget(w->configPanel->fileWidget); w->configPanel->fileLine->setText(path); } +/** + * @brief Depending of if the problem was locked/retained or not, a new problem is generated or the old problem is used. + * + * @return std::vector<cute::Point> The given Problem. Newly generated if problemRetained was not set. + */ std::vector<cute::Point> Control::getProblem() { if (!problemRetained) @@ -242,6 +379,20 @@ std::vector<cute::Point> Control::getProblem() return problem; } +/** + * @brief Big function, filling the problem member variable with the method selected in the GUI. + * The method depends on the current Widget of the ConfigPanel. + * For random generation, floats with 2 decimal places and to a maximum number of 100 are generated. + * These values are currently hard coded. The amount of numbers is read directly from the GUI. + * + * For reading a file, the CSVParser is used. + * The path is read directly from the GUI. + * Only lines with at least two fields and numbers in the first two fields are read.abort + * Other lines as well as data beyond the second column are simply discarted. + * + * For interpreting pasted or drag'n'dropped data, the CSVParser is used is the same way, + * with the exception of the string being handed to the parser rather than a filepath. + */ void Control::generateProblem() { problem.clear(); @@ -304,6 +455,10 @@ void Control::generateProblem() } } +/** + * @brief Resets the left most plot (plot0) and plots the problems Points to it. + * @todo getProblem is called here, but is that problem then used by the thread, if it is not locked? UX wise, that would make sense. + */ void Control::onPreviewButton() { if (!threadRunning) @@ -313,6 +468,12 @@ void Control::onPreviewButton() } } +/** + * @brief Depending on the state, set problemRetained and show that in the View, or release them. + * The Problem is not overwritten, so it can be locked again.abort + * In case no problem has been generated before, one is generated. + * Another option would be to just do nothing in that case. + */ void Control::onLockButton() { if (problemRetained) @@ -552,6 +713,11 @@ void Control::resizeAxes(int plot) } } +/** + * @brief Remove all series from the plot and the plots history and onTop vectors + * This currently only works with QCharts. + * @param plot The plot to be reset. + */ void Control::resetPlot(int plot) { w->charts[plot]->removeAllSeries(); @@ -663,6 +829,11 @@ void Control::setPlotScale(int plot, int x, int y) resizeAxes(plot); } +/** + * @brief Will write the current problem to a .csv file. + * The path is chosen by the User in a QFileDialog. + * To be called when the Menubars Save Button is clicked. + */ void Control::saveProblem() { QString filter = "CSV File (*.csv)";