Răsfoiți Sursa

Signal handler to get optimization progress for GUI.
The GUI is now a propper python package.
Better code organization and file naming.
Small fixes to the update command.

Iñigo Valentin 4 ani în urmă
părinte
comite
c658e8a166
77 a modificat fișierele cu 829 adăugiri și 637 ștergeri
  1. 1 0
      .gitignore
  2. 8 8
      Makefile
  3. 1 1
      README.md
  4. 0 195
      RuneOptimizerGUI/RuneOptimizer.py
  5. 1 1
      doxygen-cli.conf
  6. 1 1
      doxygen-gui.conf
  7. 1 1
      src/cli/runeoptimizer/db/db.c
  8. 3 3
      src/cli/runeoptimizer/db/db.h
  9. 0 0
      src/cli/runeoptimizer/error/error.h
  10. 0 0
      src/cli/runeoptimizer/gui/gui.c
  11. 0 0
      src/cli/runeoptimizer/gui/gui.h
  12. 1 1
      src/cli/runeoptimizer/help/help.c
  13. 0 0
      src/cli/runeoptimizer/help/help.h
  14. 91 53
      src/cli/runeoptimizer/optimize/optimize.c
  15. 1 1
      src/cli/runeoptimizer/optimize/optimize.h
  16. 1 1
      src/cli/runeoptimizer/player/player.c
  17. 0 0
      src/cli/runeoptimizer/player/player.h
  18. 4 4
      src/cli/runeoptimizer/runeoptimizer.c
  19. 1 1
      src/cli/runeoptimizer/runeoptimizer.h
  20. 1 1
      src/cli/runeoptimizer/team/team.c
  21. 3 3
      src/cli/runeoptimizer/team/team.h
  22. 1 1
      src/cli/runeoptimizer/team/team_add_unit.c
  23. 2 2
      src/cli/runeoptimizer/team/team_create.c
  24. 3 3
      src/cli/runeoptimizer/team/team_delete.c
  25. 2 2
      src/cli/runeoptimizer/team/team_list.c
  26. 1 1
      src/cli/runeoptimizer/team/team_remove_unit.c
  27. 4 4
      src/cli/runeoptimizer/unit/unit.c
  28. 0 0
      src/cli/runeoptimizer/unit/unit.h
  29. 1 1
      src/cli/runeoptimizer/update/update.c
  30. 5 5
      src/cli/runeoptimizer/update/update.h
  31. 9 9
      src/cli/runeoptimizer/update/update_current_stats.c
  32. 1 1
      src/cli/runeoptimizer/update/update_db_tables.c
  33. 5 9
      src/cli/runeoptimizer/update/update_efficiency.c
  34. 2 2
      src/cli/runeoptimizer/update/update_get_monster_name.c
  35. 3 2
      src/cli/runeoptimizer/update/update_info.c
  36. 68 69
      src/cli/runeoptimizer/update/update_json.c
  37. 1 1
      src/cli/runeoptimizer/update/update_rune_totals.c
  38. 1 1
      src/cli/test/runeoptimizer_e2e_test.c
  39. 1 1
      src/cli/test/runeoptimizer_integration_test.c
  40. 1 1
      src/cli/test/runeoptimizer_test.c
  41. 1 1
      src/cli/test/runeoptimizer_unit_test.c
  42. 0 0
      src/cli/test/unity/unity.c
  43. 0 0
      src/cli/test/unity/unity.h
  44. 0 0
      src/cli/test/unity/unity_internals.h
  45. 0 0
      src/gui/runeoptimizer_gui/__init__.py
  46. 0 0
      src/gui/runeoptimizer_gui/data/__init__.py
  47. 20 0
      src/gui/runeoptimizer_gui/data/data.py
  48. 0 0
      src/gui/runeoptimizer_gui/database/__init__.py
  49. 20 0
      src/gui/runeoptimizer_gui/database/database.py
  50. 59 56
      src/gui/runeoptimizer_gui/entity/Rune.py
  51. 16 14
      src/gui/runeoptimizer_gui/entity/RuneStat.py
  52. 27 27
      src/gui/runeoptimizer_gui/entity/StatSet.py
  53. 15 13
      src/gui/runeoptimizer_gui/entity/Team.py
  54. 23 17
      src/gui/runeoptimizer_gui/entity/Unit.py
  55. 11 9
      src/gui/runeoptimizer_gui/entity/UnitTeam.py
  56. 0 0
      src/gui/runeoptimizer_gui/entity/__init__.py
  57. 3 1
      src/gui/runeoptimizer_gui/gui/DialogConfirm.py
  58. 7 4
      src/gui/runeoptimizer_gui/gui/DialogNewTeam.py
  59. 6 2
      src/gui/runeoptimizer_gui/gui/DialogUpdateJson.py
  60. 9 6
      src/gui/runeoptimizer_gui/gui/PanelInfo.py
  61. 86 21
      src/gui/runeoptimizer_gui/gui/PanelOptimizer.py
  62. 49 29
      src/gui/runeoptimizer_gui/gui/PanelResults.py
  63. 23 17
      src/gui/runeoptimizer_gui/gui/PanelTeams.py
  64. 11 8
      src/gui/runeoptimizer_gui/gui/PanelUnits.py
  65. 19 15
      src/gui/runeoptimizer_gui/gui/RuneOptimizerFrame.py
  66. 15 7
      src/gui/runeoptimizer_gui/gui/TabList.py
  67. 0 0
      src/gui/runeoptimizer_gui/gui/__init__.py
  68. 0 0
      src/gui/runeoptimizer_gui/res/icon/help.png
  69. 0 0
      src/gui/runeoptimizer_gui/res/icon/info.png
  70. 0 0
      src/gui/runeoptimizer_gui/res/icon/optimize.png
  71. 0 0
      src/gui/runeoptimizer_gui/res/icon/progress.gif
  72. 0 0
      src/gui/runeoptimizer_gui/res/icon/results.png
  73. 0 0
      src/gui/runeoptimizer_gui/res/icon/teams.png
  74. 0 0
      src/gui/runeoptimizer_gui/res/icon/units.png
  75. 106 0
      src/gui/runeoptimizer_gui/runeoptimizer_gui.py
  76. 0 0
      src/gui/runeoptimizer_gui/values/__init__.py
  77. 73 0
      src/gui/runeoptimizer_gui/values/values.py

+ 1 - 0
.gitignore

@@ -9,6 +9,7 @@
 .pydevproject
 .project
 .settings
+.directory
 *.sqlite
 *.xcf
 *.json

+ 8 - 8
Makefile

@@ -20,12 +20,12 @@ else
 endif
 
 # Files to compile
-# For tests, RuneOptimizer.c is not included in the list: is is included from
+# For tests, runeoptimizer.c is not included in the list: is is included from
 # each test file with a redefinition of main.
-FILES=src/RuneOptimizer/RuneOptimizer.c src/RuneOptimizer/*/*.c
-TESTU_FILES=src/test/RuneOptimizerUnitTest.c src/RuneOptimizer/*/*.c
-TESTI_FILES=src/test/RuneOptimizerIntegrationTest.c src/RuneOptimizer/*/*.c
-TESTE_FILES=src/test/RuneOptimizerE2ETest.c src/RuneOptimizer/*/*.c
+FILES=src/cli/runeoptimizer/runeoptimizer.c src/cli/runeoptimizer/*/*.c
+TESTU_FILES=src/cli/test/runeoptimizer_unit_test.c src/cli/runeoptimizer/*/*.c
+TESTI_FILES=src/cli/test/runeoptimizer_integration_test.c src/cli/runeoptimizer/*/*.c
+TESTE_FILES=src/cli/test/runeoptimizer_e2e_test.c src/cli/runeoptimizer/*/*.c
 
 # Compiler
 CC=gcc
@@ -62,13 +62,13 @@ TESTE_OUT=$(OUT_DIR)$(TESTE_OUT_NAME)$(OUT_EXT)
 
 # Instalation directories
 INSTALL_CLI_SRC=$(OUT)
-INSTALL_GUI_SRC=RuneOptimizerGUI
+INSTALL_GUI_SRC=src/gui/runeoptimizer_gui
 INSTALL_DIR_BASE=/usr/share/runeoptimizer/
 INSTALL_DIR_EXEC=/usr/local/bin/
 INSTALL_CLI_EXEC_SRC=$(INSTALL_DIR_BASE)$(OUT_NAME)$(OUT_EXT)
-INSTALL_GUI_EXEC_SRC=$(INSTALL_DIR_BASE)$(INSTALL_GUI_SRC)$(OUT_EXT)/RuneOptimizer.py
+INSTALL_GUI_EXEC_SRC=$(INSTALL_DIR_BASE)runeoptimizer_gui/runeoptimizer_gui.py
 INSTALL_CLI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)$(OUT_EXT)
-INSTALL_GUI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)-gui$(OUT_EXT)
+INSTALL_GUI_EXEC_DST=$(INSTALL_DIR_EXEC)$(OUT_NAME)_gui$(OUT_EXT)
 
 CMD_DOC=doxygen
 CMD_RM=$(OS_RM)

+ 1 - 1
README.md

