Skip to content

Commit 8993558

Browse files
committed
ttktui: add experimental TermTk based text user interface
Launch in a terminal with command line argument `nicotine --tui`
1 parent 24d5293 commit 8993558

File tree

229 files changed

+57498
-16
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

229 files changed

+57498
-16
lines changed

pynicotine/__init__.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ def check_arguments():
6868
"-n", "--headless", action="store_true",
6969
help=_("start the program in headless mode (no GUI)")
7070
)
71+
parser.add_argument(
72+
"-t", "--tui", action="store_true",
73+
help=_("start experimental text user interface (TermTk TUI)")
74+
)
7175
parser.add_argument(
7276
"-v", "--version", action="version", version=f"{__application_name__} {__version__}",
7377
help=_("display version and exit")
@@ -95,7 +99,7 @@ def check_arguments():
9599
core.cli_listen_port = args.port
96100
core.cli_rescanning = args.rescan
97101

98-
return args.headless, args.hidden, args.ci_mode, args.isolated, args.rescan, multi_instance
102+
return args.headless, args.tui, args.hidden, args.ci_mode, args.isolated, args.rescan, multi_instance
99103

100104

101105
def check_python_version():
@@ -187,14 +191,15 @@ def run():
187191
set_up_python()
188192
rename_process(b"nicotine")
189193

190-
headless, hidden, ci_mode, isolated_mode, rescan, multi_instance = check_arguments()
194+
headless, tui, hidden, ci_mode, isolated_mode, rescan, multi_instance = check_arguments()
191195
error = check_python_version()
192196

193197
if error:
194198
print(error)
195199
return 1
196200

197201
core.init_components(
202+
disabled_components={"cli"} if tui else None, # "error_handler",
198203
enabled_components={"signal_handler", "cli", "shares"} if rescan else None,
199204
isolated_mode=isolated_mode
200205
)
@@ -213,8 +218,16 @@ def run():
213218
if rescan:
214219
return rescan_shares()
215220

216-
# Initialize GTK-based GUI
217-
if not headless:
221+
if tui:
222+
# Initialize TTk-based TUI
223+
from pynicotine import ttktui as application
224+
exit_code = application.run(ci_mode, isolated_mode)
225+
226+
if exit_code is not None:
227+
return exit_code
228+
229+
elif not headless:
230+
# Initialize GTK-based GUI
218231
from pynicotine import gtkgui as application
219232
exit_code = application.run(hidden, ci_mode, isolated_mode, multi_instance)
220233

pynicotine/core.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,15 @@ def __init__(self):
5151

5252
self.enabled_components = set()
5353

54-
def init_components(self, enabled_components=None, isolated_mode=False):
54+
def init_components(self, disabled_components=None, enabled_components=None, isolated_mode=False):
5555

5656
# Enable all components by default
57-
if enabled_components is None:
58-
enabled_components = {
59-
"error_handler", "signal_handler", "cli", "portmapper", "network_thread", "shares", "users",
60-
"notifications", "network_filter", "now_playing", "statistics", "port_checker", "update_checker",
61-
"search", "downloads", "uploads", "interests", "userbrowse", "userinfo", "buddies",
62-
"chatrooms", "privatechat", "pluginhandler"
63-
}
64-
65-
self.enabled_components = enabled_components
57+
self.enabled_components = enabled_components = enabled_components or {
58+
"error_handler", "signal_handler", "cli", "portmapper", "network_thread", "shares", "users",
59+
"notifications", "network_filter", "now_playing", "statistics", "port_checker", "update_checker",
60+
"search", "downloads", "uploads", "interests", "userbrowse", "userinfo", "buddies",
61+
"chatrooms", "privatechat", "pluginhandler"
62+
} - (disabled_components or set())
6663

6764
if "error_handler" in enabled_components:
6865
self._init_error_handler()
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .abstractscrollview import *
2+
from .abstractscrollarea import *
3+
from .abstractitemmodel import *
4+
from .abstracttablemodel import *
5+
from .abstract_list_item import *
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2026 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
__all__ =['_TTkAbstractListItem']
24+
25+
from typing import Any
26+
27+
from TermTk.TTkCore.signal import pyTTkSignal
28+
from TermTk.TTkCore.string import TTkString
29+
30+
class _TTkAbstractListItem():
31+
'''_TTkAbstractListItem:
32+
33+
.. note::
34+
This is the future abstract base interface for list items.
35+
36+
This class defines the minimal interface that list items must implement.
37+
In a future version, :py:class:`TTkAbstractListItem` will be converted to
38+
inherit from this abstract interface, requiring custom implementations to
39+
provide these methods if they don't use the default :py:class:`TTkListItem`.
40+
41+
Currently used as an internal marker for the planned architecture migration.
42+
'''
43+
__slots__ = ('dataChanged')
44+
45+
def __init__(self):
46+
self.dataChanged = pyTTkSignal()
47+
48+
def data(self) -> Any:
49+
'''
50+
Returns the user data associated with this item.
51+
52+
:return: The custom data object
53+
:rtype: Any
54+
'''
55+
raise NotImplementedError
56+
57+
def toTTkString(self) -> TTkString:
58+
return TTkString(str(self.data()))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# MIT License
2+
#
3+
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
__all__ = ['TTkAbstractItemModel']
24+
25+
from TermTk.TTkCore.signal import pyTTkSignal
26+
27+
class TTkAbstractItemModel():
28+
'''TTkAbstractItemModel'''
29+
__slots__ = (
30+
# Signals
31+
'dataChanged'
32+
)
33+
def __init__(self) -> None:
34+
self.dataChanged = pyTTkSignal()

0 commit comments

Comments
 (0)