From 9ee46f3f059eb2cc33b9fde1ea4c87dd896199ce Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Sat, 2 Nov 2024 22:17:00 -0700 Subject: [PATCH 1/2] Add BankModel interface that can include specials This adds a special interface that can be mixed into a BankModel to allow special channels to be added to banks. Some radios allow this, and this will let us model that. --- chirp/chirp_common.py | 13 ++++++++ chirp/drivers/fake.py | 72 ++++++++++++++++++++++++++++++++++++++++++ chirp/wxui/bankedit.py | 52 +++++++++++++++++++++--------- 3 files changed, 122 insertions(+), 15 deletions(-) diff --git a/chirp/chirp_common.py b/chirp/chirp_common.py index a5b93f97d..a8de44e39 100644 --- a/chirp/chirp_common.py +++ b/chirp/chirp_common.py @@ -821,6 +821,19 @@ def get_next_mapping_index(self, mapping): raise NotImplementedError() +class SpecialBankModelInterface: + """Interface for bank models that include special channels. + + If this is mixed into a BankModel, then any special channels returned by + get_bankable_specials() are candidates for parameters where a Memory is + used. + """ + + def get_bankable_specials(self): + """Return a list of special names that can be put into banks.""" + return [] + + class MTOBankModel(BankModel): """A bank model where one memory can be in multiple banks at once """ pass diff --git a/chirp/drivers/fake.py b/chirp/drivers/fake.py index f7f3e859a..da9812400 100644 --- a/chirp/drivers/fake.py +++ b/chirp/drivers/fake.py @@ -212,8 +212,80 @@ def get_radio(self): self.rclass = baofeng_uv17Pro.UV17Pro +class FakeBankModel(chirp_common.BankModel, + chirp_common.SpecialBankModelInterface): + def __init__(self, radio, name='Banks'): + super().__init__(radio, name) + self._banks = {i: set() for i in range(5)} + + def get_bankable_specials(self): + return self._radio.get_features().valid_special_chans[:-1] + + def get_num_mappings(self): + return len(self._banks) + + def get_mappings(self): + return [chirp_common.Bank(self, i, 'Bank %i' % i) for i in range(5)] + + def get_memory_mappings(self, memory): + banks = self.get_mappings() + in_banks = [i for i in range(5) if memory.number in self._banks[i]] + return [bank for bank in banks if bank.get_index() in in_banks] + + def add_memory_to_mapping(self, memory, mapping): + self._banks[mapping.get_index()].add(memory.number) + + def remove_memory_from_mapping(self, memory, mapping): + self._banks[mapping.get_index()].remove(memory.number) + + +class FakeClone(chirp_common.CloneModeRadio): + VENDOR = 'CHIRP' + MODEL = 'Fake Clone' + + def __init__(self, pipe): + super().__init__(pipe) + self._memories = [chirp_common.Memory(i) for i in range(10)] + # Make sure these are not contiguous with main memory so that there + # are no edge cases + self._specials = [chirp_common.Memory(100 + i) for i in range(3)] + for i, mem in enumerate(self._memories + self._specials): + mem.freq = 146000000 + (i * 100000) + if mem in self._specials: + mem.extd_number = 'Special %i' % mem.number + self._special_names = [x.extd_number for x in self._specials] + + def get_features(self) -> chirp_common.RadioFeatures: + rf = chirp_common.RadioFeatures() + rf.valid_special_chans = self._special_names + rf.valid_bands = [(144000000, 148000000)] + rf.memory_bounds = (0, 9) + rf.has_bank = True + return rf + + def get_bank_model(self): + return FakeBankModel(self) + + def get_memory(self, number): + if isinstance(number, str): + return self._specials[self._special_names.index(number)] + else: + try: + return self._memories[number] + except IndexError: + return self._specials[number - 100] + + def set_memory(self, memory): + if memory.extd_number: + self._specials[self._special_names.index(memory.extd_number)] = ( + memory) + else: + self._memories[memory.number] = memory + + def register_fakes(): directory.register(FakeLiveRadio) directory.register(FakeLiveSlowRadio) directory.register(FakeLiveRadioWithErrors) directory.register(FakeCloneFail) + directory.register(FakeClone) diff --git a/chirp/wxui/bankedit.py b/chirp/wxui/bankedit.py index 91129e451..d510eb1e7 100644 --- a/chirp/wxui/bankedit.py +++ b/chirp/wxui/bankedit.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import logging import platform import wx @@ -24,6 +25,7 @@ from chirp.wxui import memedit CONF = config.get() +LOG = logging.getLogger(__name__) if platform.system() == 'Linux': @@ -73,13 +75,17 @@ def __init__(self, radio, *a, **k): self._radio = radio self._features = radio.get_features() self._bankmodel = radio.get_bank_model() + if isinstance(self._bankmodel, chirp_common.SpecialBankModelInterface): + self._specials = self._bankmodel.get_bankable_specials() + else: + self._specials = [] self._col_defs = self._setup_columns() self._grid = memedit.ChirpMemoryGrid(self) self._grid.CreateGrid( self._features.memory_bounds[1] - self._features.memory_bounds[0] + - 1, len(self._col_defs)) + 1 + len(self._specials), len(self._col_defs)) # GridSelectNone only available in >=4.2.0 if hasattr(wx.grid.Grid, 'GridSelectNone'): self._grid.SetSelectionMode(wx.grid.Grid.GridSelectNone) @@ -143,6 +149,10 @@ def refresh_memories(self): mem = self._radio.get_memory(i) self._refresh_memory(mem) + for special in self._specials: + mem = self._radio.get_memory(special) + self._refresh_memory(mem) + wx.CallAfter(self._grid.AutoSizeColumns, setAsMin=True) def _setup_columns(self): @@ -174,10 +184,23 @@ def bank2col(self, bank): return bank + self._meta_cols def row2mem(self, row): - return row + self._features.memory_bounds[0] + number = row + self._features.memory_bounds[0] + try: + return self._memory_cache[number] + except KeyError: + special = self._specials[row - + self._features.memory_bounds[1] - + self._features.memory_bounds[0] - 1] + return self._memory_cache[special] def mem2row(self, mem): - return mem - self._features.memory_bounds[0] + if mem.extd_number: + row = (self._features.memory_bounds[1] - + self._features.memory_bounds[0] + + self._specials.index(mem.extd_number) + 1) + return row + else: + return mem.number - self._features.memory_bounds[0] def _colheader_mouseover(self, event): x = event.GetX() @@ -223,16 +246,16 @@ def _index_changed(self, event): if isinstance(self._col_defs[col], ChirpBankIndexColumn): self._change_memory_index(self.row2mem(row), int(value)) - def _change_memory_index(self, number, index): + def _change_memory_index(self, mem, index): for i, bank_index in enumerate(self._bank_index_order): - if self._grid.GetCellValue(self.mem2row(number), + if self._grid.GetCellValue(self.mem2row(mem), self.bank2col(i)) == BANK_SET_VALUE: member_bank = self._bank_indexes[bank_index] break else: raise Exception(_('Memory must be in a bank to be edited')) - self._bankmodel.set_memory_index(self._memory_cache[number], + self._bankmodel.set_memory_index(self._memory_cache[mem.number], member_bank, index) @@ -253,8 +276,7 @@ def _memory_changed(self, event): self.col2bank(col), value != BANK_SET_VALUE) - def _change_memory_mapping(self, number, bank, present): - mem = self._memory_cache[number] + def _change_memory_mapping(self, mem, bank, present): bank = self._bank_indexes[self._bank_index_order[bank]] if present: self._bankmodel.add_memory_to_mapping(mem, bank) @@ -267,16 +289,16 @@ def _change_memory_mapping(self, number, bank, present): @common.error_proof() def _refresh_memory(self, mem): - self._memory_cache[mem.number] = mem - self._grid.SetRowLabelValue(self.mem2row(mem.number), - '%i' % mem.number) + self._memory_cache[mem.extd_number or mem.number] = mem + self._grid.SetRowLabelValue(self.mem2row(mem), + mem.extd_number or ('%i' % mem.number)) bank_index = None member = [bank.get_index() for bank in self._bankmodel.get_memory_mappings(mem)] for i, bank in enumerate(self._bank_indexes.values()): present = bank.get_index() in member and not mem.empty - self._grid.SetCellValue(self.mem2row(mem.number), + self._grid.SetCellValue(self.mem2row(mem), self.bank2col(i), present and BANK_SET_VALUE or '') if present and isinstance(self._bankmodel, @@ -290,14 +312,14 @@ def _refresh_memory(self, mem): if meta_col.name == 'freq': freq = '' if mem.empty else chirp_common.format_freq(mem.freq) self._grid.SetCellValue( - self.mem2row(mem.number), i, freq) + self.mem2row(mem), i, freq) elif meta_col.name == 'name': self._grid.SetCellValue( - self.mem2row(mem.number), + self.mem2row(mem), i, '' if mem.empty else mem.name) elif meta_col.name == 'bank_index' and bank_index is not None: self._grid.SetCellValue( - self.mem2row(mem.number), + self.mem2row(mem), i, '' if mem.empty else '%i' % bank_index) From 4305d165d19ff563d84b1a61c63b811d23d74bfa Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Sat, 2 Nov 2024 22:24:09 -0700 Subject: [PATCH 2/2] Always hide empty memories in bank editor There's not much point is showing empty memories in the bank editor, so always hide them for brevity. --- chirp/drivers/fake.py | 1 + chirp/wxui/bankedit.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/chirp/drivers/fake.py b/chirp/drivers/fake.py index da9812400..749b2b1ba 100644 --- a/chirp/drivers/fake.py +++ b/chirp/drivers/fake.py @@ -251,6 +251,7 @@ def __init__(self, pipe): self._specials = [chirp_common.Memory(100 + i) for i in range(3)] for i, mem in enumerate(self._memories + self._specials): mem.freq = 146000000 + (i * 100000) + mem.empty = i < 5 if mem in self._specials: mem.extd_number = 'Special %i' % mem.number self._special_names = [x.extd_number for x in self._specials] diff --git a/chirp/wxui/bankedit.py b/chirp/wxui/bankedit.py index d510eb1e7..2db394985 100644 --- a/chirp/wxui/bankedit.py +++ b/chirp/wxui/bankedit.py @@ -292,6 +292,10 @@ def _refresh_memory(self, mem): self._memory_cache[mem.extd_number or mem.number] = mem self._grid.SetRowLabelValue(self.mem2row(mem), mem.extd_number or ('%i' % mem.number)) + if mem.empty: + self._grid.HideRow(self.mem2row(mem)) + else: + self._grid.ShowRow(self.mem2row(mem)) bank_index = None member = [bank.get_index()