Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib
diff options
context:
space:
mode:
authorWerner Llácer <wllacer@gmail.com>2022-02-28 15:02:39 +0100
committerGitHub <noreply@github.com>2022-02-28 15:02:39 +0100
commitf06aabb4d4a50d3a8ab7c07bef8390f72b091293 (patch)
tree7b079611baeb58bdf0feb9b78a009ef0ee20623e /archinstall/lib
parent391699497d0c15e7eb6a63a1369209f42f311b2c (diff)
enhacements to the menu infraestructure (#978)
* Correct definition of btrfs standard layout * Solve issue #936 * make ask_for_a_timezone as synonym to ask_timezone * Some refining in GeneralMenu secret is now a general function * Revert "Some refining in GeneralMenu" This reverts commit e6e131cb19795e0ddc169e897ae4df57a1c7f9fb. * Activate load of preset values in GeneralMenu Changed all select_functions definitions to the need of passing the preset value Corrected problems at ask_to_configure_network, and management of preset values added * minor glitches in menu processing, plus flake8 complains * Changes to ask_to_configure_network following @svartkanin code * select_language adapted to preset value. changes to the infraestructure to solve bugs * functions adapted for preset values * select_mirror_regions * select_locale_lang * select_locale_enc * ask_for_swap * Updated to preset values * ask_for_bootloader Won't use it * set_root_password() * Updated to preset values * ask_for_audio_selection * select_kernel * ask_for_a_timezone * Updated to use preset values * select_ntp * ask_ntp * ask_for_swap flake8 complains * Adapted to preset values * ask_additional_packages_to_install (from svartkanin) * ask_to_configure_network (adapted from svartkanin version) * Updated to preset values * ask_hostname * select_additional_repositories * bug in nic conversion _select_harddrives adapted to preset_menu
Diffstat (limited to 'archinstall/lib')
-rw-r--r--archinstall/lib/menu/menu.py29
-rw-r--r--archinstall/lib/menu/selection_menu.py66
-rw-r--r--archinstall/lib/user_interaction.py109
3 files changed, 134 insertions, 70 deletions
diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py
index ee4b87e3..962dcc4d 100644
--- a/archinstall/lib/menu/menu.py
+++ b/archinstall/lib/menu/menu.py
@@ -84,8 +84,6 @@ class Menu(TerminalMenu):
self.skip = skip
self.default_option = default_option
self.multi = multi
- self.preselection(preset_values,cursor_index)
-
menu_title = f'\n{title}\n\n'
if skip:
@@ -97,6 +95,7 @@ class Menu(TerminalMenu):
default = f'{default_option} (default)'
self.menu_options = [default] + [o for o in self.menu_options if default_option != o]
+ self.preselection(preset_values,cursor_index)
cursor = "> "
main_menu_cursor_style = ("fg_cyan", "bold")
main_menu_style = ("bg_blue", "fg_gray")
@@ -154,22 +153,28 @@ class Menu(TerminalMenu):
def preselection(self,preset_values :list = [],cursor_index :int = None):
def from_preset_to_cursor():
if preset_values:
- if isinstance(preset_values,str):
- self.cursor_index = self.menu_options.index(preset_values)
- else: # should return an error, but this is smoother
- self.cursor_index = self.menu_options.index(preset_values[0])
+ # if the value is not extant return 0 as cursor index
+ try:
+ if isinstance(preset_values,str):
+ self.cursor_index = self.menu_options.index(self.preset_values)
+ else: # should return an error, but this is smoother
+ self.cursor_index = self.menu_options.index(self.preset_values[0])
+ except ValueError:
+ self.cursor_index = 0
- self.preset_values = preset_values
self.cursor_index = cursor_index
- if preset_values and cursor_index is None:
- from_preset_to_cursor()
- if preset_values and not self.multi: # Not supported by the infraestructure
+ if not preset_values:
self.preset_values = None
- from_preset_to_cursor()
+ return
- if self.default_option and self.multi:
+ self.preset_values = preset_values
+ if self.default_option:
if isinstance(preset_values,str) and self.default_option == preset_values:
self.preset_values = f"{preset_values} (default)"
elif isinstance(preset_values,(list,tuple)) and self.default_option in preset_values:
idx = preset_values.index(self.default_option)
self.preset_values[idx] = f"{preset_values[idx]} (default)"
+ if cursor_index is None or not self.multi:
+ from_preset_to_cursor()
+ if not self.multi: # Not supported by the infraestructure
+ self.preset_values = None
diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py
index 97dfd2a7..a82e8a70 100644
--- a/archinstall/lib/menu/selection_menu.py
+++ b/archinstall/lib/menu/selection_menu.py
@@ -252,6 +252,7 @@ class GeneralMenu:
# we synch all the options just in case
for item in self.list_options():
self.synch(item)
+ self.post_callback # as all the values can vary i have to exec this callback
cursor_pos = None
while True:
# Before continuing, set the preferred keyboard layout/language in the current terminal.
@@ -278,7 +279,7 @@ class GeneralMenu:
Returns true if the menu shall continue, False if it has ended
"""
# find the selected option in our option list
- option = [[k, v] for k, v in self._menu_options.items() if v.text.strip() == selection]
+ option = [[k, v] for k, v in self._menu_options.items() if v.text.strip() == selection.strip()]
if len(option) != 1:
raise ValueError(f'Selection not found: {selection}')
selector_name = option[0][0]
@@ -303,8 +304,8 @@ class GeneralMenu:
result = None
if selector.func:
- # TODO insert code to allow selector functions with preset value
- result = selector.func()
+ presel_val = self.option(selector_name).get_selection()
+ result = selector.func(presel_val)
self._menu_options[selector_name].set_current_selection(result)
self._data_store[selector_name] = result
exec_ret_val = selector.exec_func(selector_name,result) if selector.exec_func else False
@@ -412,32 +413,33 @@ class GlobalMenu(GeneralMenu):
super().__init__(data_store=data_store, auto_cursor=True)
def _setup_selection_menu_options(self):
+ # archinstall.Language will not use preset values
self._menu_options['archinstall-language'] = \
Selector(
_('Select Archinstall language'),
- lambda: self._select_archinstall_language('English'),
+ lambda x: self._select_archinstall_language('English'),
default='English',
enabled=True)
self._menu_options['keyboard-layout'] = \
- Selector(_('Select keyboard layout'), lambda: select_language('us'), default='us')
+ Selector(_('Select keyboard layout'), lambda preset: select_language('us',preset), default='us')
self._menu_options['mirror-region'] = \
Selector(
_('Select mirror region'),
- lambda: select_mirror_regions(),
+ select_mirror_regions,
display_func=lambda x: list(x.keys()) if x else '[]',
default={})
self._menu_options['sys-language'] = \
- Selector(_('Select locale language'), lambda: select_locale_lang('en_US'), default='en_US')
+ Selector(_('Select locale language'), lambda preset: select_locale_lang('en_US',preset), default='en_US')
self._menu_options['sys-encoding'] = \
- Selector(_('Select locale encoding'), lambda: select_locale_enc('utf-8'), default='utf-8')
+ Selector(_('Select locale encoding'), lambda preset: select_locale_enc('utf-8',preset), default='utf-8')
self._menu_options['harddrives'] = \
Selector(
_('Select harddrives'),
- lambda: self._select_harddrives())
+ self._select_harddrives)
self._menu_options['disk_layouts'] = \
Selector(
_('Select disk layout'),
- lambda: select_disk_layout(
+ lambda x: select_disk_layout(
storage['arguments'].get('harddrives', []),
storage['arguments'].get('advanced', False)
),
@@ -445,80 +447,82 @@ class GlobalMenu(GeneralMenu):
self._menu_options['!encryption-password'] = \
Selector(
_('Set encryption password'),
- lambda: self._select_encrypted_password(),
+ lambda x: self._select_encrypted_password(),
display_func=lambda x: secret(x) if x else 'None',
dependencies=['harddrives'])
self._menu_options['swap'] = \
Selector(
_('Use swap'),
- lambda: ask_for_swap(),
+ lambda preset: ask_for_swap(preset),
default=True)
self._menu_options['bootloader'] = \
Selector(
_('Select bootloader'),
- lambda: ask_for_bootloader(storage['arguments'].get('advanced', False)),
+ lambda preset: ask_for_bootloader(storage['arguments'].get('advanced', False),preset),
default="systemd-bootctl" if has_uefi() else "grub-install")
self._menu_options['hostname'] = \
Selector(
_('Specify hostname'),
- lambda: ask_hostname(),
+ ask_hostname,
default='archlinux')
+ # root password won't have preset value
self._menu_options['!root-password'] = \
Selector(
_('Set root password'),
- lambda: self._set_root_password(),
+ lambda preset:self._set_root_password(),
display_func=lambda x: secret(x) if x else 'None')
self._menu_options['!superusers'] = \
Selector(
_('Specify superuser account'),
- lambda: self._create_superuser_account(),
+ lambda x: self._create_superuser_account(),
dependencies_not=['!root-password'],
display_func=lambda x: list(x.keys()) if x else '')
self._menu_options['!users'] = \
Selector(
_('Specify user account'),
- lambda: self._create_user_account(),
+ lambda x: self._create_user_account(),
default={},
display_func=lambda x: list(x.keys()) if x else '[]')
self._menu_options['profile'] = \
Selector(
_('Specify profile'),
- lambda: self._select_profile(),
+ lambda x: self._select_profile(),
display_func=lambda x: x if x else 'None')
self._menu_options['audio'] = \
Selector(
_('Select audio'),
- lambda: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None))))
+ lambda preset: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)),preset))
self._menu_options['kernels'] = \
Selector(
_('Select kernels'),
- lambda: select_kernel(),
+ lambda preset: select_kernel(preset),
default=['linux'])
self._menu_options['packages'] = \
Selector(
_('Additional packages to install'),
- lambda: ask_additional_packages_to_install(storage['arguments'].get('packages', None)),
+ # lambda x: ask_additional_packages_to_install(storage['arguments'].get('packages', None)),
+ ask_additional_packages_to_install,
default=[])
self._menu_options['additional-repositories'] = \
Selector(
_('Additional repositories to enable'),
- lambda: select_additional_repositories(),
+ select_additional_repositories,
default=[])
self._menu_options['nic'] = \
Selector(
_('Configure network'),
- lambda: ask_to_configure_network(),
+ ask_to_configure_network,
display_func=lambda x: x if x else _('Not configured, unavailable unless setup manually'),
default={})
self._menu_options['timezone'] = \
Selector(
_('Select timezone'),
- lambda: ask_for_a_timezone(),
+ lambda preset: ask_for_a_timezone(preset),
default='UTC')
self._menu_options['ntp'] = \
Selector(
_('Set automatic time sync (NTP)'),
- lambda: self._select_ntp(),
+ lambda preset: self._select_ntp(preset),
default=True)
self._menu_options['install'] = \
Selector(
@@ -591,20 +595,20 @@ class GlobalMenu(GeneralMenu):
else:
return None
- def _select_ntp(self) -> bool:
- ntp = ask_ntp()
+ def _select_ntp(self, preset :bool = True) -> bool:
+ ntp = ask_ntp(preset)
value = str(ntp).lower()
SysCommand(f'timedatectl set-ntp {value}')
return ntp
- def _select_harddrives(self):
- old_haddrives = storage['arguments'].get('harddrives', [])
- harddrives = select_harddrives()
+ def _select_harddrives(self, old_harddrives : list) -> list:
+ # old_haddrives = storage['arguments'].get('harddrives', [])
+ harddrives = select_harddrives(old_harddrives)
# in case the harddrives got changed we have to reset the disk layout as well
- if old_haddrives != harddrives:
+ if old_harddrives != harddrives:
self._menu_options.get('disk_layouts').set_current_selection(None)
storage['arguments']['disk_layouts'] = {}
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py
index f01f7597..ddd388dc 100644
--- a/archinstall/lib/user_interaction.py
+++ b/archinstall/lib/user_interaction.py
@@ -9,6 +9,7 @@ import signal
import sys
import time
from collections.abc import Iterable
+from copy import copy
from typing import List, Any, Optional, Dict, Union, TYPE_CHECKING
# https://stackoverflow.com/a/39757388/929999
@@ -274,21 +275,29 @@ class MiniCurses:
return response
-def ask_for_swap():
+def ask_for_swap(preset :bool = True) -> bool:
+ if preset:
+ preset_val = 'yes'
+ else:
+ preset_val = 'no'
prompt = _('Would you like to use swap on zram?')
- choice = Menu(prompt, ['yes', 'no'], default_option='yes').run()
+ choice = Menu(prompt, ['yes', 'no'], default_option='yes', preset_values=preset_val).run()
return False if choice == 'no' else True
-def ask_ntp() -> bool:
+def ask_ntp(preset :bool = True) -> bool:
prompt = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n'))
prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor more information, please check the Arch wiki'))
- choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run()
+ if preset:
+ preset_val = 'yes'
+ else:
+ preset_val = 'no'
+ choice = Menu(prompt, ['yes', 'no'], skip=False, preset_values=preset_val, default_option='yes').run()
return False if choice == 'no' else True
-def ask_hostname():
- hostname = input(_('Desired hostname for the installation: ')).strip(' ')
+def ask_hostname(preset :str = None) -> str :
+ hostname = TextInput(_('Desired hostname for the installation: '),preset).run().strip(' ')
return hostname
@@ -341,7 +350,7 @@ def ask_for_additional_users(prompt :str = '') -> tuple[dict[str, dict[str, str
return users, superusers
-def ask_for_a_timezone() -> str:
+def ask_for_a_timezone(preset :str = None) -> str:
timezones = list_timezones()
default = 'UTC'
@@ -349,18 +358,28 @@ def ask_for_a_timezone() -> str:
_('Select a timezone'),
list(timezones),
skip=False,
+ preset_values=preset,
default_option=default
).run()
return selected_tz
-def ask_for_bootloader(advanced_options :bool = False) -> str:
+def ask_for_bootloader(advanced_options :bool = False, preset :str = None) -> str:
+
+ if preset == 'systemd-bootctl':
+ preset_val = 'systemd-boot' if advanced_options else 'no'
+ elif preset == 'grub-install':
+ preset_val = 'grub' if advanced_options else 'yes'
+ else:
+ preset_val = preset
+
bootloader = "systemd-bootctl" if has_uefi() else "grub-install"
if has_uefi():
if not advanced_options:
bootloader_choice = Menu(
_('Would you like to use GRUB as a bootloader instead of systemd-boot?'),
['yes', 'no'],
+ preset_values=preset_val,
default_option='no'
).run()
@@ -369,7 +388,7 @@ def ask_for_bootloader(advanced_options :bool = False) -> str:
else:
# We use the common names for the bootloader as the selection, and map it back to the expected values.
choices = ['systemd-boot', 'grub', 'efistub']
- selection = Menu(_('Choose a bootloader'), choices).run()
+ selection = Menu(_('Choose a bootloader'), choices,preset_values=preset_val).run()
if selection != "":
if selection == 'systemd-boot':
bootloader = 'systemd-bootctl'
@@ -381,12 +400,13 @@ def ask_for_bootloader(advanced_options :bool = False) -> str:
return bootloader
-def ask_for_audio_selection(desktop :bool = True) -> str:
+def ask_for_audio_selection(desktop :bool = True, preset :str = None) -> str:
audio = 'pipewire' if desktop else 'none'
choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', 'none']
selected_audio = Menu(
_('Choose an audio server'),
choices,
+ preset_values=preset,
default_option=audio,
skip=False
).run()
@@ -424,7 +444,7 @@ def ask_additional_packages_to_install(pre_set_packages :List[str] = []) -> List
return packages
-def ask_to_configure_network() -> Dict[str, Any]:
+def ask_to_configure_network(preset :Dict[str, Any] = {}) -> Dict[str, Any]:
# Optionally configure one network interface.
# while 1:
# {MAC: Ifname}
@@ -433,12 +453,27 @@ def ask_to_configure_network() -> Dict[str, Any]:
'network_manager': str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)')),
**list_interfaces()
}
+ # for this routine it's easier to set the cursor position rather than a preset value
+ cursor_idx = None
+ if preset:
+ if preset['type'] == 'iso_config':
+ cursor_idx = 0
+ elif preset['type'] == 'network_manager':
+ cursor_idx = 1
+ else:
+ try :
+ # let's hope order in dictionaries stay
+ cursor_idx = list(interfaces.values()).index(preset.get('type'))
+ except ValueError:
+ pass
- nic = Menu(_('Select one network interface to configure'), list(interfaces.values())).run()
+ nic = Menu(_('Select one network interface to configure'), interfaces.values(),cursor_index=cursor_idx).run()
if not nic:
return {}
+ # nic = network_manager_nt
+
if nic == interfaces['iso_config']:
return {'type': 'iso_config'}
elif nic == interfaces['network_manager']:
@@ -448,16 +483,23 @@ def ask_to_configure_network() -> Dict[str, Any]:
# For selecting modes without entering text within brackets,
# printing out this part separate from options, passed in
# `generic_select`
+ # we only keep data if it is the same nic as before
+ if preset.get('type') != nic:
+ preset_d = {'type': nic, 'dhcp': True, 'ip': None, 'gateway': None, 'dns': []}
+ else:
+ preset_d = copy(preset)
+
modes = ['DHCP (auto detect)', 'IP (static)']
default_mode = 'DHCP (auto detect)'
+ cursor_idx = 0 if preset_d.get('dhcp',True) else 1
prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(nic, default_mode)
- mode = Menu(prompt, modes, default_option=default_mode).run()
-
+ mode = Menu(prompt, modes, default_option=default_mode, cursor_index=cursor_idx).run()
+ # TODO preset values for ip and gateway
if mode == 'IP (static)':
while 1:
prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(nic)
- ip = input(prompt).strip()
+ ip = TextInput(prompt,preset_d.get('ip')).run().strip()
# Implemented new check for correct IP/subnet input
try:
ipaddress.ip_interface(ip)
@@ -471,7 +513,7 @@ def ask_to_configure_network() -> Dict[str, Any]:
# Implemented new check for correct gateway IP address
while 1:
- gateway = input(_('Enter your gateway (router) IP address or leave blank for none: ')).strip()
+ gateway = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),preset_d.get('gateway')).run().strip()
try:
if len(gateway) == 0:
gateway = None
@@ -486,7 +528,11 @@ def ask_to_configure_network() -> Dict[str, Any]:
)
dns = None
- dns_input = input(_('Enter your DNS servers (space separated, blank for none): ')).strip()
+ if preset_d.get('dns'):
+ preset_d['dns'] = ' '.join(preset_d['dns'])
+ else:
+ preset_d['dns'] = None
+ dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '),preset_d['dns']).run().strip()
if len(dns_input):
dns = dns_input.split(' ')
@@ -847,7 +893,7 @@ def select_profile() -> Optional[Profile]:
return None
-def select_language(default_value :str) -> str:
+def select_language(default_value :str, preset_value :str = None) -> str:
"""
Asks the user to select a language
Usually this is combined with :ref:`archinstall.list_keyboard_languages`.
@@ -861,11 +907,11 @@ def select_language(default_value :str) -> str:
# allows for searching anyways
sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len)
- selected_lang = Menu(_('Select Keyboard layout'), sorted_kb_lang, default_option=default_value, sort=False).run()
+ selected_lang = Menu(_('Select Keyboard layout'), sorted_kb_lang, default_option=default_value, preset_values=preset_value, sort=False).run()
return selected_lang
-def select_mirror_regions() -> Dict[str, Any]:
+def select_mirror_regions(preset_values :Dict[str, Any] = {}) -> Dict[str, Any]:
"""
Asks the user to select a mirror or region
Usually this is combined with :ref:`archinstall.list_mirrors`.
@@ -873,11 +919,15 @@ def select_mirror_regions() -> Dict[str, Any]:
:return: The dictionary information about a mirror/region.
:rtype: dict
"""
-
+ if preset_values is None:
+ preselected = None
+ else:
+ preselected = list(preset_values.keys())
mirrors = list_mirrors()
selected_mirror = Menu(
_('Select one of the regions to download packages from'),
list(mirrors.keys()),
+ preset_values=preselected,
multi=True
).run()
@@ -887,7 +937,7 @@ def select_mirror_regions() -> Dict[str, Any]:
return {}
-def select_harddrives() -> Optional[str]:
+def select_harddrives(preset : List[str] = []) -> List[str]:
"""
Asks the user to select one or multiple hard drives
@@ -896,10 +946,11 @@ def select_harddrives() -> Optional[str]:
"""
hard_drives = all_blockdevices(partitions=False).values()
options = {f'{option}': option for option in hard_drives}
-
+ preset_disks = {f'{option}':option for option in preset}
selected_harddrive = Menu(
_('Select one or more hard drives to use and configure'),
list(options.keys()),
+ preset_values=list(preset_disks.keys()),
multi=True
).run()
@@ -943,7 +994,7 @@ def select_driver(options :Dict[str, Any] = AVAILABLE_GFX_DRIVERS, force_ask :bo
raise RequirementError("Selecting drivers require a least one profile to be given as an option.")
-def select_kernel() -> List[str]:
+def select_kernel(preset :List[str] = None) -> List[str]:
"""
Asks the user to select a kernel for system.
@@ -959,11 +1010,12 @@ def select_kernel() -> List[str]:
kernels,
sort=True,
multi=True,
+ preset_values=preset,
default_option=default_kernel
).run()
return selected_kernels
-def select_additional_repositories() -> List[str]:
+def select_additional_repositories(preset :List[str]) -> List[str]:
"""
Allows the user to select additional repositories (multilib, and testing) if desired.
@@ -978,6 +1030,7 @@ def select_additional_repositories() -> List[str]:
repositories,
sort=False,
multi=True,
+ preset_values=preset,
default_option=[]
).run()
@@ -986,7 +1039,7 @@ def select_additional_repositories() -> List[str]:
return []
-def select_locale_lang(default):
+def select_locale_lang(default :str,preset :str = None) -> str :
locales = list_locales()
locale_lang = set([locale.split()[0] for locale in locales])
@@ -994,13 +1047,14 @@ def select_locale_lang(default):
_('Choose which locale language to use'),
locale_lang,
sort=True,
+ preset_values=preset,
default_option=default
).run()
return selected_locale
-def select_locale_enc(default):
+def select_locale_enc(default :str,preset :str = None) -> str:
locales = list_locales()
locale_enc = set([locale.split()[1] for locale in locales])
@@ -1008,6 +1062,7 @@ def select_locale_enc(default):
_('Choose which locale encoding to use'),
locale_enc,
sort=True,
+ preset_values=preset,
default_option=default
).run()