12 files changed
+129
-75
No files matched your search
@@ -51,6 +51,7 @@ Many thanks to these additional authors:
|
|||||||
* Carton He for contributions to sgf parsing and handling.
|
* Carton He for contributions to sgf parsing and handling.
|
||||||
* "blamarche" for adding the board coordinates toggle.
|
* "blamarche" for adding the board coordinates toggle.
|
||||||
* "pdeblanc" for adding the ancient chinese scoring option.
|
* "pdeblanc" for adding the ancient chinese scoring option.
|
||||||
|
* "LiamHz" for adding the undo to main branch keyboard shortcut.
|
||||||
|
|
||||||
## Translators
|
## Translators
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ In addition to shortcuts mentioned above and those shown in the main menu:
|
|||||||
* **[Ctrl-delete]** Delete current node.
|
* **[Ctrl-delete]** Delete current node.
|
||||||
* **[c]** Collapse/Uncollapse the branch from the current node to the previous branching point.
|
* **[c]** Collapse/Uncollapse the branch from the current node to the previous branching point.
|
||||||
* **[b]** Go back to the previous branching point.
|
* **[b]** Go back to the previous branching point.
|
||||||
|
* **[Shift-b]** Go back the the main branch.
|
||||||
* **[n]** As in clicking the forward red arrow, go to one move before the next mistake (orange or worse) by a human player.
|
* **[n]** As in clicking the forward red arrow, go to one move before the next mistake (orange or worse) by a human player.
|
||||||
* **[Shift-n]** As in clicking the backward red arrow, go to one move before the previous mistake.
|
* **[Shift-n]** As in clicking the backward red arrow, go to one move before the previous mistake.
|
||||||
* **[scroll up]**: Undo move. Only works when hovering the cursor over the board.
|
* **[scroll up]**: Undo move. Only works when hovering the cursor over the board.
|
||||||
|
|||||||
@@ -12,11 +12,18 @@ Version 1.7 brings basic support for themes.
|
|||||||
```
|
```
|
||||||
* All resources (including icons which can not be renamed for now) will be looked up in `<home dir>/.katrain` first, so files with identical names there can be used to override sounds and images.
|
* All resources (including icons which can not be renamed for now) will be looked up in `<home dir>/.katrain` first, so files with identical names there can be used to override sounds and images.
|
||||||
|
|
||||||
## Available themes
|
## Installation
|
||||||
|
|
||||||
* See [here](https://github.com/sanderland/katrain/blob/master/themes/) for available themes.
|
|
||||||
* To install a theme, simply unzip the theme.zip to your .katrain folder.
|
* To install a theme, simply unzip the theme.zip to your .katrain folder.
|
||||||
* On windows you can find it in C:\Users\you\.katrain and on linux in ~/.katrain.
|
* On windows you can find it in C:\Users\you\.katrain and on linux in ~/.katrain.
|
||||||
* When in doubt, the general settings dialog will also show the location.
|
* When in doubt, the general settings dialog will also show the location.
|
||||||
* To uninstall a theme, remove theme.json and all relevant images from that folder.
|
* To uninstall a theme, remove theme.json and all relevant images from that folder.
|
||||||
|
|
||||||
|
## Available themes
|
||||||
|
|
||||||
|
### Alternate board/stones theme by "koast"
|
||||||
|
|
||||||
|
[Download](https://github.com/sanderland/katrain/blob/master/themes/koast-theme.zip)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
@@ -610,6 +610,8 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
self.controls.set_status(i18n._("Copied SGF to clipboard."), STATUS_INFO)
|
self.controls.set_status(i18n._("Copied SGF to clipboard."), STATUS_INFO)
|
||||||
elif keycode[1] == "v" and ctrl_pressed:
|
elif keycode[1] == "v" and ctrl_pressed:
|
||||||
self.load_sgf_from_clipboard()
|
self.load_sgf_from_clipboard()
|
||||||
|
elif keycode[1] == "b" and shift_pressed:
|
||||||
|
self("undo", "main-branch")
|
||||||
elif keycode[1] in shortcuts.keys() and not ctrl_pressed:
|
elif keycode[1] in shortcuts.keys() and not ctrl_pressed:
|
||||||
shortcut = shortcuts[keycode[1]]
|
shortcut = shortcuts[keycode[1]]
|
||||||
if isinstance(shortcut, Widget):
|
if isinstance(shortcut, Widget):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
PROGRAM_NAME = "KaTrain"
|
PROGRAM_NAME = "KaTrain"
|
||||||
VERSION = "1.7.1"
|
VERSION = "1.7.2"
|
||||||
HOMEPAGE = "https://github.com/sanderland/katrain"
|
HOMEPAGE = "https://github.com/sanderland/katrain"
|
||||||
CONFIG_MIN_VERSION = "1.7.0" # keep config files from this version
|
CONFIG_MIN_VERSION = "1.7.0" # keep config files from this version
|
||||||
ANALYSIS_FORMAT_VERSION = "1.0"
|
ANALYSIS_FORMAT_VERSION = "1.0"
|
||||||
|
|||||||
+46
-41
@@ -52,7 +52,7 @@ class KataGoEngine:
|
|||||||
self.write_stdin_thread = None
|
self.write_stdin_thread = None
|
||||||
self.shell = False
|
self.shell = False
|
||||||
self.write_queue = queue.Queue()
|
self.write_queue = queue.Queue()
|
||||||
|
self.thread_lock = threading.Lock()
|
||||||
exe = config.get("katago", "").strip()
|
exe = config.get("katago", "").strip()
|
||||||
if config.get("altcommand", ""):
|
if config.get("altcommand", ""):
|
||||||
self.command = config["altcommand"]
|
self.command = config["altcommand"]
|
||||||
@@ -92,39 +92,43 @@ class KataGoEngine:
|
|||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.write_queue = queue.Queue()
|
with self.thread_lock:
|
||||||
try:
|
self.write_queue = queue.Queue()
|
||||||
self.katrain.log(f"Starting KataGo with {self.command}", OUTPUT_DEBUG)
|
try:
|
||||||
startupinfo = None
|
self.katrain.log(f"Starting KataGo with {self.command}", OUTPUT_DEBUG)
|
||||||
if hasattr(subprocess, "STARTUPINFO"):
|
startupinfo = None
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
if hasattr(subprocess, "STARTUPINFO"):
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # stop command box popups on win/pyinstaller
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
self.katago_process = subprocess.Popen(
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # stop command box popups on win/pyinstaller
|
||||||
self.command,
|
self.katago_process = subprocess.Popen(
|
||||||
startupinfo=startupinfo,
|
self.command,
|
||||||
stdin=subprocess.PIPE,
|
startupinfo=startupinfo,
|
||||||
stdout=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
shell=self.shell,
|
stderr=subprocess.PIPE,
|
||||||
)
|
shell=self.shell,
|
||||||
except (FileNotFoundError, PermissionError, OSError) as e:
|
)
|
||||||
self.katrain.log(
|
except (FileNotFoundError, PermissionError, OSError) as e:
|
||||||
i18n._("Starting Kata failed").format(command=self.command, error=e),
|
self.katrain.log(
|
||||||
OUTPUT_ERROR,
|
i18n._("Starting Kata failed").format(command=self.command, error=e),
|
||||||
)
|
OUTPUT_ERROR,
|
||||||
return # don't start
|
)
|
||||||
self.analysis_thread = threading.Thread(target=self._analysis_read_thread, daemon=True)
|
return # don't start
|
||||||
self.stderr_thread = threading.Thread(target=self._read_stderr_thread, daemon=True)
|
self.analysis_thread = threading.Thread(target=self._analysis_read_thread, daemon=True)
|
||||||
self.write_stdin_thread = threading.Thread(target=self._write_stdin_thread, daemon=True)
|
self.stderr_thread = threading.Thread(target=self._read_stderr_thread, daemon=True)
|
||||||
self.analysis_thread.start()
|
self.write_stdin_thread = threading.Thread(target=self._write_stdin_thread, daemon=True)
|
||||||
self.stderr_thread.start()
|
self.analysis_thread.start()
|
||||||
self.write_stdin_thread.start()
|
self.stderr_thread.start()
|
||||||
|
self.write_stdin_thread.start()
|
||||||
|
|
||||||
def on_new_game(self):
|
def on_new_game(self):
|
||||||
self.base_priority += 1
|
self.base_priority += 1
|
||||||
for query_id in list(self.queries.keys()):
|
if not self.is_idle():
|
||||||
self.terminate_query(query_id)
|
with self.thread_lock:
|
||||||
self.queries = {}
|
for query_id in list(self.queries.keys()):
|
||||||
|
self.terminate_query(query_id)
|
||||||
|
self.queries = {}
|
||||||
|
self.write_queue = queue.Queue()
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
self.queries = {}
|
self.queries = {}
|
||||||
@@ -248,16 +252,17 @@ class KataGoEngine:
|
|||||||
query, callback, error_callback, next_move = self.write_queue.get(block=True, timeout=0.1)
|
query, callback, error_callback, next_move = self.write_queue.get(block=True, timeout=0.1)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
continue
|
continue
|
||||||
if "id" not in query:
|
with self.thread_lock:
|
||||||
self.query_counter += 1
|
if "id" not in query:
|
||||||
query["id"] = f"QUERY:{str(self.query_counter)}"
|
self.query_counter += 1
|
||||||
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move)
|
query["id"] = f"QUERY:{str(self.query_counter)}"
|
||||||
self.katrain.log(f"Sending query {query['id']}: {json.dumps(query)}", OUTPUT_DEBUG)
|
self.queries[query["id"]] = (callback, error_callback, time.time(), next_move)
|
||||||
try:
|
self.katrain.log(f"Sending query {query['id']}: {json.dumps(query)}", OUTPUT_DEBUG)
|
||||||
self.katago_process.stdin.write((json.dumps(query) + "\n").encode())
|
try:
|
||||||
self.katago_process.stdin.flush()
|
self.katago_process.stdin.write((json.dumps(query) + "\n").encode())
|
||||||
except OSError as e:
|
self.katago_process.stdin.flush()
|
||||||
self.check_alive(os_error=str(e), exception_if_dead=False)
|
except OSError as e:
|
||||||
|
self.check_alive(os_error=str(e), exception_if_dead=False)
|
||||||
|
|
||||||
def send_query(self, query, callback, error_callback, next_move=None):
|
def send_query(self, query, callback, error_callback, next_move=None):
|
||||||
self.write_queue.put((query, callback, error_callback, next_move))
|
self.write_queue.put((query, callback, error_callback, next_move))
|
||||||
|
|||||||
+14
-1
@@ -279,9 +279,14 @@ class Game:
|
|||||||
self._calculate_groups()
|
self._calculate_groups()
|
||||||
return
|
return
|
||||||
break_on_branch = False
|
break_on_branch = False
|
||||||
|
break_on_main_branch = False
|
||||||
|
last_branching_node = cn
|
||||||
if n_times == "branch":
|
if n_times == "branch":
|
||||||
n_times = 9999
|
n_times = 9999
|
||||||
break_on_branch = True
|
break_on_branch = True
|
||||||
|
elif n_times == "main-branch":
|
||||||
|
n_times = 9999
|
||||||
|
break_on_main_branch = True
|
||||||
for move in range(n_times):
|
for move in range(n_times):
|
||||||
if (
|
if (
|
||||||
stop_on_mistake is not None
|
stop_on_mistake is not None
|
||||||
@@ -291,13 +296,21 @@ class Game:
|
|||||||
):
|
):
|
||||||
self.set_current_node(cn.parent)
|
self.set_current_node(cn.parent)
|
||||||
return
|
return
|
||||||
|
previous_cn = cn
|
||||||
if cn.shortcut_from:
|
if cn.shortcut_from:
|
||||||
cn = cn.shortcut_from
|
cn = cn.shortcut_from
|
||||||
elif not cn.is_root:
|
elif not cn.is_root:
|
||||||
cn = cn.parent
|
cn = cn.parent
|
||||||
|
else:
|
||||||
|
break # root
|
||||||
if break_on_branch and len(cn.children) > 1:
|
if break_on_branch and len(cn.children) > 1:
|
||||||
break
|
break
|
||||||
self.set_current_node(cn)
|
elif break_on_main_branch and cn.ordered_children[0] != previous_cn: # implies > 1 child
|
||||||
|
last_branching_node = cn
|
||||||
|
if break_on_main_branch:
|
||||||
|
cn = last_branching_node
|
||||||
|
if cn is not self.current_node:
|
||||||
|
self.set_current_node(cn)
|
||||||
|
|
||||||
def redo(self, n_times=1, stop_on_mistake=None):
|
def redo(self, n_times=1, stop_on_mistake=None):
|
||||||
if self.insert_mode:
|
if self.insert_mode:
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ class SGFNode:
|
|||||||
def initial_player(self): # player for first node
|
def initial_player(self): # player for first node
|
||||||
root = self.root
|
root = self.root
|
||||||
if "PL" in root.properties: # explicit
|
if "PL" in root.properties: # explicit
|
||||||
return "B" if self.get_property("PL").upper().strip() == "B" else "W"
|
return "B" if self.root.get_property("PL").upper().strip() == "B" else "W"
|
||||||
elif root.children: # child exist, use it if not placement
|
elif root.children: # child exist, use it if not placement
|
||||||
for child in root.children:
|
for child in root.children:
|
||||||
for color in "BW":
|
for color in "BW":
|
||||||
|
|||||||
+49
-24
@@ -3,6 +3,8 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import stat
|
import stat
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from typing import Any, Dict, List, Tuple, Union
|
from typing import Any, Dict, List, Tuple, Union
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
@@ -426,7 +428,10 @@ class ConfigAIPopup(QuickConfigGui):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigPopup(QuickConfigGui):
|
class ConfigPopup(QuickConfigGui):
|
||||||
MODEL_ENDPOINTS = {"Latest distributed model": "https://katagotraining.org/api/networks/newest_training/"}
|
MODEL_ENDPOINTS = {
|
||||||
|
"Latest distributed model": "https://katagotraining.org/api/networks/newest_training/",
|
||||||
|
"Strongest distributed model": "https://katagotraining.org/api/networks/get_strongest/",
|
||||||
|
}
|
||||||
MODELS = {
|
MODELS = {
|
||||||
"20 block model": "https://github.com/lightvector/KataGo/releases/download/v1.4.5/g170e-b20c256x2-s5303129600-d1228401921.bin.gz",
|
"20 block model": "https://github.com/lightvector/KataGo/releases/download/v1.4.5/g170e-b20c256x2-s5303129600-d1228401921.bin.gz",
|
||||||
"30 block model": "https://github.com/lightvector/KataGo/releases/download/v1.4.5/g170-b30c320x2-s4824661760-d1229536699.bin.gz",
|
"30 block model": "https://github.com/lightvector/KataGo/releases/download/v1.4.5/g170-b30c320x2-s4824661760-d1229536699.bin.gz",
|
||||||
@@ -461,6 +466,7 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
self.paths = [self.katrain.config("engine/model"), "katrain/models", DATA_FOLDER]
|
self.paths = [self.katrain.config("engine/model"), "katrain/models", DATA_FOLDER]
|
||||||
self.katago_paths = [self.katrain.config("engine/katago"), DATA_FOLDER]
|
self.katago_paths = [self.katrain.config("engine/katago"), DATA_FOLDER]
|
||||||
Clock.schedule_once(self.check_katas)
|
Clock.schedule_once(self.check_katas)
|
||||||
|
self.last_clicked_download_models = 0
|
||||||
MDApp.get_running_app().bind(language=self.check_models)
|
MDApp.get_running_app().bind(language=self.check_models)
|
||||||
MDApp.get_running_app().bind(language=self.check_katas)
|
MDApp.get_running_app().bind(language=self.check_katas)
|
||||||
|
|
||||||
@@ -506,7 +512,11 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
self.paths.append(path) # persistent on paths with models found
|
self.paths.append(path) # persistent on paths with models found
|
||||||
model_files += files
|
model_files += files
|
||||||
|
|
||||||
model_files = sorted([(find_description(path), path) for path in model_files])
|
# no description to bottom
|
||||||
|
model_files = sorted(
|
||||||
|
[(find_description(path), path) for path in model_files],
|
||||||
|
key=lambda descpath: "Z" * 10 + path if descpath[0] == descpath[1] else descpath[0],
|
||||||
|
)
|
||||||
models_available_msg = i18n._("models available").format(num=len(model_files))
|
models_available_msg = i18n._("models available").format(num=len(model_files))
|
||||||
self.model_files.values = [models_available_msg] + [desc for desc, path in model_files]
|
self.model_files.values = [models_available_msg] + [desc for desc, path in model_files]
|
||||||
self.model_files.value_keys = [""] + [path for desc, path in model_files]
|
self.model_files.value_keys = [""] + [path for desc, path in model_files]
|
||||||
@@ -557,18 +567,23 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
self.katago_files.text = katas_available_msg
|
self.katago_files.text = katas_available_msg
|
||||||
|
|
||||||
def download_models(self, *_largs):
|
def download_models(self, *_largs):
|
||||||
|
if time.time() - self.last_clicked_download_models > 5:
|
||||||
|
self.last_clicked_download_models = time.time()
|
||||||
|
threading.Thread(target=self._download_models, daemon=True).start()
|
||||||
|
|
||||||
|
def _download_models(self):
|
||||||
def download_complete(req, tmp_path, path, model):
|
def download_complete(req, tmp_path, path, model):
|
||||||
try:
|
try:
|
||||||
os.rename(tmp_path, path)
|
os.rename(tmp_path, path)
|
||||||
self.katrain.log(f"Download of {model} model complete -> {path}", OUTPUT_INFO)
|
self.katrain.log(f"Download of {model} complete -> {path}", OUTPUT_INFO)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.katrain.log(f"Download of {model} model complete, but could not move file: {e}", OUTPUT_ERROR)
|
self.katrain.log(f"Download of {model} complete, but could not move file: {e}", OUTPUT_ERROR)
|
||||||
self.check_models()
|
self.check_models()
|
||||||
|
|
||||||
for c in self.download_progress_box.children:
|
for c in self.download_progress_box.children:
|
||||||
if isinstance(c, ProgressLoader) and c.request:
|
if isinstance(c, ProgressLoader) and c.request:
|
||||||
c.request.cancel()
|
c.request.cancel()
|
||||||
self.download_progress_box.clear_widgets()
|
Clock.schedule_once(lambda _dt: self.download_progress_box.clear_widgets(), -1) # main thread
|
||||||
downloading = False
|
downloading = False
|
||||||
|
|
||||||
dist_models = {k: v for k, v in self.katrain.config("dist_models", {}).items() if k in self.MODEL_ENDPOINTS}
|
dist_models = {k: v for k, v in self.katrain.config("dist_models", {}).items() if k in self.MODEL_ENDPOINTS}
|
||||||
@@ -577,6 +592,10 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
try:
|
try:
|
||||||
http = urllib3.PoolManager()
|
http = urllib3.PoolManager()
|
||||||
response = http.request("GET", url)
|
response = http.request("GET", url)
|
||||||
|
if response.status != 200:
|
||||||
|
raise Exception(
|
||||||
|
f"Request to {url} returned code {response.status} != 200: {response.data.decode()}"
|
||||||
|
)
|
||||||
dist_models[name] = json.loads(response.data.decode("utf-8"))["model_file"]
|
dist_models[name] = json.loads(response.data.decode("utf-8"))["model_file"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.katrain.log(f"Failed to retrieve info for model: {e}", OUTPUT_INFO)
|
self.katrain.log(f"Failed to retrieve info for model: {e}", OUTPUT_INFO)
|
||||||
@@ -590,27 +609,33 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
savepath = os.path.expanduser(os.path.join(DATA_FOLDER, filename))
|
savepath = os.path.expanduser(os.path.join(DATA_FOLDER, filename))
|
||||||
savepath_tmp = savepath + ".part"
|
savepath_tmp = savepath + ".part"
|
||||||
self.katrain.log(f"Downloading {name} model from {url} to {savepath_tmp}", OUTPUT_INFO)
|
self.katrain.log(f"Downloading {name} model from {url} to {savepath_tmp}", OUTPUT_INFO)
|
||||||
progress = ProgressLoader(
|
Clock.schedule_once(
|
||||||
download_url=url,
|
lambda _dt, _savepath=savepath, _savepath_tmp=savepath_tmp, _url=url, _name=name: ProgressLoader(
|
||||||
path_to_file=savepath_tmp,
|
self.download_progress_box,
|
||||||
downloading_text=f"Downloading {name} model: " + "{}",
|
download_url=_url,
|
||||||
label_downloading_text=f"Starting download for {name} model",
|
path_to_file=_savepath_tmp,
|
||||||
download_complete=lambda req, tmp=savepath_tmp, path=savepath, model=name: download_complete(
|
downloading_text=f"Downloading {_name}: " + "{}",
|
||||||
req, tmp, path, model
|
label_downloading_text=f"Starting download for {_name}",
|
||||||
|
download_complete=lambda req, tmp=_savepath_tmp, path=_savepath, model=_name: download_complete(
|
||||||
|
req, tmp, path, model
|
||||||
|
),
|
||||||
|
download_redirected=lambda req, mname=_name: self.katrain.log(
|
||||||
|
f"Download {mname} redirected {req.resp_headers}", OUTPUT_DEBUG
|
||||||
|
),
|
||||||
|
download_error=lambda req, error, mname=_name: self.katrain.log(
|
||||||
|
f"Download of {mname} failed or cancelled ({error})", OUTPUT_ERROR
|
||||||
|
),
|
||||||
),
|
),
|
||||||
download_redirected=lambda req, mname=name: self.katrain.log(
|
0,
|
||||||
f"Download {mname} redirected {req.resp_headers}", OUTPUT_DEBUG
|
) # main thread
|
||||||
),
|
|
||||||
download_error=lambda req, error, mname=name: self.katrain.log(
|
|
||||||
f"Download of {mname} failed or cancelled ({error})", OUTPUT_ERROR
|
|
||||||
),
|
|
||||||
)
|
|
||||||
progress.start(self.download_progress_box)
|
|
||||||
downloading = True
|
downloading = True
|
||||||
if not downloading:
|
if not downloading:
|
||||||
self.download_progress_box.add_widget(
|
Clock.schedule_once(
|
||||||
Label(text=i18n._("All models downloaded"), font_name=i18n.font_name, text_size=(None, dp(50)))
|
lambda _dt: self.download_progress_box.add_widget(
|
||||||
)
|
Label(text=i18n._("All models downloaded"), font_name=i18n.font_name, text_size=(None, dp(50)))
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
) # main thread
|
||||||
|
|
||||||
def download_katas(self, *_largs):
|
def download_katas(self, *_largs):
|
||||||
def unzipped_name(zipfile):
|
def unzipped_name(zipfile):
|
||||||
@@ -641,7 +666,7 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
os.remove(tmp_path)
|
os.remove(tmp_path)
|
||||||
else:
|
else:
|
||||||
os.rename(tmp_path, path)
|
os.rename(tmp_path, path)
|
||||||
self.katrain.log(f"Download of katago binary {binary} model complete -> {path}", OUTPUT_INFO)
|
self.katrain.log(f"Download of katago binary {binary} complete -> {path}", OUTPUT_INFO)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.katrain.log(
|
self.katrain.log(
|
||||||
f"Download of katago binary {binary} complete, but could not move file: {e}", OUTPUT_ERROR
|
f"Download of katago binary {binary} complete, but could not move file: {e}", OUTPUT_ERROR
|
||||||
|
|||||||
@@ -59,13 +59,13 @@ class ProgressLoader(BoxLayout):
|
|||||||
request = ObjectProperty()
|
request = ObjectProperty()
|
||||||
"""UrlRequest object."""
|
"""UrlRequest object."""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, root_instance, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.root_instance = None
|
|
||||||
self.request = None
|
|
||||||
|
|
||||||
def start(self, root_instance):
|
|
||||||
self.root_instance = root_instance
|
self.root_instance = root_instance
|
||||||
|
self.request = None
|
||||||
|
Clock.schedule_once(self.start, 0)
|
||||||
|
|
||||||
|
def start(self, _dt):
|
||||||
self.root_instance.add_widget(self)
|
self.root_instance.add_widget(self)
|
||||||
self.request_download_file(self.download_url, self.path_to_file)
|
self.request_download_file(self.download_url, self.path_to_file)
|
||||||
Clock.schedule_once(self.animation_show, 1)
|
Clock.schedule_once(self.animation_show, 1)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 904 KiB |
Reference in new issue
Block a user