@@ -53,7 +53,7 @@ Well, this was painfull to get right
     (Credit for this part and many thanks to http://source.online.free.fr/Windows_HowToCompileSQLite)
     Download https://www.sqlite.org/2022/sqlite-amalgamation-3370200.zip . It contains four files:
 
-    * shell.c 
+    * shell.c
     * sqlite3.c
     * sqlite3.h
     * sqlite3ext.h

+ 0 - 195
RuneOptimizerGUI/RuneOptimizer.py

@@ -1,195 +0,0 @@
-#!/usr/bin/env python
-
-"""
-This file is part of RuneOptimizer.
-
-RuneOptimizer is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the Free
-Software Foundation, either version 3 of the License, or (at your option)
-any later version.
-
-RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-more details.
-
-You should have received a copy of the GNU General Public License along with
-RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
-
-"""
-
-import wx
-import wx.grid
-import wx.adv
-import sqlite3
-import math
-import subprocess
-from subprocess import Popen, PIPE
-import json
-import os
-import pipes
-import appdirs
-import multiprocessing
-from types import SimpleNamespace
-from time import sleep
-
-# Include files
-classes_to_include = [
-    '/gui/PanelUnits.py',
-    '/gui/PanelTeams.py',
-    '/gui/PanelOptimizer.py',
-    '/gui/PanelResults.py',
-    '/gui/PanelInfo.py',
-    '/gui/RuneOptimizerFrame.py',
-    '/gui/TabList.py',
-    '/gui/DialogConfirm.py',
-    '/gui/DialogUpdateJson.py',
-    '/gui/DialogNewTeam.py',
-    '/entity/RuneStat.py',
-    '/entity/Rune.py',
-    '/entity/StatSet.py',
-    '/entity/UnitTeam.py',
-    '/entity/Unit.py',
-    '/entity/Team.py',
-]
-for cls in classes_to_include:
-    exec(
-      compile(
-        source=open(os.path.dirname(os.path.realpath(__file__)) + cls).read(),
-        filename=os.path.dirname(os.path.realpath(__file__)) + cls,
-        mode='exec'
-      )
-    )
-
-# The path to the RuneOptimizer executable. Different in Windows.
-executable_path = \
-  os.path.dirname(os.path.realpath(__file__)) + '/../RuneOptimizer'
-if os.name == 'nt':
-    executable_path = \
-      os.path.dirname(os.path.realpath(__file__)) + '\..\RuneOptimizer.exe'
-
-# App information
-app_info = {
-    "name": "Rune Optimizer",
-    "app_version": "UNKNOWN",
-    "gui_version": "0.1-RC1",
-    "author": "Iñigo Valentin",
-    "author_mail": "i@inigovalentin.com",
-    "url": {
-        "Home page": "https://inigovalentin.com/projects/RuneOptimizer/",
-        "Source code": "https://github.com/InigoValentin/RuneOptimizer/"
-    }
-}
-
-# Global database connection
-conn = None
-
-RUNE_STATS = {
-    "HP": 1, "HP_P": 2, "ATK": 3, "ATK_P": 4, "DEF": 5, "DEF_P": 6,
-    "SPD": 8, "CRR": 9, "CRD": 10, "RES": 11, "ACC": 12
-}
-
-# Unit stat names, indexed with Com2Us values.
-STAT_NAMES = [
-  "NULL", "HP  ", "HP% ", "ATK ", "ATK%", "DEF ",
-  "DEF%", "NULL", "SPD ", "CRR ", "CRD ", "RES ", "ACC "
-]
-
-# Rune set names, indexed with Com2Us values.
-SET_NAMES = [
-  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "BLADE",    "RAGE",     #  0- 5
-  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "DESPAIR",  "VAMPIRE",  #  6-11
-  "NULL",    "VIOLENT", "NEMESIS",  "WILL",    "SHIELD",   "REVENGE",  # 12-17
-  "DESTROY", "FIGHT",   "DETERMIN", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
-]
-
-# TODO: Work in progress...
-"""
-# Rune set names, indexed with Com2Us values.
-SET_SYMBOLS = [
-  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "ᚬ",    "ᛟ",     #  0- 5
-  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "ᛃ",  "VAMPIRE",  #  6-11
-  "NULL",    "ᛒ", "NEMESIS",  "WILL",    "SHIELD",   "ᛝ",  # 12-17
-  "DESTROY", "FIGHT",   "ᛗ", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
-]
-
-SET_SYMBOLS_UNICODE = [
-  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "\u16AC",    "\u16DF", #  0- 5
-  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "\u16C3",  "VAMPIRE",  #  6-11
-  "NULL",    "\u16D2", "NEMESIS",  "WILL",    "SHIELD",   "\u16DD",  # 12-17
-  "DESTROY", "FIGHT",   "\u16D7", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
-]
-"""
-
-QUALITY_NAMES = [
-  "NULL",
-  "NORMAL", "MAGIC", "RARE", "HERO", "LEGEND"
-  "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
-  "A.NORMAL", "A.MAGIC", "A.RARE", "A.HERO",  "A.LEGEND"
-]
-
-units = {}
-teams = {}
-
-def reload_units():
-    # Get all the units, sorted by priority desc
-    global units
-    global conn
-    units = {}
-    cursor = conn.execute("""
-      SELECT
-        id,
-        (
-          SELECT sum(priority)
-          FROM teams
-          WHERE id IN (SELECT team FROM units_teams WHERE unit = units.id)
-        ) AS prio
-      FROM units
-      ORDER BY prio DESC;
-    """)
-    rows = cursor.fetchall()
-    for row in rows:
-        units[str(row[0])] = Unit(str(row[0]))
-
-def reload_teams():
-    # Get all the teams, sorted by priority desc
-    global teams
-    global conn
-    teams = {}
-    cursor = conn.execute("SELECT id FROM teams ORDER BY priority DESC;")
-    rows = cursor.fetchall()
-    for row in rows:
-        teams[str(row[0])] = Team(str(row[0]))
-
-if __name__ == '__main__':
-    """
-    Main function.
-
-    Conects to the database and shows the initial frame.
-    """
-
-    #Get CLI app version
-    # TODO: Change executable name for windows
-    #if os.name == 'nt':
-    app_info["app_version"] = subprocess.check_output(
-      ['RuneOptimizer', 'version']
-    )
-
-    # TODO: Test for executable to exist
-    # TODO: Test for database to exist
-
-    # Open the database
-    conn = sqlite3.connect(
-      appdirs.user_data_dir("RuneOptimizer") + "/data.sqlite"
-    )
-    
-    reload_units()
-    reload_teams()
-
-    # Start the GUI
-    app = wx.App()
-    frm = RuneOptimizerFrame(
-      parent=None, title='Rune Optimizer', pos=(100, 100), size=(1000, 650)
-    )
-    frm.Show()
-    app.MainLoop()

+ 1 - 1
doxygen-cli.conf

@@ -864,7 +864,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  =src/
+INPUT                  =src/cli/runeoptimizer
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 1 - 1
doxygen-gui.conf

@@ -864,7 +864,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  =RuneOptimizerGUI/
+INPUT                  =src/gui/runeoptimizer_gui/
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 1 - 1
src/RuneOptimizer/db/db.c → src/cli/runeoptimizer/db/db.c

@@ -30,7 +30,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "db.h"
 

+ 3 - 3
src/RuneOptimizer/db/db.h → src/cli/runeoptimizer/db/db.h

@@ -38,7 +38,7 @@ extern int db_close();
 
 /**
  * Opens the database connection.
- * 
+ *
  * Sets up  and enables the global db connection {@link db}. On error, it prints
  * a description to stderr.
  *
@@ -53,7 +53,7 @@ extern int db_open(char *path);
 
 /**
  * Executes a database query for a result.
- * 
+ *
  * Uses the global database connection {@link db}. If the connection is not
  * initializad, it calls {@link db_open}. On error, it prints a description to
  * stderr.
@@ -74,7 +74,7 @@ extern int db_query(sqlite3_stmt **stmt, char query[], char *parameters[]);
 
 /**
  * Executes a database statement.
- * 
+ *
  * Uses the global database connection {@link db}. If the connection is not
  * initializad, it calls {@link db_open}. On error, it prints a description to
  * stderr.

+ 0 - 0
src/RuneOptimizer/error/error.h → src/cli/runeoptimizer/error/error.h


+ 0 - 0
src/RuneOptimizer/gui/gui.c → src/cli/runeoptimizer/gui/gui.c


+ 0 - 0
src/RuneOptimizer/gui/gui.h → src/cli/runeoptimizer/gui/gui.h


+ 1 - 1
src/RuneOptimizer/help/help.c → src/cli/runeoptimizer/help/help.c

@@ -25,7 +25,7 @@
  */
 
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "help.h"
 
 void help(){

+ 0 - 0
src/RuneOptimizer/help/help.h → src/cli/runeoptimizer/help/help.h


+ 91 - 53
src/RuneOptimizer/optimize/optimize.c → src/cli/runeoptimizer/optimize/optimize.c

@@ -31,7 +31,8 @@
 #include <sqlite3.h>
 #include <math.h>
 #include <pthread.h>
-#include "../RuneOptimizer.h"
+#include <signal.h>
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "optimize.h"
@@ -310,7 +311,7 @@ static void optimize_sort_results(Result results[MAX_RESULTS], int total);
  * in {@link error.h} if there were problems.
  * @todo Maybe I can bring that width down to 80...
  */
-int optimize_print_result(struct Unit *unit, Result *result);
+static int optimize_print_result(struct Unit *unit, Result *result);
 
 /**
  * Prepares and prints the output for the GUI.
@@ -319,15 +320,41 @@ int optimize_print_result(struct Unit *unit, Result *result);
  *
  * @param[in] results All results.
  * @param[in] result_count Number of results.
+ * @param[in] level Rune requested level, as indicated with the "--level"
+ * argument.
  * @return {@link SUCCESS} if all the runes could be fetch, or an error defined
  * in {@link error.h} if there were problems.
  */
-extern int optimize_print_gui(Result results[MAX_RESULTS], int result_count);
+static int optimize_print_gui(
+  Result results[MAX_RESULTS], int result_count, int level
+);
+
+/**
+ * Handles received signals.
+ *
+ * For SIGUSR1, it prints the current progress.
+ *
+ * @param[in] sig Signal code.
+ */
+static void optimize_handle_signal(int sig);
+
+/**
+ * The max number of combinations to test by the optimizer.
+ */
+unsigned long long max_combinations = 0;
+
+/**
+ * The number of combinations already tested by the optimizer.
+ */
+unsigned long long progress_combinations = 0;
 
 extern int optimize(int argc, char *argv[]){
 
     int status = SUCCESS;
 
+    // Set signal handler
+    signal(SIGUSR1, optimize_handle_signal);
+
     // Create custom argument array
     // If I don't do this, in the function optimize_parse_arguments,
     // every 7th (index 6, index 15...) are missing. NULL.
@@ -396,7 +423,7 @@ extern int optimize(int argc, char *argv[]){
     }
 
     // Precalculate how many combinations will have to be tested.
-    unsigned long long max_combinations =
+    max_combinations =
       (unsigned long long) rune_count[1] *
       (unsigned long long) rune_count[2] *
       (unsigned long long) rune_count[3] *
@@ -415,10 +442,6 @@ extern int optimize(int argc, char *argv[]){
     if (options.gui == FALSE){
         printf("\n\n__Optimization progress___________________________\n");
     }
-    else{
-        printf("Total combinations: %llu\n", max_combinations);
-        fflush(stdout);
-    }
 
     Optimizer_Data opt_data[options.threads];
     for (int t = 0; t < options.threads; t++){
@@ -475,7 +498,7 @@ extern int optimize(int argc, char *argv[]){
         }
         else{
             // Create the output
-            optimize_print_gui(results, result_count);
+            optimize_print_gui(results, result_count, options.level);
         }
     }
     else{
@@ -1490,48 +1513,48 @@ static void optimize_print_summary(
     printf("\n");
     printf(" -----------------------------------------\n");
     printf(
-      " | HP:  | %*d  | %*d  | %*d  |    ", 7,
+      " | HP:  | %*u  | %*u  | %*u  |    ", 7,
       unit->base_hp, 7, unit->current_hp, 7, options->min_stats->hp
     );
     if (options->level == 0) printf("Using runes at their current level\n");
     else printf("Considering runes at level %d\n", options->level);
     printf(
-      " | ATK: | %*d  | %*d  | %*d  |\n", 7,
+      " | ATK: | %*u  | %*u  | %*u  |\n", 7,
       unit->base_atk, 7, unit->current_atk, 7, options->min_stats->atk
     );
     printf(
-      " | DEF: | %*d  | %*d  | %*d  |    Runes considered by slot:\n",
+      " | DEF: | %*u  | %*u  | %*u  |    Runes considered by slot:\n",
       7, unit->base_def, 7, unit->current_def, 7, options->min_stats->def
     );
     printf(
-      " | SPD: | %*d  | %*d  | %*d  |      1:%*d    2:%*d    3:%*d\n",
+      " | SPD: | %*u  | %*u  | %*u  |      1:%*u    2:%*u    3:%*u\n",
       7, unit->base_spd, 7, unit->current_spd, 7, options->min_stats->spd,
       3, rune_count[1], 3, rune_count[2], 3, rune_count[3]
     );
     printf(
-      " | CRR: | %*d%% | %*d%% | %*d%% |      4:%*d    5:%*d    6:%*d\n",
+      " | CRR: | %*u%% | %*u%% | %*u%% |      4:%*u    5:%*u    6:%*u\n",
       7, unit->base_crr, 7, unit->current_crr, 7, options->min_stats->crr,
       3, rune_count[4], 3, rune_count[5], 3, rune_count[6]
     );
     printf(
-      " | CRD: | %*d%% | %*d%% | %*d%% |\n",
+      " | CRD: | %*u%% | %*u%% | %*u%% |\n",
       7, unit->base_crd, 7, unit->current_crd, 7, options->min_stats->crd
     );
     printf(
-      " | RES: | %*d%% | %*d%% | %*d%% |    Total combinations: %llu\n",
+      " | RES: | %*u%% | %*u%% | %*u%% |    Total combinations: %llu\n",
       7, unit->base_res, 7, unit->current_res, 7, options->min_stats->res,
       max_combinations
     );
     printf(
-      " | ACC: | %*d%% | %*d%% | %*d%% |\n",
+      " | ACC: | %*u%% | %*u%% | %*u%% |\n",
       7, unit->base_acc, 7, unit->current_acc, 7, options->min_stats->acc
     );
     printf(
-      " | EHP: | %*d  | %*d  | %*d  |\n",
+      " | EHP: | %*u  | %*u  | %*u  |\n",
       7, unit->base_ehp, 7, unit->current_ehp, 7, options->min_stats->ehp
     );
     printf(
-      " | DMG: | %*d  | %*d  | %*d  |\n",
+      " | DMG: | %*u  | %*u  | %*u  |\n",
       7, unit->base_dmg, 7, unit->current_dmg, 7, options->min_stats->dmg
     );
     printf(" -----------------------------------------\n");
@@ -1564,19 +1587,13 @@ static void *optimize_thread(void *data){
           (unsigned long)((opt_data->max_combinations) / 50)
           == 0
         ){
+            //progress_combinations += tested_combinations;
             if (opt_data->options.gui == FALSE){
                 printf("#");
                 //printf("%d", opt_data->thread_id);
+                // Line buffered! need to flush after every char.
+                fflush(stdout);
             }
-            else{
-                // For GUI flushing, newlines are required
-                printf(
-                  "%lu\n",
-                  (unsigned long)( (tested_combinations + 1) /
-                  (unsigned long)(opt_data->max_combinations / 20)));
-            }
-            // Line buffered! need to flush after every char.
-            fflush(stdout);
         }
 
         // Calculate rune sets at current indexes.
@@ -1793,6 +1810,7 @@ static void *optimize_thread(void *data){
 
         // Loop control. Rotate the reels 'right to left'
         tested_combinations ++;
+        progress_combinations ++;
         index[6] ++;
         if (index[6] == opt_data->count[6]){
             index[6] = 0;
@@ -1873,16 +1891,16 @@ int optimize_print_result(struct Unit *unit, Result *result){
     printf(" ------------------------------\n");
     printf(" | STAT | CURR.    | NEW      |\n");
     printf(" ------------------------------\n");
-    printf(" | HP:  | %*d  | %*d  |\n", 7, unit->current_hp, 7, result->stats.hp);
-    printf(" | ATK: | %*d  | %*d  |\n", 7, unit->current_atk, 7, result->stats.atk);
-    printf(" | DEF: | %*d  | %*d  |\n", 7, unit->current_def, 7, result->stats.def);
-    printf(" | SPD: | %*d  | %*d  |\n", 7, unit->current_spd, 7, result->stats.spd);
-    printf(" | CRR: | %*d%% | %*d%% |\n", 7, unit->current_crr, 7, result->stats.crr);
-    printf(" | CRD: | %*d%% | %*d%% |\n", 7, unit->current_crd, 7, result->stats.crd);
-    printf(" | RES: | %*d%% | %*d%% |\n", 7, unit->current_res, 7, result->stats.res);
-    printf(" | ACC: | %*d%% | %*d%% |\n", 7, unit->current_acc, 7, result->stats.acc);
-    printf(" | EHP: | %*d  | %*d  |\n", 7, unit->current_ehp, 7, result->stats.ehp);
-    printf(" | DMG: | %*d  | %*d  |\n", 7, unit->current_dmg, 7, result->stats.dmg);
+    printf(" | HP:  | %*u  | %*u  |\n", 7, unit->current_hp, 7, result->stats.hp);
+    printf(" | ATK: | %*u  | %*u  |\n", 7, unit->current_atk, 7, result->stats.atk);
+    printf(" | DEF: | %*u  | %*u  |\n", 7, unit->current_def, 7, result->stats.def);
+    printf(" | SPD: | %*u  | %*u  |\n", 7, unit->current_spd, 7, result->stats.spd);
+    printf(" | CRR: | %*u%% | %*u%% |\n", 7, unit->current_crr, 7, result->stats.crr);
+    printf(" | CRD: | %*u%% | %*u%% |\n", 7, unit->current_crd, 7, result->stats.crd);
+    printf(" | RES: | %*u%% | %*u%% |\n", 7, unit->current_res, 7, result->stats.res);
+    printf(" | ACC: | %*u%% | %*u%% |\n", 7, unit->current_acc, 7, result->stats.acc);
+    printf(" | EHP: | %*u  | %*u  |\n", 7, unit->current_ehp, 7, result->stats.ehp);
+    printf(" | DMG: | %*u  | %*u  |\n", 7, unit->current_dmg, 7, result->stats.dmg);
     printf(" ------------------------------\n");
 
     // This bit may be hard to follow.
@@ -1984,7 +2002,7 @@ int optimize_print_result(struct Unit *unit, Result *result){
               parameters
             );
             int curr_slot = -1;
-            while (1 == 1){
+            while (1){
                 int status = sqlite3_step(stats_res);
                 if (status == SQLITE_ROW){
                     // Print empty lines for no-stats
@@ -2136,37 +2154,40 @@ int optimize_print_result(struct Unit *unit, Result *result){
     return SUCCESS;
 }
 
-int optimize_print_gui(Result results[MAX_RESULTS], int result_count){
+static int optimize_print_gui(
+  Result results[MAX_RESULTS], int result_count, int level
+){
     //Print for GUI
     char tmp[300];
     strcpy(tmp, "");
     char json[300];
-    printf("{\"result_count\":%d,\"results\":[", result_count);
+    printf("{\"result_count\":%d,", result_count);
+    printf("\"rune_level\":%d,", level);
+    printf("\"results\":[");
     int result_limit = 100;
     for (int i = 0; i < result_count && i < result_limit; i++){
         //printf("RES LOOP %d", i);
         sprintf(tmp, "{\"id\":%d,\"rating\":%d,", i, results[i].rating);
         strcpy(json, tmp);
-        sprintf(tmp, "\"hp\":%d,", results[i].stats.hp);
+        sprintf(tmp, "\"hp\":%u,", results[i].stats.hp);
         strcat(json, tmp);
-        sprintf(tmp, "\"atk\":%d,", results[i].stats.atk);
+        sprintf(tmp, "\"atk\":%u,", results[i].stats.atk);
         strcat(json, tmp);
-        sprintf(tmp, "\"dfc\":%d,", results[i].stats.def);
+        sprintf(tmp, "\"dfc\":%u,", results[i].stats.def);
         strcat(json, tmp);
-        sprintf(tmp, "\"spd\":%d,", results[i].stats.spd);
+        sprintf(tmp, "\"spd\":%u,", results[i].stats.spd);
         strcat(json, tmp);
-        sprintf(tmp, "\"crr\":%d,", results[i].stats.crr);
+        sprintf(tmp, "\"crr\":%u,", results[i].stats.crr);
         strcat(json, tmp);
-
-        sprintf(tmp, "\"crd\":%d,", results[i].stats.crd);
+        sprintf(tmp, "\"crd\":%u,", results[i].stats.crd);
         strcat(json, tmp);
-        sprintf(tmp, "\"res\":%d,", results[i].stats.res);
+        sprintf(tmp, "\"res\":%u,", results[i].stats.res);
         strcat(json, tmp);
-        sprintf(tmp, "\"acc\":%d,", results[i].stats.acc);
+        sprintf(tmp, "\"acc\":%u,", results[i].stats.acc);
         strcat(json, tmp);
-        sprintf(tmp, "\"ehp\":%d,", results[i].stats.ehp);
+        sprintf(tmp, "\"ehp\":%u,", results[i].stats.ehp);
         strcat(json, tmp);
-        sprintf(tmp, "\"dmg\":%d,", results[i].stats.dmg);
+        sprintf(tmp, "\"dmg\":%u,", results[i].stats.dmg);
         strcat(json, tmp);
         strcat(json, "\"runes\":[");
         //printf("    A %s", json);
@@ -2188,7 +2209,24 @@ int optimize_print_gui(Result results[MAX_RESULTS], int result_count){
         printf(json);
     }
 
-    // End and print
+    // End
     printf("]}\n");
     return SUCCESS;
 }
+
+static void optimize_handle_signal(int sig){
+    if (sig == SIGUSR1){
+        unsigned int progress;
+        if (0 == max_combinations){
+            progress = 0;
+        }
+        else{
+            progress = progress_combinations * 100 / max_combinations;
+        }
+        printf(
+          "  Progress: %u%% (%llu / %llu)\n",
+          progress, progress_combinations, max_combinations
+        );
+        fflush(stdout);
+    }
+}

+ 1 - 1
src/RuneOptimizer/optimize/optimize.h → src/cli/runeoptimizer/optimize/optimize.h

@@ -26,7 +26,7 @@
 
 #pragma once
 
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 /**
  * Hard limit on the number of results

+ 1 - 1
src/RuneOptimizer/player/player.c → src/cli/runeoptimizer/player/player.c

@@ -25,7 +25,7 @@
  */
 
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "player.h"

+ 0 - 0
src/RuneOptimizer/player/player.h → src/cli/runeoptimizer/player/player.h


+ 4 - 4
src/RuneOptimizer/RuneOptimizer.c → src/cli/runeoptimizer/runeoptimizer.c

@@ -16,12 +16,12 @@
  */
 
 /**
- * @file RuneOptimizer.c
+ * @file runeoptimizer.c
  *
  * Implementation of the functions used by the gui command.
  *
  * This file implements the main function, and the functions declared in
- * {@link RuneOptimizer.h}.
+ * {@link runeoptimizer.h}.
  */
 
 #include <stdio.h>
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <sqlite3.h>
 #include <math.h>
-#include "RuneOptimizer.h"
+#include "runeoptimizer.h"
 #include "error/error.h"
 #include "db/db.h"
 #include "help/help.h"
@@ -289,6 +289,6 @@ int main(int argc, char *argv[]){
         fprintf(stderr, "Invalid command specified: %s\n", argv[1]);
         return(ERROR_INPUT_INVALID_COMMAND);
     }
-    
+
     return result;
 }

+ 1 - 1
src/RuneOptimizer/RuneOptimizer.h → src/cli/runeoptimizer/runeoptimizer.h

@@ -16,7 +16,7 @@
  */
 
 /**
- * @file RuneOptimizer.h
+ * @file runeoptimizer.h
  *
  * Declarations of the functions and data types used across the application.
  *

+ 1 - 1
src/RuneOptimizer/team/team.c → src/cli/runeoptimizer/team/team.c

@@ -25,7 +25,7 @@
 
 #include <stdio.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "team.h"
 

+ 3 - 3
src/RuneOptimizer/team/team.h → src/cli/runeoptimizer/team/team.h

@@ -28,7 +28,7 @@
 
 /**
  * Adds a unit to a team.
- * 
+ *
  * Inserts a row in the table 'units_teams'. In case of error, it will print a
  * description to stderr.
  *
@@ -43,7 +43,7 @@ extern int team_add_unit(int argc, char *argv[]);
 
 /**
  * Creates a team.
- * 
+ *
  * Saves it to the database table 'teams 'with the provided name and
  * (optionally) priority. It will have no units in 'units_teams'. In case of
  * error, it will print a description to stderr.
@@ -60,7 +60,7 @@ extern int team_create(int argc, char *argv[]);
 
 /**
  * Deletes a team.
- * 
+ *
  * It deletes the row from the table 'teams', and any related row in the table
  * 'units_teams'. In case of error, it will print a
  * description to stderr.

+ 1 - 1
src/RuneOptimizer/team/team_add_unit.c → src/cli/runeoptimizer/team/team_add_unit.c

@@ -26,7 +26,7 @@
  */
 
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "team.h"

+ 2 - 2
src/RuneOptimizer/team/team_create.c → src/cli/runeoptimizer/team/team_create.c

@@ -28,7 +28,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "team.h"
@@ -78,7 +78,7 @@ extern int team_create(int argc, char *argv[]){
         fprintf(stderr, "Unable to get id for the team\n");
         return(ERROR_DB_NEW_TEAM_ID);
     }
-    
+
     char priority_c[2];
     char id_c[4];
     sprintf(id_c, "%d", new_id);

+ 3 - 3
src/RuneOptimizer/team/team_delete.c → src/cli/runeoptimizer/team/team_delete.c

@@ -26,7 +26,7 @@
  */
 
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "team.h"
@@ -50,8 +50,8 @@ extern int team_delete(int argc, char *argv[]){
         );
         return(ERROR_INPUT_TEAM_DELETE_ARGUMENT_COUNT);
     }
-    
-    
+
+
     // Verify that the team exists
     int validation = team_delete_validate(argv[0]);
     if (SUCCESS != validation){

+ 2 - 2
src/RuneOptimizer/team/team_list.c → src/cli/runeoptimizer/team/team_list.c

@@ -27,7 +27,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "team.h"
@@ -57,7 +57,7 @@ extern int team_list(int argc, char *argv[]){
     printf(" --------------------------------------------------\n");
     printf(" | ID   | Name                             | Prio |\n");
     printf(" --------------------------------------------------\n");
-    while (1 == 1){
+    while (1){
         int status = sqlite3_step(stmt_teams);
         if (status == SQLITE_ROW){
             printf(

+ 1 - 1
src/RuneOptimizer/team/team_remove_unit.c → src/cli/runeoptimizer/team/team_remove_unit.c

@@ -26,7 +26,7 @@
  */
 
 #include <stdio.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "team.h"

+ 4 - 4
src/RuneOptimizer/unit/unit.c → src/cli/runeoptimizer/unit/unit.c

@@ -26,7 +26,7 @@
 
 #include <stdio.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "unit.h"
@@ -72,10 +72,10 @@ static int unit_list(char *search){
       parameters
     );
     int units_found = 0;
-    int unit_has_runes = FALSE;
     char tmp[32];
     while (SQLITE_ROW == sqlite3_step(stmt_units)){
         char unit_name[14];
+        int unit_has_runes = FALSE;
 
         // Print a separator between units
         if (units_found > 0){
@@ -144,7 +144,7 @@ static int unit_list(char *search){
           6, sqlite3_column_int(stmt_units, 20)
         );
         printf(
-          "                 | EHP: | %*d  | %*d  |\n",
+          "                 | EHP: | %*u  | %*u  |\n",
           6,
           calculate_ehp(
             sqlite3_column_int(stmt_units, 5),
@@ -157,7 +157,7 @@ static int unit_list(char *search){
           )
         );
         printf(
-          "                 | DMG: | %*d  | %*d  |\n",
+          "                 | DMG: | %*u  | %*u  |\n",
           6,
           calculate_dmg(
             sqlite3_column_int(stmt_units, 6),

+ 0 - 0
src/RuneOptimizer/unit/unit.h → src/cli/runeoptimizer/unit/unit.h


+ 1 - 1
src/RuneOptimizer/update/update.c → src/cli/runeoptimizer/update/update.c

@@ -25,7 +25,7 @@
 
 #include <stdio.h>
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "update.h"
 

+ 5 - 5
src/RuneOptimizer/update/update.h → src/cli/runeoptimizer/update/update.h

@@ -26,7 +26,7 @@
 
 #pragma once
 
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 /**
  * Max substats in a rune.
@@ -658,7 +658,7 @@ extern const int MAIN_STATS_VALUES[DIFFERENT_STATS][RUNE_MAX_STARS + 1][2];
 
 /**
  * Calculates the current stats for units with runes in the database.
- * 
+ *
  * It counts the bonus stats provided by runes and sets, and updates the
  * current_* columns in the database for every unit with at least one rune
  * assigned.
@@ -670,7 +670,7 @@ extern int update_current_stats();
 
 /**
  * Recreates the tables in the database.
- * 
+ *
  * It does not just delete the data, but it actualy drops and recreats the
  * tables. By default, itdoes this to all tables except 'teams' and
  * 'units_teams'.
@@ -684,7 +684,7 @@ extern int update_db_tables(unsigned char clear_teams);
 
 /**
  * Gets a monster name from it's ID.
- * 
+ *
  * If the monster is unawakened, the name will be in the format
  * 'name (element)'. If the monster is second awakened, the name will be
  * 'name (2A)'. For the rest of cases, it will be just 'name'. In any case, the
@@ -698,7 +698,7 @@ extern void update_get_monster_name(int id, char name[UNIT_NAME_LEN]);
 
 /**
  * Updaes the data in the table 'info'.
- * 
+ *
  * @param[in] id Player ID.
  * @param[in] name Player name.
  * @param[in] level Player level.

+ 9 - 9
src/RuneOptimizer/update/update_current_stats.c → src/cli/runeoptimizer/update/update_current_stats.c

@@ -28,7 +28,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <sqlite3.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "update.h"
@@ -171,14 +171,14 @@ extern int update_current_stats(){
         for (int i = 0; i < 9; i ++){
             parameters_update[i] = malloc(5);
         }
-        sprintf(parameters_update[0], "%d", unit.current_hp);
-        sprintf(parameters_update[1], "%d", unit.current_atk);
-        sprintf(parameters_update[2], "%d", unit.current_def);
-        sprintf(parameters_update[3], "%d", unit.current_spd);
-        sprintf(parameters_update[4], "%d", unit.current_crr);
-        sprintf(parameters_update[5], "%d", unit.current_crd);
-        sprintf(parameters_update[6], "%d", unit.current_res);
-        sprintf(parameters_update[7], "%d", unit.current_acc);
+        sprintf(parameters_update[0], "%u", unit.current_hp);
+        sprintf(parameters_update[1], "%u", unit.current_atk);
+        sprintf(parameters_update[2], "%u", unit.current_def);
+        sprintf(parameters_update[3], "%u", unit.current_spd);
+        sprintf(parameters_update[4], "%u", unit.current_crr);
+        sprintf(parameters_update[5], "%u", unit.current_crd);
+        sprintf(parameters_update[6], "%u", unit.current_res);
+        sprintf(parameters_update[7], "%u", unit.current_acc);
         parameters_update[8] = (char *) unit.id;
         if (
           SUCCESS !=

+ 1 - 1
src/RuneOptimizer/update/update_db_tables.c → src/cli/runeoptimizer/update/update_db_tables.c

@@ -26,7 +26,7 @@
 
 #include <stdio.h>
 #include <sqlite3.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 

+ 5 - 9
src/RuneOptimizer/update/update_efficiency.c → src/cli/runeoptimizer/update/update_efficiency.c

@@ -28,7 +28,7 @@
 #include <sqlite3.h>
 #include "../error/error.h"
 #include "../db/db.h"
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 extern int update_efficiency(char *id, float *efficiency, float *max_efficiency){
     *efficiency = 0.0f;
@@ -55,15 +55,11 @@ extern int update_efficiency(char *id, float *efficiency, float *max_efficiency)
 
     // Calculate efficiency for each slot.
     // Main slot is excluded from the calculation.
-    int slot = -1;
-    int stat = -1;
-    int value = -1;
-    int max_roll_value = -1;
     while (SQLITE_ROW == sqlite3_step(stmt_stats)){
-        slot = sqlite3_column_int(stmt_stats, 0);
-        stat = sqlite3_column_int(stmt_stats, 1);
-        value = sqlite3_column_int(stmt_stats, 2);
-        max_roll_value = STAT_ROLL_MAX[stat][stars];
+        int slot = sqlite3_column_int(stmt_stats, 0);
+        int stat = sqlite3_column_int(stmt_stats, 1);
+        int value = sqlite3_column_int(stmt_stats, 2);
+        int max_roll_value = STAT_ROLL_MAX[stat][stars];
         eff[slot] += (((float) value) / ((float) max_roll_value));
     }
     sqlite3_finalize(stmt_stats);

+ 2 - 2
src/RuneOptimizer/update/update_get_monster_name.c → src/cli/runeoptimizer/update/update_get_monster_name.c

@@ -25,10 +25,10 @@
  */
 
 #include <string.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 extern void update_get_monster_name(int id, char name[UNIT_NAME_LEN]){
-    
+
     /*
      * This list is generated from a SWDB database, with the query:
      * SELECT (

+ 3 - 2
src/RuneOptimizer/update/update_info.c → src/cli/runeoptimizer/update/update_info.c

@@ -26,11 +26,12 @@
 
 #include <stdio.h>
 #include <sqlite3.h>
+#include <string.h>
 #include <stdlib.h>
 #include <time.h>
 #include "../error/error.h"
 #include "../db/db.h"
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 
 extern int update_info(const char *id, const char *name, const char *level){
     time_t t = time(NULL);
@@ -43,7 +44,7 @@ extern int update_info(const char *id, const char *name, const char *level){
       tm.tm_hour, tm.tm_min, tm.tm_sec
     );
     char *query_parameters[5];
-    for (int i = 1; i < 5; i ++) query_parameters[i] = malloc(5);
+    for (int i = 0; i < 5; i ++) query_parameters[i] = malloc(strlen(ts));
     sprintf(query_parameters[0], "%s", id);
     sprintf(query_parameters[1], "%s", name);
     sprintf(query_parameters[2], "%s", level);

+ 68 - 69
src/RuneOptimizer/update/update_json.c → src/cli/runeoptimizer/update/update_json.c

@@ -29,7 +29,7 @@
 #include <string.h>
 #include <sqlite3.h>
 #include <json-c/json.h>
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "../error/error.h"
 #include "../db/db.h"
 #include "update.h"
@@ -120,7 +120,7 @@ extern int update_json(
     int unit_count = json_object_array_length(unit_list);
     for (int i = 0; i < unit_count; i++){
         idx = json_object_array_get_idx(unit_list, i);
-        
+
         status = update_json_unit(
           idx, six_stars, with_runes, &total_runes, &total_stats
         );
@@ -137,11 +137,11 @@ extern int update_json(
       json_object_get_string(wizard_level)
     );
     if (SUCCESS != status) return(status);
-    
+
     // All parsed
-    printf("%d units saved.\n", total_units);
-    printf("%d runes saved.\n", total_runes);
-    printf("%d rune stats saved.\n", total_stats);
+    printf("%u units saved.\n", total_units);
+    printf("%u runes saved.\n", total_runes);
+    printf("%u rune stats saved.\n", total_stats);
     printf("Player info updated\n");
 
     return(SUCCESS);
@@ -151,7 +151,7 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     int total_stats = 0;
     DB_Rune rune;
     char *query_parameters[43];
-    for (int i = 1; i < 43; i ++) query_parameters[i] = malloc(5);
+    for (int i = 1; i < 43; i ++) query_parameters[i] = malloc(35);
     // Rune ID
     json_object *rune_id = json_object_object_get(rune_json, "rune_id");
     strcpy((char *) rune.id, json_object_get_string(rune_id));
@@ -196,11 +196,11 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     json_object *sec_eff = json_object_object_get(rune_json, "sec_eff");
     // Loop stats
     int stat_count = json_object_array_length(sec_eff);
-    json_object *stat_json;
+
     rune.stat_count = 0;
     for (int i = 0; i < stat_count && i < 4; i++){
         rune.stat_count ++;
-        stat_json = json_object_array_get_idx(sec_eff, i);
+        json_object *stat_json = json_object_array_get_idx(sec_eff, i);
         json_object *stat = json_object_array_get_idx(stat_json, 0);
         rune.stats[i].stat = json_object_get_int(stat);
         json_object *value = json_object_array_get_idx(stat_json, 1);
@@ -225,39 +225,39 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     sprintf(query_parameters[7], "%f", rune.efficiency);
     sprintf(query_parameters[8], "%f", rune.max_efficiency);
     sprintf(query_parameters[9], "%d", rune.main.stat);
-    sprintf(query_parameters[10], "%d", rune.current_hp_percent);
-    sprintf(query_parameters[11], "%d", rune.current_atk_percent);
-    sprintf(query_parameters[12], "%d", rune.current_def_percent);
-    sprintf(query_parameters[13], "%d", rune.current_hp_flat);
-    sprintf(query_parameters[14], "%d", rune.current_atk_flat);
-    sprintf(query_parameters[15], "%d", rune.current_def_flat);
-    sprintf(query_parameters[16], "%d", rune.current_spd);
-    sprintf(query_parameters[17], "%d", rune.current_crr);
-    sprintf(query_parameters[18], "%d", rune.current_crd);
-    sprintf(query_parameters[19], "%d", rune.current_acc);
-    sprintf(query_parameters[20], "%d", rune.current_res);
-    sprintf(query_parameters[21], "%d", rune.lv12_hp_percent);
-    sprintf(query_parameters[22], "%d", rune.lv12_atk_percent);
-    sprintf(query_parameters[23], "%d", rune.lv12_def_percent);
-    sprintf(query_parameters[24], "%d", rune.lv12_hp_flat);
-    sprintf(query_parameters[25], "%d", rune.lv12_atk_flat);
-    sprintf(query_parameters[26], "%d", rune.lv12_def_flat);
-    sprintf(query_parameters[27], "%d", rune.lv12_spd);
-    sprintf(query_parameters[28], "%d", rune.lv12_crr);
-    sprintf(query_parameters[29], "%d", rune.lv12_crd);
-    sprintf(query_parameters[30], "%d", rune.lv12_acc);
-    sprintf(query_parameters[31], "%d", rune.lv12_res);
-    sprintf(query_parameters[32], "%d", rune.lv15_hp_percent);
-    sprintf(query_parameters[33], "%d", rune.lv15_atk_percent);
-    sprintf(query_parameters[34], "%d", rune.lv15_def_percent);
-    sprintf(query_parameters[35], "%d", rune.lv15_hp_flat);
-    sprintf(query_parameters[36], "%d", rune.lv15_atk_flat);
-    sprintf(query_parameters[37], "%d", rune.lv15_def_flat);
-    sprintf(query_parameters[38], "%d", rune.lv15_spd);
-    sprintf(query_parameters[39], "%d", rune.lv15_crr);
-    sprintf(query_parameters[40], "%d", rune.lv15_crd);
-    sprintf(query_parameters[41], "%d", rune.lv15_acc);
-    sprintf(query_parameters[42], "%d", rune.lv15_res);
+    sprintf(query_parameters[10], "%u", rune.current_hp_percent);
+    sprintf(query_parameters[11], "%u", rune.current_atk_percent);
+    sprintf(query_parameters[12], "%u", rune.current_def_percent);
+    sprintf(query_parameters[13], "%u", rune.current_hp_flat);
+    sprintf(query_parameters[14], "%u", rune.current_atk_flat);
+    sprintf(query_parameters[15], "%u", rune.current_def_flat);
+    sprintf(query_parameters[16], "%u", rune.current_spd);
+    sprintf(query_parameters[17], "%u", rune.current_crr);
+    sprintf(query_parameters[18], "%u", rune.current_crd);
+    sprintf(query_parameters[19], "%u", rune.current_acc);
+    sprintf(query_parameters[20], "%u", rune.current_res);
+    sprintf(query_parameters[21], "%u", rune.lv12_hp_percent);
+    sprintf(query_parameters[22], "%u", rune.lv12_atk_percent);
+    sprintf(query_parameters[23], "%u", rune.lv12_def_percent);
+    sprintf(query_parameters[24], "%u", rune.lv12_hp_flat);
+    sprintf(query_parameters[25], "%u", rune.lv12_atk_flat);
+    sprintf(query_parameters[26], "%u", rune.lv12_def_flat);
+    sprintf(query_parameters[27], "%u", rune.lv12_spd);
+    sprintf(query_parameters[28], "%u", rune.lv12_crr);
+    sprintf(query_parameters[29], "%u", rune.lv12_crd);
+    sprintf(query_parameters[30], "%u", rune.lv12_acc);
+    sprintf(query_parameters[31], "%u", rune.lv12_res);
+    sprintf(query_parameters[32], "%u", rune.lv15_hp_percent);
+    sprintf(query_parameters[33], "%u", rune.lv15_atk_percent);
+    sprintf(query_parameters[34], "%u", rune.lv15_def_percent);
+    sprintf(query_parameters[35], "%u", rune.lv15_hp_flat);
+    sprintf(query_parameters[36], "%u", rune.lv15_atk_flat);
+    sprintf(query_parameters[37], "%u", rune.lv15_def_flat);
+    sprintf(query_parameters[38], "%u", rune.lv15_spd);
+    sprintf(query_parameters[39], "%u", rune.lv15_crr);
+    sprintf(query_parameters[40], "%u", rune.lv15_crd);
+    sprintf(query_parameters[41], "%u", rune.lv15_acc);
+    sprintf(query_parameters[42], "%u", rune.lv15_res);
     if (
       SUCCESS !=
       db_execute(
@@ -293,7 +293,7 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
     //query_parameters[0] = rune.id; // No need, already has id
     query_parameters[1] = "-1"; // Main stat
     sprintf(query_parameters[2], "%d", rune.main.stat);
-    sprintf(query_parameters[3], "%d", rune.main.value);
+    sprintf(query_parameters[3], "%u", rune.main.value);
     query_parameters[4] = "0"; // Mains can't be grinded.
     query_parameters[5] = "0"; // Mains can't be enchanted.
     if (
@@ -316,7 +316,7 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         //query_parameters[0] = rune.id; // No need, already has id
         query_parameters[1] = "0"; // Main stat
         sprintf(query_parameters[2], "%d", rune.innate.stat);
-        sprintf(query_parameters[3], "%d", rune.innate.value);
+        sprintf(query_parameters[3], "%u", rune.innate.value);
         query_parameters[4] = "0"; // Innates can't be grinded.
         query_parameters[5] = "0"; // Innates can't be enchanted.
         if (
@@ -344,9 +344,9 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         sprintf(query_parameters[1], "%d", s + 1); // 1-4
         query_parameters[2] = malloc(5);
         sprintf(query_parameters[2], "%d", rune.stats[s].stat);
-        sprintf(query_parameters[3], "%d", rune.stats[s].value);
+        sprintf(query_parameters[3], "%u", rune.stats[s].value);
         query_parameters[4] = malloc(5);
-        sprintf(query_parameters[4], "%d", rune.stats[s].grind);
+        sprintf(query_parameters[4], "%u", rune.stats[s].grind);
         query_parameters[5] = malloc(5);
         sprintf(query_parameters[5], "%d", rune.stats[s].enchant);
         if (
@@ -365,14 +365,15 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         }
 
     }
-
     // Now that everything is in the database, I can calculate efficiencies
     float efficiency = 0.0f;
     float max_efficiency = 0.0f;
     update_efficiency(query_parameters[0], &efficiency, &max_efficiency);
     strcpy(query_parameters[2], query_parameters[0]); //ID
-    sprintf(query_parameters[0], "%6.2f", efficiency);
-    sprintf(query_parameters[1], "%6.2f", max_efficiency);
+    query_parameters[1] = malloc(35);
+    query_parameters[2] = malloc(35);
+    sprintf(query_parameters[0], "%3.2f", efficiency);
+    sprintf(query_parameters[1], "%3.2f", max_efficiency);
     if (
       SUCCESS !=
       db_execute(
@@ -385,7 +386,6 @@ static int update_json_rune(json_object *rune_json, unsigned char *unit_id){
         );
         return(-1);
     }
-
     return(total_stats);
 }
 
@@ -448,12 +448,11 @@ static int update_json_unit(
     // Parse unit runes
     json_object *unit_runes = json_object_object_get(unit_json, "runes");
     int rune_count = json_object_array_length(unit_runes);
-    json_object *idx;
     for (int i = 0; i < rune_count; i++){
         unit_has_runes = TRUE;
         // TODO: Test this: reference or value?
         total_runes ++;
-        idx = json_object_array_get_idx(unit_runes, i);
+        json_object *idx = json_object_array_get_idx(unit_runes, i);
         int status = update_json_rune(idx, unit.id);
         if (status != -1) *total_stats += status;
     }
@@ -469,27 +468,27 @@ static int update_json_unit(
         query_parameters[1] = (char *) unit.name;
         sprintf(query_parameters[2], "%d", unit.stars);
         sprintf(query_parameters[3], "%d", unit.level);
-        sprintf(query_parameters[4], "%d", unit.base_hp);
-        sprintf(query_parameters[5], "%d", unit.base_atk);
-        sprintf(query_parameters[6], "%d", unit.base_def);
-        sprintf(query_parameters[7], "%d", unit.base_spd);
-        sprintf(query_parameters[8], "%d", unit.base_crr);
-        sprintf(query_parameters[9], "%d", unit.base_crd);
-        sprintf(query_parameters[10], "%d", unit.base_res);
-        sprintf(query_parameters[11], "%d", unit.base_acc);
+        sprintf(query_parameters[4], "%u", unit.base_hp);
+        sprintf(query_parameters[5], "%u", unit.base_atk);
+        sprintf(query_parameters[6], "%u", unit.base_def);
+        sprintf(query_parameters[7], "%u", unit.base_spd);
+        sprintf(query_parameters[8], "%u", unit.base_crr);
+        sprintf(query_parameters[9], "%u", unit.base_crd);
+        sprintf(query_parameters[10], "%u", unit.base_res);
+        sprintf(query_parameters[11], "%u", unit.base_acc);
         // Insert current stats as base stats.
         // They will be updated later.
-        sprintf(query_parameters[12], "%d", unit.base_hp);
-        sprintf(query_parameters[13], "%d", unit.base_atk);
-        sprintf(query_parameters[14], "%d", unit.base_def);
-        sprintf(query_parameters[15], "%d", unit.base_spd);
-        sprintf(query_parameters[16], "%d", unit.base_crr);
-        sprintf(query_parameters[17], "%d", unit.base_crd);
-        sprintf(query_parameters[18], "%d", unit.base_res);
-        sprintf(query_parameters[19], "%d", unit.base_acc);
+        sprintf(query_parameters[12], "%u", unit.base_hp);
+        sprintf(query_parameters[13], "%u", unit.base_atk);
+        sprintf(query_parameters[14], "%u", unit.base_def);
+        sprintf(query_parameters[15], "%u", unit.base_spd);
+        sprintf(query_parameters[16], "%u", unit.base_crr);
+        sprintf(query_parameters[17], "%u", unit.base_crd);
+        sprintf(query_parameters[18], "%u", unit.base_res);
+        sprintf(query_parameters[19], "%u", unit.base_acc);
         // TODO: Get storage status. But how??
         sprintf(query_parameters[20], "%d", 0);
-        sprintf(query_parameters[21], "%d", unit.monster);
+        sprintf(query_parameters[21], "%u", unit.monster);
         if (
           SUCCESS !=
           db_execute(

+ 1 - 1
src/RuneOptimizer/update/update_rune_totals.c → src/cli/runeoptimizer/update/update_rune_totals.c

@@ -27,7 +27,7 @@
 #include <sqlite3.h>
 #include "../error/error.h"
 #include "../db/db.h"
-#include "../RuneOptimizer.h"
+#include "../runeoptimizer.h"
 #include "update.h"
 
 extern void update_rune_totals(struct DB_Rune *rune){

+ 1 - 1
src/test/RuneOptimizerE2ETest.c → src/cli/test/runeoptimizer_e2e_test.c

@@ -1,4 +1,4 @@
-#include "RuneOptimizerTest.c"
+#include "runeoptimizer_test.c"
 
 /**
  * Placeholder dummy test.

+ 1 - 1
src/test/RuneOptimizerIntegrationTest.c → src/cli/test/runeoptimizer_integration_test.c

@@ -1,4 +1,4 @@
-#include "RuneOptimizerTest.c"
+#include "runeoptimizer_test.c"
 
 /**
  * Placeholder dummy test.

+ 1 - 1
src/test/RuneOptimizerTest.c → src/cli/test/runeoptimizer_test.c

@@ -4,7 +4,7 @@
 #include "unity/unity.c"
 
 #define main app_main
-#include "../RuneOptimizer/RuneOptimizer.c"
+#include "../runeoptimizer/runeoptimizer.c"
 #undef main
 
 /**

+ 1 - 1
src/test/RuneOptimizerUnitTest.c → src/cli/test/runeoptimizer_unit_test.c

@@ -1,4 +1,4 @@
-#include "RuneOptimizerTest.c"
+#include "runeoptimizer_test.c"
 
 /**
  * Test the main function for called without any options.

+ 0 - 0
src/test/unity/unity.c → src/cli/test/unity/unity.c


+ 0 - 0
src/test/unity/unity.h → src/cli/test/unity/unity.h


+ 0 - 0
src/test/unity/unity_internals.h → src/cli/test/unity/unity_internals.h


+ 0 - 0
RuneOptimizerGUI/__init__.py → src/gui/runeoptimizer_gui/__init__.py


+ 0 - 0
RuneOptimizerGUI/entity/__init__.py → src/gui/runeoptimizer_gui/data/__init__.py


+ 20 - 0
src/gui/runeoptimizer_gui/data/data.py

@@ -0,0 +1,20 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+UNITS = {}
+TEAMS = {}

+ 0 - 0
RuneOptimizerGUI/gui/__init__.py → src/gui/runeoptimizer_gui/database/__init__.py


+ 20 - 0
src/gui/runeoptimizer_gui/database/database.py

@@ -0,0 +1,20 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+# Global database connection
+DB_CONNECTION = None

+ 59 - 56
RuneOptimizerGUI/entity/Rune.py → src/gui/runeoptimizer_gui/entity/Rune.py

@@ -16,6 +16,10 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import database.database as database
+import values.values as values
+from entity.RuneStat import RuneStat
+
 class Rune():
     """
     A rune.
@@ -61,7 +65,7 @@ class Rune():
         Gets a stat given by the rune.
 
     """
-    
+
     _id = None
     _unit = ""
     _type = 0
@@ -83,10 +87,9 @@ class Rune():
     def __init__(self, id=None):
         if id != None:
             self._load(id)
-            
+
     def _load(self, id):
-        global conn
-        cursor = conn.execute("""
+        cursor = database.CONNECTION.execute("""
           SELECT
             id,                    --  0
             unit,
@@ -145,50 +148,50 @@ class Rune():
             else:
                 self._unit = None
             self._type = int(row[2])
-            self._type_name = SET_NAMES[self._type]
+            self._type_name = values.SET_NAMES[self._type]
             self._slot = int(row[3])
             self._stars = int(row[4])
             self._level = int(row[5])
             self._quality = int(row[6])
-            self._quality_name = QUALITY_NAMES[self._quality]
+            self._quality_name = values.QUALITY_NAMES[self._quality]
             self._efficiency = float(row[7])
             self._max_efficiency = float(row[8])
-            self._stats_current[RUNE_STATS["HP_P"]] = int(row[9])
-            self._stats_current[RUNE_STATS["ATK_P"]] = int(row[10])
-            self._stats_current[RUNE_STATS["DEF_P"]] = int(row[11])
-            self._stats_current[RUNE_STATS["HP"]] = int(row[12])
-            self._stats_current[RUNE_STATS["ATK"]] = int(row[13])
-            self._stats_current[RUNE_STATS["DEF"]] = int(row[14])
-            self._stats_current[RUNE_STATS["SPD"]] = int(row[15])
-            self._stats_current[RUNE_STATS["CRR"]] = int(row[16])
-            self._stats_current[RUNE_STATS["CRD"]] = int(row[17])
-            self._stats_current[RUNE_STATS["RES"]] = int(row[18])
-            self._stats_current[RUNE_STATS["ACC"]] = int(row[19])
-            self._stats_lv12[RUNE_STATS["HP_P"]] = int(row[21])
-            self._stats_lv12[RUNE_STATS["ATK_P"]] = int(row[21])
-            self._stats_lv12[RUNE_STATS["DEF_P"]] = int(row[22])
-            self._stats_lv12[RUNE_STATS["HP"]] = int(row[23])
-            self._stats_lv12[RUNE_STATS["ATK"]] = int(row[22])
-            self._stats_lv12[RUNE_STATS["DEF"]] = int(row[25])
-            self._stats_lv12[RUNE_STATS["SPD"]] = int(row[26])
-            self._stats_lv12[RUNE_STATS["CRR"]] = int(row[27])
-            self._stats_lv12[RUNE_STATS["CRD"]] = int(row[28])
-            self._stats_lv12[RUNE_STATS["RES"]] = int(row[29])
-            self._stats_lv12[RUNE_STATS["ACC"]] = int(row[30])
-            self._stats_lv15[RUNE_STATS["HP_P"]] = int(row[31])
-            self._stats_lv15[RUNE_STATS["ATK_P"]] = int(row[32])
-            self._stats_lv15[RUNE_STATS["DEF_P"]] = int(row[33])
-            self._stats_lv15[RUNE_STATS["HP"]] = int(row[34])
-            self._stats_lv15[RUNE_STATS["ATK"]] = int(row[35])
-            self._stats_lv15[RUNE_STATS["DEF"]] = int(row[36])
-            self._stats_lv15[RUNE_STATS["SPD"]] = int(row[37])
-            self._stats_lv15[RUNE_STATS["CRR"]] = int(row[38])
-            self._stats_lv15[RUNE_STATS["CRD"]] = int(row[39])
-            self._stats_lv15[RUNE_STATS["RES"]] = int(row[40])
-            self._stats_lv15[RUNE_STATS["ACC"]] = int(row[41])
+            self._stats_current[values.RUNE_STATS["HP_P"]] = int(row[9])
+            self._stats_current[values.RUNE_STATS["ATK_P"]] = int(row[10])
+            self._stats_current[values.RUNE_STATS["DEF_P"]] = int(row[11])
+            self._stats_current[values.RUNE_STATS["HP"]] = int(row[12])
+            self._stats_current[values.RUNE_STATS["ATK"]] = int(row[13])
+            self._stats_current[values.RUNE_STATS["DEF"]] = int(row[14])
+            self._stats_current[values.RUNE_STATS["SPD"]] = int(row[15])
+            self._stats_current[values.RUNE_STATS["CRR"]] = int(row[16])
+            self._stats_current[values.RUNE_STATS["CRD"]] = int(row[17])
+            self._stats_current[values.RUNE_STATS["RES"]] = int(row[18])
+            self._stats_current[values.RUNE_STATS["ACC"]] = int(row[19])
+            self._stats_lv12[values.RUNE_STATS["HP_P"]] = int(row[21])
+            self._stats_lv12[values.RUNE_STATS["ATK_P"]] = int(row[21])
+            self._stats_lv12[values.RUNE_STATS["DEF_P"]] = int(row[22])
+            self._stats_lv12[values.RUNE_STATS["HP"]] = int(row[23])
+            self._stats_lv12[values.RUNE_STATS["ATK"]] = int(row[22])
+            self._stats_lv12[values.RUNE_STATS["DEF"]] = int(row[25])
+            self._stats_lv12[values.RUNE_STATS["SPD"]] = int(row[26])
+            self._stats_lv12[values.RUNE_STATS["CRR"]] = int(row[27])
+            self._stats_lv12[values.RUNE_STATS["CRD"]] = int(row[28])
+            self._stats_lv12[values.RUNE_STATS["RES"]] = int(row[29])
+            self._stats_lv12[values.RUNE_STATS["ACC"]] = int(row[30])
+            self._stats_lv15[values.RUNE_STATS["HP_P"]] = int(row[31])
+            self._stats_lv15[values.RUNE_STATS["ATK_P"]] = int(row[32])
+            self._stats_lv15[values.RUNE_STATS["DEF_P"]] = int(row[33])
+            self._stats_lv15[values.RUNE_STATS["HP"]] = int(row[34])
+            self._stats_lv15[values.RUNE_STATS["ATK"]] = int(row[35])
+            self._stats_lv15[values.RUNE_STATS["DEF"]] = int(row[36])
+            self._stats_lv15[values.RUNE_STATS["SPD"]] = int(row[37])
+            self._stats_lv15[values.RUNE_STATS["CRR"]] = int(row[38])
+            self._stats_lv15[values.RUNE_STATS["CRD"]] = int(row[39])
+            self._stats_lv15[values.RUNE_STATS["RES"]] = int(row[40])
+            self._stats_lv15[values.RUNE_STATS["ACC"]] = int(row[41])
 
         # Get rune stats
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
             SELECT slot, stat, value, grind, enchant
             FROM rune_stats WHERE rune = ?
@@ -213,27 +216,27 @@ class Rune():
     @property
     def id(self):
         return self._id
-    
+
     @property
     def unit(self):
         return self._unit
-    
+
     @property
     def type(self):
         return self._type
-    
+
     @property
     def type_name(self):
         return self._type_name
-    
+
     @property
     def slot(self):
         return self._slot
-    
+
     @property
     def stars(self):
         return self._stars
-    
+
     @property
     def level(self):
         return self._level
@@ -241,47 +244,47 @@ class Rune():
     @property
     def quality(self):
         return self._quality
-    
+
     @property
     def quality_name(self):
         return self._quality_name
-    
+
     @property
     def efficiency(self):
         return self._efficiency
-    
+
     @property
     def max_efficiency(self):
         return self._max_efficiency
-    
+
     @property
     def main_stat(self):
         return self._main_stat
-    
+
     @property
     def innate_stat(self):
         return self._innate_stat
-    
+
     @property
     def sub_stats(self):
         return self._sub_stats
-    
+
     @property
     def stats(self):
         if self._innate_stat != None:
             return [self._main_stat] + [self.innate_stat] + self._sub_stats
         else:
             return [self._main_stat] + self._sub_stats
-    
+
     def get_stat(self, stat, level = None):
         """
         Gets a stat given by the rune.
-        
+
         Level can be specified as 12 or 15 to get the values the rune will have
         at that level. If 12 is specified and the rune current level is higher,
         it's current value is returned.
         """
-        if stat in RUNE_STATS.values() == False:
+        if stat in values.RUNE_STATS.values() == False:
             return 0
         if int(level) == 12:
             return self._stats_lv12[stat]

+ 16 - 14
RuneOptimizerGUI/entity/RuneStat.py → src/gui/runeoptimizer_gui/entity/RuneStat.py

@@ -16,10 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import values.values as values
+
 class RuneStat():
     """
     A stat of a rune.
-    
+
     Any stat of a rune. Values are validated on setting and capped
     automatically.
 
@@ -51,38 +53,38 @@ class RuneStat():
 
     def __init__(self):
         pass
-    
+
     @property
     def slot(self):
         return self._slot
-    
+
     @slot.setter
     def slot(self, value):
         value = int(value)
         if value < -1 or value > 4:
             value = -1;
         self._slot = value
-        
+
     @property
     def stat(self):
         return self._stat
-    
+
     @stat.setter
     def stat(self, value):
         value = int(value)
         if value < 0:
             value = 0;
         self._stat = value
-        self._stat_name = STAT_NAMES[value]
-    
+        self._stat_name = values.STAT_NAMES[value]
+
     @property
     def name(self):
         return self._stat_name
-    
+
     @property
     def value(self):
         return self._value
-    
+
     @value.setter
     def value(self, value):
         value = int(value)
@@ -90,25 +92,25 @@ class RuneStat():
             value = 1;
         self._value = value
         self._total = self._value + self._grind
-        
+
     @property
     def grind(self):
         return self._grind
-    
+
     @grind.setter
     def grind(self, value):
         value = int(value)
         self._grind = value
         self._total = self._value + self._grind
-        
+
     @property
     def total(self):
         return self._total
-    
+
     @property
     def is_enchanted(self):
         return self._is_enchanted
-    
+
     @is_enchanted.setter
     def is_enchanted(self, value):
         if (value == True or int(value) == 1):

+ 27 - 27
RuneOptimizerGUI/entity/StatSet.py → src/gui/runeoptimizer_gui/entity/StatSet.py

@@ -20,12 +20,12 @@ import math
 class StatSet():
     """
     A set of stats.
-    
+
     It can be used as a set of stats a unit has in any given moment. Compound
     stats EHP and DMG can't be set manually and are calculated as the stats
     they depend on are set. Values are validated on setting and capped
     automatically.
-    
+
     Note that the value caps don't take into acount material monsters, which
     have uniquely low stats. But if you are optimizing your Angelmon runes, I
     can't help you.
@@ -64,14 +64,14 @@ class StatSet():
     _acc = 0
     _ehp = 0
     _dmg = 0
-    
+
     def __init__(self):
         pass
-    
+
     @property
     def hp(self):
         return self._hp
-    
+
     @hp.setter
     def hp(self, value):
         value = int(value)
@@ -79,11 +79,11 @@ class StatSet():
             value = 1;
         self._hp = value
         self._calculate_ehp()
-        
+
     @property
     def atk(self):
         return self._atk
-    
+
     @atk.setter
     def atk(self, value):
         value = int(value)
@@ -91,11 +91,11 @@ class StatSet():
             value = 1;
         self._atk = value
         self._calculate_dmg()
-        
+
     @property
     def dfc(self):
         return self._dfc
-    
+
     @dfc.setter
     def dfc(self, value):
         value = int(value)
@@ -103,22 +103,22 @@ class StatSet():
             value = 1;
         self._dfc = value
         self._calculate_ehp()
-        
+
     @property
     def spd(self):
         return self._spd
-    
+
     @spd.setter
     def spd(self, value):
         value = int(value)
         if value < 1:
             value = 1;
         self._spd = value
-    
+
     @property
     def crr(self):
         return self._crr
-    
+
     @crr.setter
     def crr(self, value):
         value = int(value)
@@ -128,11 +128,11 @@ class StatSet():
             value = 100
         self._crr = value
         self._calculate_dmg()
-    
+
     @property
     def crd(self):
         return self._crd
-    
+
     @crd.setter
     def crd(self, value):
         value = int(value)
@@ -140,11 +140,11 @@ class StatSet():
             value = 50;
         self._crd = value
         self._calculate_dmg()
-        
+
     @property
     def res(self):
         return self._res
-    
+
     @res.setter
     def res(self, value):
         value = int(value)
@@ -153,11 +153,11 @@ class StatSet():
         if value > 100:
             value = 100
         self._res = value
-    
+
     @property
     def acc(self):
         return self._acc
-    
+
     @acc.setter
     def acc(self, value):
         value = int(value)
@@ -166,28 +166,28 @@ class StatSet():
         if value > 85:
             value = 85
         self._acc = value
-        
+
     @property
     def ehp(self):
         return self._ehp
-    
+
     @property
     def dmg(self):
         return self._dmg
-    
+
     def _calculate_ehp(self):
         """
         Recalculates _ehp.
-        
+
         Uses _dfc and _hp, and it is called from automatically from their
         setters.
         """
         self._ehp = math.ceil((((self._dfc * 3.5) + 1140) * self._hp) / 1000)
-    
+
     def _calculate_dmg(self):
         """
         Recalculates _dmg.
-        
+
         Uses _atk, _crr and _crd, and it is called from automatically from
         their setters.
         """
@@ -195,7 +195,7 @@ class StatSet():
           (self._atk * (100 - self._crr) / 100) +
           (self._atk * (      self._crr  / 100) * (self._crd + 100) / 100)
         )
-    
+
     def values(self):
         """
         Gets all stats as an array.
@@ -206,4 +206,4 @@ class StatSet():
           self._hp,  self._atk, self._dfc, self._spd, self._crr,
           self._crd, self._res, self._acc, self._ehp, self._dmg
         ]
-    
+

+ 15 - 13
RuneOptimizerGUI/entity/Team.py → src/gui/runeoptimizer_gui/entity/Team.py

@@ -16,10 +16,13 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import database.database as database
+from entity.Unit import Unit
+
 class Team():
     """
     A team of units.
-    
+
     Very similar to the Teams class, but this one loads units, so it's not
     safe to have as property of Unit.
 
@@ -34,19 +37,18 @@ class Team():
     units : Unit[]
 
     """
-    
+
     _id = None
     _name = ""
     _priority = 1
     _units = []
-    
+
     def __init__(self, id=None):
         if id != None:
             self._load(id)
-            
+
     def _load(self, id):
-        global conn
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           "SELECT id, name, priority FROM teams WHERE id = ?",
           (id,)
         )
@@ -55,30 +57,30 @@ class Team():
             self._id = str(row[0])
             self._name = str(row[1])
             self._priority = int(row[2])
-            
+
             # Load units
             self._units = []
-            cursor = conn.execute(
+            cursor = database.CONNECTION.execute(
               "SELECT unit FROM units_teams WHERE team = ?",
               (self._id,)
             )
             rows = cursor.fetchall()
             for row in rows:
                 self.units.append(Unit(str(row[0])))
-    
+
     @property
     def id(self):
         return self._id
-    
+
     @property
     def name(self):
         return self._name
-    
+
     @property
     def priority(self):
         return self._priority
-    
+
     @property
     def units(self):
         return self._units
-    
+

+ 23 - 17
RuneOptimizerGUI/entity/Unit.py → src/gui/runeoptimizer_gui/entity/Unit.py

@@ -16,6 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import database.database as database
+import values.values as values
+from entity.StatSet import StatSet
+from entity.Rune import Rune
+from entity.UnitTeam import UnitTeam
+
 class Unit():
     """
     A unit.
@@ -57,7 +63,7 @@ class Unit():
         Processes data obtained from RuneOptimizer.
 
     """
-    
+
     _id = None
     _name = ""
     _stars = 1
@@ -74,9 +80,9 @@ class Unit():
     def __init__(self, id=None):
         if id != None:
             self._load(id)
-            
+
     def _load(self, id):
-        cursor = conn.execute("""
+        cursor = database.CONNECTION.execute("""
           SELECT
             id,          --  0
             name,
@@ -132,20 +138,20 @@ class Unit():
             self._stats.crd = int(row[18])
             self._stats.res = int(row[19])
             self._stats.acc = int(row[20])
-            
+
             # Get runes
             self._runes = [None, None, None, None, None, None]
-            cursor = conn.execute(
+            cursor = database.CONNECTION.execute(
               "SELECT id, slot FROM runes WHERE unit = ?", (self._id,)
             )
             rows = cursor.fetchall()
             for row in rows:
                 self._runes[int(row[1]) - 1] = Rune(str(row[0]))
                 self._has_runes = True
-                
+
             # Get teams
             self._teams = []
-            cursor = conn.execute(
+            cursor = database.CONNECTION.execute(
               "SELECT DISTINCT team FROM units_teams WHERE unit = ?",
               (self._id,)
             )
@@ -159,23 +165,23 @@ class Unit():
     @property
     def id(self):
         return self._id
-    
+
     @property
     def name(self):
         return self._name
-    
+
     @property
     def stars(self):
         return self._stars
-    
+
     @property
     def level(self):
         return self._level
-    
+
     @property
     def in_storage(self):
         return self._in_storage
-    
+
     @property
     def base_stats(self):
         return self._base_stats
@@ -183,23 +189,23 @@ class Unit():
     @property
     def stats(self):
         return self._stats
-    
+
     @property
     def runes(self):
         return self._runes
-    
+
     @property
     def has_runes(self):
         return self._has_runes
-    
+
     @property
     def teams(self):
         return self._teams
-    
+
     @property
     def in_teams(self):
         return self._in_teams
-    
+
     @property
     def priority(self):
         return self._priority

+ 11 - 9
RuneOptimizerGUI/entity/UnitTeam.py → src/gui/runeoptimizer_gui/entity/UnitTeam.py

@@ -16,10 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import database.database as database
+
 class UnitTeam():
     """
     A team of a unit.
-    
+
     Very similar to the Team class, but this one doesn't load units, so it's
     safe to have as property of Unit.
 
@@ -33,17 +35,17 @@ class UnitTeam():
         Team priority, from 1 to 50.
 
     """
-    
+
     _id = None
     _name = ""
     _priority = 1
-    
+
     def __init__(self, id=None):
         if id != None:
             self._load(id)
-            
+
     def _load(self, id):
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           "SELECT id, name, priority FROM teams WHERE id = ?",
           (id,)
         )
@@ -52,16 +54,16 @@ class UnitTeam():
             self._id = str(row[0])
             self._name = str(row[1])
             self._priority = int(row[2])
-    
+
     @property
     def id(self):
         return self._id
-    
+
     @property
     def name(self):
         return self._name
-    
+
     @property
     def priority(self):
         return self._priority
-    
+

+ 0 - 0
src/gui/runeoptimizer_gui/entity/__init__.py


+ 3 - 1
RuneOptimizerGUI/gui/DialogConfirm.py → src/gui/runeoptimizer_gui/gui/DialogConfirm.py

@@ -16,10 +16,12 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import wx
+
 class DialogConfirm(wx.Dialog):
     """
     Dialog for confirmations.
-    
+
     Simple dialog with a Yes and a No button.
     """
 

+ 7 - 4
RuneOptimizerGUI/gui/DialogNewTeam.py → src/gui/runeoptimizer_gui/gui/DialogNewTeam.py

@@ -16,10 +16,13 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import wx
+import database.database as database
+
 class DialogNewTeam(wx.Dialog):
     """
     Dialog for team creation.
-    
+
     Allows for name and priority input.
     """
 
@@ -66,7 +69,7 @@ class DialogNewTeam(wx.Dialog):
           parent=self, id=wx.ID_CANCEL, pos=(166, 100),
           size=(100, 40), style=wx.LC_REPORT, label="Close"
         )
-        
+
 
     def _accept(self, event=None):
         error = False
@@ -87,11 +90,11 @@ class DialogNewTeam(wx.Dialog):
             )
         if error == False:
             # TODO: Use a command
-            cursor = conn.execute("""
+            cursor = database.CONNECTION.execute("""
               INSERT INTO teams (id, name, priority) VALUES (
                 (SELECT max(CAST(id AS INTEGER)) + 1 FROM teams), ?, ?)
               """, (name, priority))
-            conn.commit()
+            DB_CONNECTION.commit()
             reload_teams()
             self.EndModal(wx.ID_OK)
 

+ 6 - 2
RuneOptimizerGUI/gui/DialogUpdateJson.py → src/gui/runeoptimizer_gui/gui/DialogUpdateJson.py

@@ -16,10 +16,14 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import os
+import pipes
+import wx
+
 class DialogUpdateJson(wx.Dialog):
     """
     The JSON data updater dialog.
-    
+
     Allows for JSON file selection and control options. Runs a update command.
 
     Parameters
@@ -110,7 +114,7 @@ class DialogUpdateJson(wx.Dialog):
         self._accept_button.Bind(wx.EVT_BUTTON, self._accept)
 
         anim = wx.adv.Animation(
-          os.path.dirname(os.path.realpath(__file__)) + '/res/icon/progress.gif'
+          os.path.dirname(os.path.realpath(__file__)) + '/../res/icon/progress.gif'
         )
         progress_ctrl = wx.adv.AnimationCtrl(
           parent=self, id=wx.ID_ANY, anim=anim, pos=(126, 176), size=(48, 48)

+ 9 - 6
RuneOptimizerGUI/gui/PanelInfo.py → src/gui/runeoptimizer_gui/gui/PanelInfo.py

@@ -16,10 +16,13 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import wx
+import values.values as values
+
 class PanelInfo(wx.Panel):
     """
     The panel that shows info about the app.
-    
+
     Static content, just some labels.
 
     """
@@ -56,7 +59,7 @@ class PanelInfo(wx.Panel):
         )
 
         wx.StaticText(
-          parent=self, id=wx.ID_ANY, label=app_info["name"],
+          parent=self, id=wx.ID_ANY, label=values.APP_INFO["name"],
           pos=(30, 30), size=(400, 40)
         ).SetFont(title_font)
         wx.StaticText(
@@ -64,7 +67,7 @@ class PanelInfo(wx.Panel):
           pos=(40, 90), size=(150, 30), style=wx.ALIGN_RIGHT
         ).SetFont(label_font)
         wx.StaticText(
-          parent=self, id=wx.ID_ANY, label=app_info["app_version"],
+          parent=self, id=wx.ID_ANY, label=values.APP_INFO["app_version"],
           pos=(200, 90), size=(400, 30)
         ).SetFont(content_font)
         wx.StaticText(
@@ -72,7 +75,7 @@ class PanelInfo(wx.Panel):
           pos=(40, 120), size=(150, 30), style=wx.ALIGN_RIGHT
         ).SetFont(label_font)
         wx.StaticText(
-          parent=self, id=wx.ID_ANY, label=app_info["gui_version"],
+          parent=self, id=wx.ID_ANY, label=values.APP_INFO["gui_version"],
           pos=(200, 120), size=(400, 30)
         ).SetFont(content_font)
         wx.StaticText(
@@ -81,11 +84,11 @@ class PanelInfo(wx.Panel):
         ).SetFont(label_font)
         wx.StaticText(
           parent=self, id=wx.ID_ANY,
-          label=app_info["author"] + " <" +app_info["author_mail"] + ">",
+          label=values.APP_INFO["author"] + " <" +values.APP_INFO["author_mail"] + ">",
           pos=(200, 150), size=(400, 30)
         ).SetFont(content_font)
         y = 180
-        for name, url in app_info["url"].items():
+        for name, url in values.APP_INFO["url"].items():
             wx.StaticText(
               parent=self, id=wx.ID_ANY, label=name + ":",
               pos=(40, y), size=(150, 30), style=wx.ALIGN_RIGHT

+ 86 - 21
RuneOptimizerGUI/gui/PanelOptimizer.py → src/gui/runeoptimizer_gui/gui/PanelOptimizer.py

@@ -15,6 +15,16 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
+import os
+import signal
+import json
+import multiprocessing
+from types import SimpleNamespace
+import wx
+import wx.grid
+import database.database as database
+import data.data as data
+import values.values as values
 
 class PanelOptimizer(wx.Panel):
     """
@@ -33,7 +43,10 @@ class PanelOptimizer(wx.Panel):
     _selected_unit = None
     _team_ids = []
     _unit_id_selector_index = []
+    _max_combinations = -1
     _options_sizer = None
+    _optimize_start_sizer = None
+    _optimize_stop_sizer = None
     _unit_choice = None
     _stat_grid = None
     _name_label = None
@@ -85,20 +98,22 @@ class PanelOptimizer(wx.Panel):
           pointSize=10, family=wx.FONTFAMILY_DEFAULT,
           style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_BOLD
         )
-        
+
         # Prepare the help icon
         icon_path = \
-          os.path.dirname(os.path.realpath(__file__)) + "/res/icon/help.png"
+          os.path.dirname(os.path.realpath(__file__)) + "/../res/icon/help.png"
         if os.name == 'nt':
             icon_path = os.path.dirname(__file__)
             if icon_path == "":
                 icon_path += "."
-            icon_path += "\\res\\icon\\help.png"
+            icon_path += "\\..\\res\\icon\\help.png"
         help_icon = wx.Bitmap(name=icon_path, type=wx.BITMAP_TYPE_PNG)
 
         # Sizer for all optimization options. Will be hidden until a unit is
         # selected.
         self._options_sizer = wx.BoxSizer(wx.VERTICAL)
+        self._optimize_start_sizer = wx.BoxSizer(wx.VERTICAL)
+        self._optimize_stop_sizer = wx.BoxSizer(wx.VERTICAL)
 
         # Unit info box
         unit_info_box = wx.StaticBox(
@@ -107,7 +122,7 @@ class PanelOptimizer(wx.Panel):
         )
 
         # Get data from all units and populate the unit selector
-        cursor = conn.execute("SELECT id, name FROM units ORDER BY name")
+        cursor = database.CONNECTION.execute("SELECT id, name FROM units ORDER BY name")
         unit_names = []
         for row in cursor:
             unit_names.append(row[1])
@@ -605,7 +620,7 @@ class PanelOptimizer(wx.Panel):
         ]
         # Team list
         teams_formatted = []
-        for team in teams.values():
+        for team in data.TEAMS.values():
             self._team_ids.append(team.id)
             teams_formatted.append(team.name + " (" + str(team.priority) + ")")
         teams_box = wx.StaticBox(
@@ -758,22 +773,39 @@ class PanelOptimizer(wx.Panel):
         self._options_sizer.Add(thread_help)
 
         # Button to start
-        btOptimize = wx.Button(
+        self._optimize_start_button = wx.Button(
           parent=self, id=wx.ID_ANY, pos=(750, 550),
           size=(100, 40), style=wx.LC_REPORT, label="OPTIMIZE"
         )
-        self._options_sizer.Add(btOptimize)
-        self.Bind(wx.EVT_BUTTON, self._start_optimization, btOptimize)
+        self._optimize_start_sizer.Add(self._optimize_start_button)
+        self._optimize_start_button.Bind(
+          wx.EVT_BUTTON, self._start_optimization
+        )
+
+        # Button to start
+        self._optimize_stop_button = wx.Button(
+          parent=self, id=wx.ID_ANY, pos=(750, 550),
+          size=(100, 40), style=wx.LC_REPORT, label="STOP"
+        )
+        self._optimize_stop_sizer.Add(self._optimize_stop_button)
+        self._optimize_stop_button.Bind(wx.EVT_BUTTON, self._stop_optimization)
 
         # Progress bar
+        self._progress_label = wx.StaticText(
+          parent=self, label="", id=wx.ID_ANY,
+          pos=(90, 550), size=(300, 25)
+        )
+        self._options_sizer.Add(self._progress_label)
         self._progress_gauge = wx.Gauge(
-          parent=self, id=wx.ID_ANY, range=20,
+          parent=self, id=wx.ID_ANY, range=100,
           pos=(50, 535), size=(650, 40), style=wx.GA_HORIZONTAL
         )
         self._options_sizer.Add(self._progress_gauge)
 
         # By default, hide all optimization options
         self._options_sizer.ShowItems(False)
+        self._optimize_start_sizer.ShowItems(False)
+        self._optimize_stop_sizer.ShowItems(False)
 
 
     def _show_help(self, event, text=None):
@@ -857,7 +889,7 @@ class PanelOptimizer(wx.Panel):
                     self._unit_choice.SetSelection(i)
                     break
 
-        self._selected_unit = units[unit_id]
+        self._selected_unit = data.UNITS[unit_id]
 
         # Set unit info labels
         self._name_label.SetLabel(self._selected_unit.name)
@@ -1003,10 +1035,32 @@ class PanelOptimizer(wx.Panel):
         self._opt_set_checklist_list[0].SetCheckedItems(())
         self._opt_set_checklist_list[1].SetCheckedItems(())
 
-
         self._options_sizer.ShowItems(True)
+        self._optimize_start_sizer.ShowItems(True)
+
+    def _stop_optimization(self, event=None):
+        """
+        Stops the optimization process.
+
+        Resets window element for another optimization.
+
+        Parameters
+        ----------
+        event : wxEvent, optional
+            The event that triggered the call (default is None).
+
+        """
+        if self._process is not None:
+            #wx.Process.Kill(self._pid())
+            os.kill(self._process.GetPid(), signal.SIGSTOP)
+        if self._timer is not None:
+            self._timer.Stop()
+        self._progress_gauge.SetValue(0)
+        self._progress_label.SetLabel("")
+        self._optimize_stop_sizer.ShowItems(False)
+        self._optimize_start_sizer.ShowItems(True)
 
-    def _start_optimization(self, event):
+    def _start_optimization(self, event=None):
         """
         Prepares and runs a command optimization.
 
@@ -1039,11 +1093,11 @@ class PanelOptimizer(wx.Panel):
               self._set_choice_list[i].GetSelection()
             ).upper().replace(" ", "");
             for j in range(0, 22):
-                name = SET_NAMES[j].upper()
+                name = values.SET_NAMES[j].upper()
                 if len(name) > 7:
                     name = name[0:7]
                 if selected == name:
-                    sets += SET_NAMES[j].lower() + ","
+                    sets += values.SET_NAMES[j].lower() + ","
         if len(sets) > 0:
             sets = sets[:-1]
             # TODO: ELSE ERROR
@@ -1151,21 +1205,23 @@ class PanelOptimizer(wx.Panel):
 
         command += (" --gui ")
         print("Command: " + command)
+        self._progress_label.SetLabel("")
         self._progress_gauge.SetValue(0)
 
         # Timer ot periodically check on the process
         self._timer = wx.Timer(self)
-        self._timer.Start(1000)
+        self._timer.Start(400)
         self.Bind(wx.EVT_TIMER, self._check_process)
 
         # Create and execute the process
         self.Bind(wx.EVT_END_PROCESS, self._optimization_complete)
-        #print("Command: " + command)
+        self._optimize_start_sizer.ShowItems(False)
+        self._optimize_stop_sizer.ShowItems(True)
         self._process = wx.Process(self)
         self._process.Redirect()
         wx.Execute(command, wx.EXEC_ASYNC, self._process)
 
-    def _check_process(self, event):
+    def _check_process(self, event=None):
         """
         Checks the process output and updates progress bar.
 
@@ -1176,6 +1232,9 @@ class PanelOptimizer(wx.Panel):
 
         """
         if self._process is not None:
+            #wx.Process.Kill(self._pid())
+            os.kill(self._process.GetPid(), signal.SIGUSR1)
+            #signal.signal(signal.SIGUSR1,signalUSR1_handler)
             stream = self._process.GetInputStream()
             if stream.CanRead():
                 text = bytes.decode(stream.read())
@@ -1185,9 +1244,12 @@ class PanelOptimizer(wx.Panel):
                 if text.rfind("\n") != -1:
                     text = text[text.rfind("\n") + 1:]
 
-                # If the line is just a number, it's a progress indicator (1-20)
-                if text.isnumeric() and int(text) <= 20:
-                    self._progress_gauge.SetValue(int(text))
+                self._progress_label.SetLabel(text)
+                for character in '%()/':
+                    text = text.replace(character, '')
+                text = text.strip().split()[1:]
+                if text[0].isnumeric() and int(text[0]) <= 100:
+                    self._progress_gauge.SetValue(int(text[0]))
         else:
             self._timer.Stop()
 
@@ -1205,7 +1267,10 @@ class PanelOptimizer(wx.Panel):
         """
 
         self._timer.Stop()
-        self._progress_gauge.SetValue(20)
+        self._progress_gauge.SetValue(100)
+        self._progress_label.SetLabel("")
+        self._optimize_stop_sizer.ShowItems(False)
+        self._optimize_start_sizer.ShowItems(True)
 
         stream = self._process.GetInputStream()
         json_text = ""

+ 49 - 29
RuneOptimizerGUI/gui/PanelResults.py → src/gui/runeoptimizer_gui/gui/PanelResults.py

@@ -16,6 +16,15 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import math
+import json
+import wx
+import wx.grid
+from types import SimpleNamespace
+import database.database as database
+import data.data as data
+from entity.Rune import Rune
+
 class PanelResults(wx.Panel):
     """
     The panel that shows results.
@@ -31,6 +40,7 @@ class PanelResults(wx.Panel):
     _results = None
     _data = None
     _page = 0
+    _level = 0
     _total_pages = 0
     _lines_per_page = 10
     _selected_result_index = -1
@@ -97,28 +107,28 @@ class PanelResults(wx.Panel):
         # Unit name, or placeholder message
         self._unit_name_label = wx.StaticText(
             parent=self, id=wx.ID_ANY, label="Nothing yet.\nOptimize something!",
-            pos=(10, 0), size=(400, 50)
+            pos=(10, 0), size=(400, 100)
         )
         self._unit_name_label.SetFont(title_font)
 
         #Unit details
         self._stars_label = wx.StaticText(
-          parent=self, label="", pos=(20, 30), size=(120, 100)
+          parent=self, label="", pos=(20, 30), size=(180, 100)
         )
         self._stars_label.SetFont(title_font)
         self._table_sizer.Add(self._stars_label)
         self._level_label = wx.StaticText(
-          parent=self, label="", pos=(20, 60), size=(120, 100)
+          parent=self, label="", pos=(20, 60), size=(180, 100)
         )
         self._table_sizer.Add(self._level_label)
         self._level_label.SetFont(subtitle_font)
         self._priority_label = wx.StaticText(
-          parent=self, label="", pos=(20, 90), size=(120, 100)
+          parent=self, label="", pos=(20, 90), size=(180, 100)
         )
         self._priority_label.SetFont(subtitle_font)
         self._table_sizer.Add(self._priority_label)
         self._id_label = wx.StaticText(
-          parent=self, label="", pos=(20, 120), size=(120, 100)
+          parent=self, label="", pos=(20, 120), size=(180, 100)
         )
         self._id_label.SetFont(subtitle_font)
         self._table_sizer.Add(self._id_label)
@@ -598,7 +608,7 @@ class PanelResults(wx.Panel):
         """
 
         if unit_id != None:
-            self._unit = units[unit_id]
+            self._unit = data.UNITS[unit_id]
 
         self._update_base_and_current_stats()
         # Read results
@@ -608,12 +618,18 @@ class PanelResults(wx.Panel):
         )
 
 
-
-        self._total_pages = math.ceil(len(self._results.results) / self._lines_per_page)
+        self._level = self._results.rune_level
+        self._total_pages = (
+          math.ceil(len(self._results.results) / self._lines_per_page)
+        )
         self._page = 0
         self._print_results()
 
-    def _page_prev(self, event):
+        # Select the first row and load it
+        self._result_grid.SelectRow(row=0, addToSelected=False)
+        self._result_selected(event=None)
+
+    def _page_prev(self, event=None):
         """
         Goes to the previous page of results.
 
@@ -631,7 +647,7 @@ class PanelResults(wx.Panel):
             self._page -= 1
             self._print_results()
 
-    def _page_next(self, event):
+    def _page_next(self, event=None):
         """
         Goes to the next page of results.
 
@@ -649,7 +665,7 @@ class PanelResults(wx.Panel):
             self._page += 1
             self._print_results()
 
-    def _apply_runes(self, event):
+    def _apply_runes(self, event=None):
         """
         Applies the selected results and saves data to the database.
 
@@ -660,13 +676,11 @@ class PanelResults(wx.Panel):
 
         """
 
-        global conn
-
         if (self._selected_result_index < 0):
             # TODO: Show error
             return;
         # First, unassign all runes currently assigned to the unit
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
             UPDATE runes SET unit = null
             WHERE unit = ?
@@ -677,7 +691,7 @@ class PanelResults(wx.Panel):
         )
 
         # Next, mark units as modified
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
             UPDATE units SET modified = 1
             WHERE
@@ -696,7 +710,7 @@ class PanelResults(wx.Panel):
         )
 
         # Lastly, assign the runes
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
             UPDATE runes SET unit = ?
             WHERE id IN (?, ?, ?, ?, ?, ?)
@@ -711,7 +725,7 @@ class PanelResults(wx.Panel):
             self._results.results[self._selected_result_index].runes[5]
           )
         )
-        conn.commit()
+        database.CONNECTION.commit()
         # TODO: Recalculate all modified units stats from the database
         print("Applied!")
         self._recalculte_stats_of_modified_units()
@@ -785,7 +799,7 @@ class PanelResults(wx.Panel):
         # Make the table visible
         self._table_sizer.ShowItems(True)
 
-    def _result_selected(self, event):
+    def _result_selected(self, event=None):
         """
         Populates and shows the runes and effective stats with the
         currently seleced result.
@@ -1069,7 +1083,7 @@ class PanelResults(wx.Panel):
             self._location_label_list[i].SetLabel("")
 
         # Display the runes
-        cursor = conn.execute(
+        cursor = database.CONNECTION.execute(
           """
             SELECT
               runes.id,
@@ -1099,10 +1113,17 @@ class PanelResults(wx.Panel):
             # Rows are 18 charactes width
 
             # First line: Set name, level
-            self._set_label_list[rune.slot - 1].SetLabel(
-              rune.type_name[0:9].ljust(12, " ") + \
-              "+" + str(rune.level).ljust(3, " ")
-            )
+            if self._level > rune.level:
+                self._set_label_list[rune.slot - 1].SetLabel(
+                  rune.type_name[0:9].ljust(9, " ")
+                  + "+" + str(rune.level).ljust(2, " ")
+                  + "\u2794" + str(self._level)
+                )
+            else:
+                self._set_label_list[rune.slot - 1].SetLabel(
+                  rune.type_name[0:9].ljust(12, " ") + \
+                  "+" + str(rune.level).ljust(3, " ")
+                )
 
             # Second line: ID
             lvl_set_eff = rune.type_name[0:6].ljust(6, " ")
@@ -1122,7 +1143,7 @@ class PanelResults(wx.Panel):
             # Source
             label_source = ""
             if rune.unit is not None:
-                label_source = units[rune.unit].name
+                label_source = data.UNITS[rune.unit].name
             else:
                 label_source = "In storage"
             self._location_label_list[rune.slot - 1].SetLabel(label_source)
@@ -1180,8 +1201,7 @@ class PanelResults(wx.Panel):
         It doen't remove the modified flag.
         """
 
-        global conn
-        unit_cursor = conn.execute("""
+        unit_cursor = database.CONNECTION.execute("""
           SELECT
             base_hp,
             base_atk,
@@ -1197,7 +1217,7 @@ class PanelResults(wx.Panel):
         """)
         i = 0
         for unit_row in unit_cursor:
-            rune_cursor = conn.execute(
+            rune_cursor = database.CONNECTION.execute(
               """
                 SELECT
                   sum(current_hp_percent) AS hp,
@@ -1231,7 +1251,7 @@ class PanelResults(wx.Panel):
             crd = unit_row[5] + rune_row[8]
             res = unit_row[6] + rune_row[9]
             acc = unit_row[7] + rune_row[10]
-            conn.execute(
+            database.CONNECTION.execute(
               """
                 UPDATE units SET
                   current_hp = ?,
@@ -1246,4 +1266,4 @@ class PanelResults(wx.Panel):
               """,
               (hp, atk, dfc, spd, crr, crd, res, acc, unit_row[8],)
             )
-        conn.commit()
+        database.CONNECTION.commit()

+ 23 - 17
RuneOptimizerGUI/gui/PanelTeams.py → src/gui/runeoptimizer_gui/gui/PanelTeams.py

@@ -15,6 +15,12 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
+import wx
+import wx.grid
+import database.database as database
+import data.data as data
+from gui.DialogConfirm import DialogConfirm
+from gui.DialogNewTeam import DialogNewTeam
 
 class PanelTeams(wx.Panel):
     """
@@ -24,7 +30,7 @@ class PanelTeams(wx.Panel):
     removing units to and from teams.
 
     """
-    
+
     _selected_team = None
     _title_change_pending = False
     _priority_change_pending = False
@@ -57,7 +63,7 @@ class PanelTeams(wx.Panel):
 
         # Parent constructor
         wx.Panel.__init__(self, parent=parent, id=id)
-        
+
         # Prepare some fonts
         bold_font = wx.Font(
           pointSize=10, family=wx.FONTFAMILY_DEFAULT,
@@ -133,7 +139,7 @@ class PanelTeams(wx.Panel):
           label="Name             Prio.   Sto.",
           pos=(10, 10), size=(190, 20)
         ).SetFont(bold_font)
-        
+
         self._all_unit_list = wx.ListCtrl(
           parent=all_units_box, id=wx.ID_ANY, pos=(10, 30), size=(190, 165),
           style=wx.LC_REPORT|wx.LC_NO_HEADER
@@ -214,7 +220,7 @@ class PanelTeams(wx.Panel):
         """
         self._team_list.DeleteAllItems()
         i = 0
-        for team in teams.values():
+        for team in data.TEAMS.values():
             self._team_list.InsertItem(i, team.name)
             self._team_list.SetItem(i, 1, str(team.priority))
             self._team_list.SetItemData(i, int(team.id))
@@ -239,7 +245,7 @@ class PanelTeams(wx.Panel):
         opt_no_runes = self._filter_storage_check.GetValue()
         opt_no_teams = self._filter_no_teams_check.GetValue()
         self._all_unit_list.DeleteAllItems()
-        for unit in units.values():
+        for unit in data.UNITS.values():
             if opt_storage == False and unit.in_storage == True:
                 continue
             if opt_no_runes == False and unit.has_runes == False:
@@ -310,13 +316,13 @@ class PanelTeams(wx.Panel):
         with DialogConfirm(parent=self, message=message) as dlg:
             if dlg.ShowModal() == wx.ID_OK:
                 # TODO: Do this with a command
-                cursor = conn.execute(
+                cursor = DB_CONNECTION.execute(
                   "DELETE FROM units_teams WHERE team = ?",
                   (self._team_selected.id,))
-                cursor = conn.execute(
+                cursor = DB_CONNECTION.execute(
                   "DELETE FROM teams WHERE id = ?", (self._team_selected.id,)
                 )
-                conn.commit()
+                DB_CONNECTION.commit()
                 reload_teams()
 
                 # Clear the selection and hide the details
@@ -341,9 +347,9 @@ class PanelTeams(wx.Panel):
             unitId = self._all_unit_list.GetItemData(selected_index)
             # TODO: Do this with a command
             query = "INSERT INTO units_teams (team, unit) VALUES (?, ?)";
-            cursor = conn.execute(query, (self._team_selected.id, unitId))
+            cursor = DB_CONNECTION.execute(query, (self._team_selected.id, unitId))
             selected_index = self._all_unit_list.GetNextSelected(selected_index)
-        conn.commit()
+        DB_CONNECTION.commit()
         reload_teams();
         self._populate_team_unit_list(event=None)
         self._populate_unit_list(event=None)
@@ -366,9 +372,9 @@ class PanelTeams(wx.Panel):
             unitId = self._team_unit_list.GetItemData(selected_index)
             # TODO: Do this with a command
             query = "DELETE FROM units_teams WHERE team = ? AND unit = ?";
-            cursor = conn.execute(query, (self._team_selected.id, unitId))
+            cursor = database.CONNECTION.execute(query, (self._team_selected.id, unitId))
             selected_index = self._team_unit_list.GetNextSelected(selected_index)
-        conn.commit()
+        database.CONNECTION.commit()
         reload_teams()
         self._populate_team_unit_list(event=None)
         self._populate_unit_list(event=None)
@@ -387,7 +393,7 @@ class PanelTeams(wx.Panel):
         """
         selected_team_id = \
           str(self._team_list.GetItemData(self._team_list.GetFirstSelected()))
-        self._team_selected = teams[selected_team_id]
+        self._team_selected = data.TEAMS[selected_team_id]
         self._populate_team_unit_list(event=None)
 
         # Unbind for the automatic change
@@ -446,8 +452,8 @@ class PanelTeams(wx.Panel):
                 )
                 # TODO: Do this with a command
                 query = "UPDATE teams SET name = ? WHERE id = ?";
-                cursor = conn.execute(query, (new_title, self._team_selected.id))
-                conn.commit()
+                cursor = DB_CONNECTION.execute(query, (new_title, self._team_selected.id))
+                DB_CONNECTION.commit()
                 reload_teams()
                 self._populate_team_list(event=None)
             else:
@@ -502,8 +508,8 @@ class PanelTeams(wx.Panel):
                 )
                 # TODO Do this with a command, when there is a command to do it.
                 query = "UPDATE teams SET priority = ? WHERE id = ?";
-                cursor = conn.execute(query, (new_priority, self._team_selected.id))
-                conn.commit()
+                cursor = DB_CONNECTION.execute(query, (new_priority, self._team_selected.id))
+                DB_CONNECTION.commit()
                 reload_teams()
                 self._populate_team_list(event=None)
                 self.GetParent().panel_units.populate_unit_list(event=None)

+ 11 - 8
RuneOptimizerGUI/gui/PanelUnits.py → src/gui/runeoptimizer_gui/gui/PanelUnits.py

@@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
+import wx
+import wx.grid
+import data.data as data
 
 class PanelUnits(wx.Panel):
     """
@@ -153,7 +156,7 @@ class PanelUnits(wx.Panel):
         )
         self._details_sizer.Add(self._name_label)
         self._name_label.SetFont(title_font)
-        
+
         #Unit details
         self._stars_label = wx.StaticText(
           parent=self, label="", pos=(250, 30), size=(120, 100)
@@ -175,7 +178,7 @@ class PanelUnits(wx.Panel):
         )
         self._id_label.SetFont(subtitle_font)
         self._details_sizer.Add(self._id_label)
-        
+
         # Button to go to the ptimizer
         optimize_button = wx.Button(
           parent=self, id=wx.ID_ANY, pos=(700, 520), size=(120, 50),
@@ -318,7 +321,7 @@ class PanelUnits(wx.Panel):
             pos=(5, 15), size=(130, 10)
           )
         ]
-        
+
 
         self._main_label_list = [
           wx.StaticText(
@@ -373,7 +376,7 @@ class PanelUnits(wx.Panel):
             pos=(5, 50), size=(130, 10)
           )
         ]
-        
+
 
         self._stat_label_list = [
           [
@@ -552,7 +555,7 @@ class PanelUnits(wx.Panel):
         opt_no_runes = self._filter_storage_check.GetValue()
         opt_no_teams = self._filter_no_teams_check.GetValue()
         self._unit_list.DeleteAllItems()
-        for unit in units.values():
+        for unit in data.UNITS.values():
             if opt_storage == False and unit.in_storage == True:
                 continue
             if opt_no_runes == False and unit.has_runes == False:
@@ -601,7 +604,7 @@ class PanelUnits(wx.Panel):
         unit_id = \
           str(self._unit_list.GetItemData(self._unit_list.GetFirstSelected()))
 
-        self._selected_unit = units[unit_id]
+        self._selected_unit = data.UNITS[unit_id]
 
         # Set label
         self._name_label.SetLabel(self._selected_unit.name)
@@ -615,7 +618,7 @@ class PanelUnits(wx.Panel):
         )
         self._id_label.SetLabel("#" + self._selected_unit.id)
         self._details_sizer.ShowItems(True)
-        
+
         # Populate stats
         self._stat_grid.SetCellValue(
           row=0, col=0, s=str(self._selected_unit.base_stats.hp)
@@ -702,7 +705,7 @@ class PanelUnits(wx.Panel):
             eff = ("  Eff:" + str(effv) + "%")
             lvl_set_eff += eff
             self._id_label_list[i].SetLabel(("#" + rune.id).rjust(15, " "))
-            
+
             # Efficiency
             label_eff_text = \
               "Eff.: " + ("{:.1f}".format(rune.efficiency)).rjust(4, " ") + \

+ 19 - 15
RuneOptimizerGUI/gui/RuneOptimizerFrame.py → src/gui/runeoptimizer_gui/gui/RuneOptimizerFrame.py

@@ -16,6 +16,11 @@ RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
 
+import wx
+import database.database as database
+from gui.TabList import Tab_List
+from gui.DialogUpdateJson import DialogUpdateJson
+
 class RuneOptimizerFrame(wx.Frame):
     """
     The main application window.
@@ -30,7 +35,6 @@ class RuneOptimizerFrame(wx.Frame):
 
         """
 
-        global conn
         super(RuneOptimizerFrame, self).__init__(*args, **kw)
 
         # Create and configure the menue
@@ -39,7 +43,7 @@ class RuneOptimizerFrame(wx.Frame):
 
         # Create the statsusbar
         status = "Status: "
-        cursor = conn.execute("""
+        cursor = database.CONNECTION.execute("""
           SELECT
             player_id,
             player_name,
@@ -55,7 +59,7 @@ class RuneOptimizerFrame(wx.Frame):
             status = \
               row[1] + ", Lv" + str(row[2]) + " (ID #" +str(row[0]) + ")."
             status += " Last updated " + str(row[3])
-            if (row[4] == 1):
+            if row[4] == 1:
                 status += ". Modifications applied since."
             else:
                 status += ". No modifications applied since."
@@ -65,7 +69,7 @@ class RuneOptimizerFrame(wx.Frame):
         # Create tabs
         tabs = Tab_List(parent=pnl, id=wx.ID_ANY)
 
-    def _make_menu_bar(self):
+    def _make_menu_bar(self=None):
         """
         Sets up the application menu.
         """
@@ -75,22 +79,22 @@ class RuneOptimizerFrame(wx.Frame):
           -1,
           "&Update from JSON file\tCtrl-J",
           "Updates the database from a profile JSON file."
-        );
+        )
         update_swdb = update_menu.Append(
           -1,
           "&Update from SWDB\tCtrl-W",
           "Updates the database from data retrieved from a SWDB instance."
-        );
+        )
         update_swarfarm = update_menu.Append(
           -1,
           "&Update from Sarfarm\tCtrl-F",
           "Updates the database from data retrieved from Swarfarm."
-        );
+        )
         update_sqlite = update_menu.Append(
           -1,
           "&Update from a sqlite database\tCtrl-Q",
           "Updates the database from a SWDB sqlite database."
-        );
+        )
 
         file_menu = wx.Menu()
         file_about = file_menu.Append(wx.ID_ABOUT)
@@ -111,7 +115,7 @@ class RuneOptimizerFrame(wx.Frame):
         self.Bind(wx.EVT_MENU, self._update_from_swarfarm, update_swarfarm)
         self.Bind(wx.EVT_MENU, self._update_from_sqlite, update_sqlite)
 
-    def _close_app(self, event):
+    def _close_app(self, event=None):
         """
         Closes the app.
 
@@ -124,7 +128,7 @@ class RuneOptimizerFrame(wx.Frame):
 
         self.Close(True)
 
-    def _show_about(self, event):
+    def _show_about(self, event=None):
         """
         Display an About dialog.
 
@@ -142,7 +146,7 @@ class RuneOptimizerFrame(wx.Frame):
           style=wx.OK | wx.ICON_INFORMATION
         )
 
-    def _update_from_json(self, event = None):
+    def _update_from_json(self, event=None):
         """
         Shows a dialog to update data from a JSON file.
 
@@ -156,11 +160,11 @@ class RuneOptimizerFrame(wx.Frame):
         with DialogUpdateJson(self) as dlg:
             if dlg.ShowModal() == wx.ID_OK:
                 # do something here
-                if (dlg.update_done == True and dlg.update_error == False):
+                if (dlg.update_done is True and dlg.update_error is False):
                     print("Update succesfull!")
                     # TODO: Recalculate status bas message
 
-    def _update_from_swdb(self, event):
+    def _update_from_swdb(self, event=None):
         """
         Updates data from a SWDB instance.
 
@@ -175,7 +179,7 @@ class RuneOptimizerFrame(wx.Frame):
 
         self._show_unimplemented()
 
-    def _update_from_swarfarm(self, event):
+    def _update_from_swarfarm(self, event=None):
         """
         Updates data from Swarfarm.
 
@@ -190,7 +194,7 @@ class RuneOptimizerFrame(wx.Frame):
 
         self._show_unimplemented()
 
-    def _update_from_sqlite(self, event):
+    def _update_from_sqlite(self, event=None):
         """
         Updates data from a Sqlite file.
 

+ 15 - 7
RuneOptimizerGUI/gui/TabList.py → src/gui/runeoptimizer_gui/gui/TabList.py

@@ -15,6 +15,14 @@ You should have received a copy of the GNU General Public License along with
 RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
 
 """
+import os
+import wx
+import wx.grid
+from gui.PanelUnits import PanelUnits
+from gui.PanelTeams import PanelTeams
+from gui.PanelOptimizer import PanelOptimizer
+from gui.PanelResults import PanelResults
+from gui.PanelInfo import PanelInfo
 
 class Tab_List(wx.Listbook):
     """
@@ -41,23 +49,23 @@ class Tab_List(wx.Listbook):
     _panel_optimizer = None
     _panel_results = None
     _panel_info = None
-    
+
     @property
     def panel_units(self):
         return self._panel_units
-    
+
     @property
     def panel_teams(self):
         return self._panel_teams
-    
+
     @property
     def panel_optimizer(self):
         return self._panel_optimizer
-    
+
     @property
     def panel_results(self):
         return self._panel_results
-    
+
     @property
     def panel_info(self):
         return self._panel_inif
@@ -86,12 +94,12 @@ class Tab_List(wx.Listbook):
 
         # Load the icons
         icon_path = \
-          os.path.dirname(os.path.realpath(__file__)) + "/res/icon/"
+          os.path.dirname(os.path.realpath(__file__)) + "/../res/icon/"
         if os.name == 'nt':
             icon_path = os.path.dirname(__file__)
             if icon_path == "":
                 icon_path += "."
-            icon_path += "\\res\\icon\\"
+            icon_path += "\\..\\res\\icon\\"
         il = wx.ImageList(50, 50)
         for ico in [
           "units.png", "teams.png", "optimize.png", "results.png", "info.png"

+ 0 - 0
src/gui/runeoptimizer_gui/gui/__init__.py


+ 0 - 0
RuneOptimizerGUI/res/icon/help.png → src/gui/runeoptimizer_gui/res/icon/help.png


+ 0 - 0
RuneOptimizerGUI/res/icon/info.png → src/gui/runeoptimizer_gui/res/icon/info.png


+ 0 - 0
RuneOptimizerGUI/res/icon/optimize.png → src/gui/runeoptimizer_gui/res/icon/optimize.png


+ 0 - 0
RuneOptimizerGUI/res/icon/progress.gif → src/gui/runeoptimizer_gui/res/icon/progress.gif


+ 0 - 0
RuneOptimizerGUI/res/icon/results.png → src/gui/runeoptimizer_gui/res/icon/results.png


+ 0 - 0
RuneOptimizerGUI/res/icon/teams.png → src/gui/runeoptimizer_gui/res/icon/teams.png


+ 0 - 0
RuneOptimizerGUI/res/icon/units.png → src/gui/runeoptimizer_gui/res/icon/units.png


+ 106 - 0
src/gui/runeoptimizer_gui/runeoptimizer_gui.py

@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+import sqlite3
+import subprocess
+import json
+import os
+import signal
+import pipes
+import appdirs
+from types import SimpleNamespace
+from time import sleep
+import wx
+import wx.grid
+import wx.adv
+import data.data as data
+import values.values as values
+import database.database as database
+from entity.Team import Team
+from entity.Unit import Unit
+from gui.RuneOptimizerFrame import RuneOptimizerFrame
+
+# The path to the RuneOptimizer executable. Different in Windows.
+executable_path = \
+  os.path.dirname(os.path.realpath(__file__)) + '/../RuneOptimizer'
+if os.name == 'nt':
+    executable_path = \
+      os.path.dirname(os.path.realpath(__file__)) + '\\..\\RuneOptimizer.exe'
+
+def reload_units():
+    # Get all the units, sorted by priority desc
+    data.UNITS = {}
+    cursor = database.CONNECTION.execute("""
+      SELECT
+        id,
+        (
+          SELECT sum(priority)
+          FROM teams
+          WHERE id IN (SELECT team FROM units_teams WHERE unit = units.id)
+        ) AS prio
+      FROM units
+      ORDER BY prio DESC;
+    """)
+    rows = cursor.fetchall()
+    for row in rows:
+        data.UNITS[str(row[0])] = Unit(str(row[0]))
+
+def reload_teams():
+    # Get all the teams, sorted by priority desc
+    data.TEAMS = {}
+    cursor = database.CONNECTION.execute(
+      "SELECT id FROM teams ORDER BY priority DESC;"
+    )
+    rows = cursor.fetchall()
+    for row in rows:
+        data.TEAMS[str(row[0])] = Team(str(row[0]))
+
+if __name__ == '__main__':
+    """
+    Main function.
+
+    Conects to the database and shows the initial frame.
+    """
+
+    #Get CLI app version
+    # TODO: Change executable name for windows
+    #if os.name == 'nt':
+    values.APP_INFO["app_version"] = subprocess.check_output(
+      ['runeoptimizer', 'version']
+    )
+
+    # TODO: Test for executable to exist
+    # TODO: Test for database to exist
+
+    # Open the database
+    database.CONNECTION = sqlite3.connect(
+      appdirs.user_data_dir("RuneOptimizer") + "/data.sqlite"
+    )
+
+    reload_units()
+    reload_teams()
+
+    # Start the GUI
+    app = wx.App()
+    frm = RuneOptimizerFrame(
+      parent=None, title='Rune Optimizer', pos=(100, 100), size=(1000, 650)
+    )
+    frm.Show()
+    app.MainLoop()

+ 0 - 0
src/gui/runeoptimizer_gui/values/__init__.py


+ 73 - 0
src/gui/runeoptimizer_gui/values/values.py

@@ -0,0 +1,73 @@
+"""
+This file is part of RuneOptimizer.
+
+RuneOptimizer is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option)
+any later version.
+
+RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+
+APP_INFO = {
+    "name": "Rune Optimizer",
+    "app_version": "UNKNOWN",
+    "gui_version": "0.1-RC1",
+    "author": "Iñigo Valentin",
+    "author_mail": "i@inigovalentin.com",
+    "url": {
+        "Home page": "https://inigovalentin.com/projects/RuneOptimizer/",
+        "Source code": "https://github.com/InigoValentin/RuneOptimizer/"
+    }
+}
+
+RUNE_STATS = {
+    "HP": 1, "HP_P": 2, "ATK": 3, "ATK_P": 4, "DEF": 5, "DEF_P": 6,
+    "SPD": 8, "CRR": 9, "CRD": 10, "RES": 11, "ACC": 12
+}
+
+# Unit stat names, indexed with Com2Us values.
+STAT_NAMES = [
+  "NULL", "HP  ", "HP% ", "ATK ", "ATK%", "DEF ",
+  "DEF%", "NULL", "SPD ", "CRR ", "CRD ", "RES ", "ACC "
+]
+
+# Rune set names, indexed with Com2Us values.
+SET_NAMES = [
+  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "BLADE",    "RAGE",     #  0- 5
+  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "DESPAIR",  "VAMPIRE",  #  6-11
+  "NULL",    "VIOLENT", "NEMESIS",  "WILL",    "SHIELD",   "REVENGE",  # 12-17
+  "DESTROY", "FIGHT",   "DETERMIN", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
+]
+
+# TODO: Work in progress...
+"""
+# Rune set names, indexed with Com2Us values.
+SET_SYMBOLS = [
+  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "ᚬ",    "ᛟ",     #  0- 5
+  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "ᛃ",  "VAMPIRE",  #  6-11
+  "NULL",    "ᛒ", "NEMESIS",  "WILL",    "SHIELD",   "ᛝ",  # 12-17
+  "DESTROY", "FIGHT",   "ᛗ", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
+]
+
+SET_SYMBOLS_UNICODE = [
+  "NULL",    "ENERGY",  "GUARD",    "SWIFT",   "\u16AC",    "\u16DF", #  0- 5
+  "FOCUS",   "ENDURE",  "FATAL",    "NULL",    "\u16C3",  "VAMPIRE",  #  6-11
+  "NULL",    "\u16D2", "NEMESIS",  "WILL",    "SHIELD",   "\u16DD",  # 12-17
+  "DESTROY", "FIGHT",   "\u16D7", "ENHANCE", "ACCURACY", "TOLERANCE" # 18-23
+]
+"""
+
+QUALITY_NAMES = [
+  "NULL",
+  "NORMAL", "MAGIC", "RARE", "HERO", "LEGEND"
+  "NULL", "NULL", "NULL", "NULL", "NULL", "NULL",
+  "A.NORMAL", "A.MAGIC", "A.RARE", "A.HERO",  "A.LEGEND"
+]