diff --git a/not1mm/__main__.py b/not1mm/__main__.py index a80eae8..0adc7e5 100644 --- a/not1mm/__main__.py +++ b/not1mm/__main__.py @@ -164,6 +164,7 @@ class MainWindow(QtWidgets.QMainWindow): text_color = QColorConstants.Black current_palette = None use_esm = False + use_call_history = False esm_dict = {} radio_thread = QThread() @@ -199,6 +200,7 @@ def __init__(self, splash): self.setCorner(Qt.Corner.TopLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea) self.setCorner(Qt.Corner.BottomLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea) uic.loadUi(fsutils.APP_DATA_PATH / "main.ui", self) + self.history_info.hide() QApplication.instance().focusObjectChanged.connect(self.on_focus_changed) self.inputs_dict = { self.callsign: "callsign", @@ -224,12 +226,15 @@ def __init__(self, splash): self.actionCW_Macros.triggered.connect(self.cw_macros_state_changed) self.actionDark_Mode_2.triggered.connect(self.dark_mode_state_changed) - self.actionCommand_Buttons.triggered.connect(self.command_buttons_state_change) + self.actionCommand_Buttons_2.triggered.connect( + self.command_buttons_state_change + ) self.actionLog_Window.triggered.connect(self.launch_log_window) self.actionBandmap.triggered.connect(self.launch_bandmap_window) self.actionCheck_Window.triggered.connect(self.launch_check_window) self.actionVFO.triggered.connect(self.launch_vfo) self.actionRecalculate_Mults.triggered.connect(self.recalculate_mults) + self.actionLoad_Call_History_File.triggered.connect(self.load_call_history) self.actionGenerate_Cabrillo_ASCII.triggered.connect( lambda x: self.generate_cabrillo("ascii") @@ -290,6 +295,12 @@ def __init__(self, splash): self.radio_green = QtGui.QPixmap(str(icon_path / "radio_green.png")) self.radio_icon.setPixmap(self.radio_grey) + self.log_it.clicked.connect(self.save_contact) + self.wipe.clicked.connect(self.clearinputs) + self.esc_stop.clicked.connect(self.stop_cw) + self.mark.clicked.connect(self.mark_spot) + self.spot_it.clicked.connect(self.spot_dx) + self.F1.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu) self.F1.customContextMenuRequested.connect(lambda x: self.edit_macro(self.F1)) self.F1.clicked.connect(lambda x: self.process_function_key(self.F1)) @@ -703,6 +714,45 @@ def __init__(self, splash): "You can udate to the current version by using:\npip install -U not1mm" ) + def load_call_history(self) -> None: + """""" + filename = self.filepicker("other") + if filename: + self.database.create_callhistory_table() + self.database.delete_callhistory() + + try: + with open(filename, "rt", encoding="utf-8") as file_descriptor: + lines = file_descriptor.readlines() + if "!!Order!!" in lines[0]: + item_names = lines[0].strip().split(",") + # ['!!Order!!', 'Call', 'Sect', 'State', 'CK', 'UserText', ''] + item_names = item_names[1:-1] + # ['Call', 'Sect', 'State', 'CK', 'UserText'] + lines = lines[1:] + group_list = [] + for line in lines: + if line.startswith("#"): + continue + group = {} + fields = line.strip().split(",") + # ['4U1WB','MDC','DC','89',''] + count = 0 + try: + for item in item_names: + if item == "": + continue + group[item] = fields[count] + count += 1 + group_list.append(group) + # database.add_callhistory_item(group) + # print(f"{group=}") + except IndexError: + ... + self.database.add_callhistory_items(group_list) + except FileNotFoundError as err: + self.show_message_box(f"{err}") + def on_focus_changed(self, new): """""" if self.use_esm: @@ -1728,6 +1778,14 @@ def filepicker(self, action: str) -> str: "Database (*.db)", options=options, ) + if action == "other": + file, _ = QFileDialog.getOpenFileName( + self, + "Choose a File", + "~/", + "Any (*.*)", + options=options, + ) return file def recalculate_mults(self) -> None: @@ -1907,6 +1965,43 @@ def cwspeed_spinbox_changed(self) -> None: if self.rig_control.interface == "flrig": self.rig_control.cat.set_flrig_cw_speed(self.cw_speed.value()) + def stop_cw(self): + """""" + if self.cw is not None: + if self.cw.servertype == 1: + self.cw.sendcw("\x1b4") + return + if self.rig_control: + if self.rig_control.online: + if self.pref.get("cwtype") == 3 and self.rig_control is not None: + if self.rig_control.interface == "flrig": + self.rig_control.cat.set_flrig_cw_send(False) + self.rig_control.cat.set_flrig_cw_send(True) + + def mark_spot(self): + """""" + freq = self.radio_state.get("vfoa") + dx = self.callsign.text() + if freq and dx: + cmd = {} + cmd["cmd"] = "MARKDX" + cmd["dx"] = dx + cmd["freq"] = float(int(freq) / 1000) + if self.bandmap_window: + self.bandmap_window.msg_from_main(cmd) + + def spot_dx(self): + """""" + freq = self.radio_state.get("vfoa") + dx = self.callsign.text() + if freq and dx: + cmd = {} + cmd["cmd"] = "SPOTDX" + cmd["dx"] = dx + cmd["freq"] = float(int(freq) / 1000) + if self.bandmap_window: + self.bandmap_window.msg_from_main(cmd) + def keyPressEvent(self, event) -> None: # pylint: disable=invalid-name """ This overrides Qt key event. @@ -1921,6 +2016,12 @@ def keyPressEvent(self, event) -> None: # pylint: disable=invalid-name None """ modifier = event.modifiers() + if ( + event.key() == Qt.Key.Key_Equal + and modifier == Qt.KeyboardModifier.ControlModifier + ): + self.save_contact() + return if event.key() == Qt.Key.Key_K: self.toggle_cw_entry() return @@ -2901,6 +3002,9 @@ def readpreferences(self) -> None: "DISABLED": None, } + self.use_call_history = self.pref.get("use_call_history", False) + if self.use_call_history: + self.history_info.show() self.use_esm = self.pref.get("use_esm", False) self.esm_dict["CQ"] = fkey_dict.get(self.pref.get("esm_cq", "DISABLED")) self.esm_dict["EXCH"] = fkey_dict.get(self.pref.get("esm_exch", "DISABLED")) @@ -2968,7 +3072,7 @@ def command_buttons_state_change(self) -> None: None """ - self.pref["command_buttons"] = self.actionCommand_Buttons.isChecked() + self.pref["command_buttons"] = self.actionCommand_Buttons_2.isChecked() self.write_preference() self.show_command_buttons() @@ -3125,6 +3229,10 @@ def callsign_changed(self) -> None: self.lookup_service.msg_from_main(cmd) self.next_field.setFocus() if self.contest: + if self.use_call_history and hasattr( + self.contest, "check_call_history" + ): + self.contest.check_call_history(self) if "CQ WW" in self.contest.name or "IARU HF" in self.contest.name: self.contest.prefill(self) return diff --git a/not1mm/data/configuration.ui b/not1mm/data/configuration.ui index 2dacb35..a72306e 100644 --- a/not1mm/data/configuration.ui +++ b/not1mm/data/configuration.ui @@ -6,8 +6,8 @@ 0 0 - 703 - 443 + 684 + 499 @@ -22,36 +22,10 @@ - - - - - 0 - 0 - - - - - JetBrains Mono ExtraLight - 12 - false - - - - Qt::LayoutDirection::LeftToRight - - - Qt::Horizontal - - - QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save - - - true - - + + - + true @@ -1624,589 +1598,620 @@ Options - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - - - - Enable ESM - - - - - - - AGN - - - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - - - - QRZ - - - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Exchange - - - - - - - His Call - - - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - - - - CQ - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - - - - My Call - - - - - - - QSO B4 - - - - - - - - F1 - - - - - F2 - - - - - F3 - - - - - F4 - - - - - F5 - - - - - F6 - - - - - F7 - - - - - F8 - - - - - F9 - - - - - F10 - - - - - F11 - - - - - F12 - - - - - DISABLED - - - - - + + + + 5 + 25 + 636 + 376 + + + + + QLayout::SizeConstraint::SetDefaultConstraint + + + + + + + Enable ESM + + + + + + + CQ + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + My Call + + + + + + + His Call + + + + + + + Exchange + + + + + + + AGN + + + + + + + QRZ + + + + + + + QSO B4 + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + F4 + + + + + F5 + + + + + F6 + + + + + F7 + + + + + F8 + + + + + F9 + + + + + F10 + + + + + F11 + + + + + F12 + + + + + DISABLED + + + + + + + + + + + + Use Call History + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + JetBrains Mono ExtraLight + 12 + false + + + + Qt::LayoutDirection::LeftToRight + + + Qt::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save + + + true + + + @@ -2292,7 +2297,7 @@ - + diff --git a/not1mm/data/main.ui b/not1mm/data/main.ui index 7baf05b..42a979d 100644 --- a/not1mm/data/main.ui +++ b/not1mm/data/main.ui @@ -9,8 +9,8 @@ 0 0 - 785 - 268 + 855 + 641 @@ -59,7 +59,7 @@ 0 - + @@ -1302,7 +1302,7 @@ Qt::FocusPolicy::NoFocus - Edit + --- @@ -1322,7 +1322,7 @@ Qt::FocusPolicy::NoFocus - Store + --- @@ -1342,7 +1342,7 @@ Qt::FocusPolicy::NoFocus - QRZ + --- @@ -1367,6 +1367,13 @@ + + + + + + + @@ -1443,7 +1450,7 @@ 0 0 - 878 + 855 25 @@ -1476,6 +1483,7 @@ + @@ -1496,6 +1504,7 @@ + @@ -1978,6 +1987,24 @@ + + + Load Call History File + + + + + Command Buttons + + + + + true + + + Command Buttons + + diff --git a/not1mm/lib/database.py b/not1mm/lib/database.py index 8dfc7f5..eb53a53 100644 --- a/not1mm/lib/database.py +++ b/not1mm/lib/database.py @@ -78,6 +78,7 @@ def __init__(self, database: str, app_data_dir: str): self.create_contest_table() self.create_contest_instance_table() self.create_station_table() + self.create_callhistory_table() @staticmethod def row_factory(cursor, row): @@ -152,6 +153,36 @@ def create_dxlog_table(self) -> None: except sqlite3.OperationalError as exception: logger.debug("%s", exception) + def create_callhistory_table(self) -> None: + """creates the callhistory table""" + logger.debug("Creating DXLOG Table") + try: + with sqlite3.connect(self.database) as conn: + cursor = conn.cursor() + sql_command = ( + "CREATE TABLE IF NOT EXISTS CALLHISTORY (" + "Call VARCHAR(15) NOT NULL, " + "Name VARCHAR(20), " + "Loc1 VARCHAR(6) DEFAULT '', " + "Loc2 VARCHAR(6) DEFAULT '', " + "Sect VARCHAR(8) DEFAULT '', " + "State VARCHAR(8) DEFAULT '', " + "CK TINYINT DEFAULT 0, " + "BirthDate DATE, " + "Exch1 VARCHAR(12) DEFAULT '', " + "Misc VARCHAR(15) DEFAULT '', " + "Power VARCHAR(8) DEFAULT '', " + "CqZone TINYINT DEFAULT 0, " + "ITUZone TINYINT DEFAULT 0, " + "UserText VARCHAR(60) DEFAULT '', " + "LastUpdateNote VARCHAR(20) DEFAULT '' " + ");" + ) + cursor.execute(sql_command) + conn.commit() + except sqlite3.OperationalError as exception: + logger.debug("%s", exception) + def update_dxlog_table(self) -> None: """update missing columns""" logger.debug("Updating DXLOG Table") @@ -356,6 +387,49 @@ def fetch_contest_by_id(self, contest_nr: str) -> dict: logger.debug("%s", exception) return {} + def add_callhistory_item(self, history: dict) -> None: + """Add an item to the call history db""" + pre = "INSERT INTO CALLHISTORY(" + values = [] + columns = "" + placeholders = "" + for key in history.keys(): + columns += f"{key}," + values.append(history[key]) + placeholders += "?," + post = f") VALUES({placeholders[:-1]});" + sql = f"{pre}{columns[:-1]}{post}" + + try: + with sqlite3.connect(self.database) as conn: + cur = conn.cursor() + cur.execute(sql, tuple(values)) + conn.commit() + except sqlite3.Error as exception: + logger.info("%s", exception) + + def add_callhistory_items(self, history_list: list) -> None: + """Add a list of items to the call history db""" + try: + with sqlite3.connect(self.database) as conn: + for history in history_list: + pre = "INSERT INTO CALLHISTORY(" + values = [] + columns = "" + placeholders = "" + for key in history.keys(): + columns += f"{key}," + values.append(history[key]) + placeholders += "?," + post = f") VALUES({placeholders[:-1]});" + sql = f"{pre}{columns[:-1]}{post}" + cur = conn.cursor() + cur.execute(sql, tuple(values)) + conn.commit() + except sqlite3.Error as exception: + print(exception) + logger.info("%s", exception) + def add_contest(self, contest: dict) -> None: """Add Contest""" @@ -465,6 +539,29 @@ def delete_contact(self, unique_id: str) -> None: except sqlite3.Error as exception: logger.info("DataBase delete_contact: %s", exception) + def delete_callhistory(self) -> None: + """Deletes all info from callhistory table.""" + try: + with sqlite3.connect(self.database) as conn: + sql = "delete from CALLHISTORY;" + cur = conn.cursor() + cur.execute(sql) + conn.commit() + except sqlite3.Error as exception: + logger.info("%s", exception) + + def fetch_call_history(self, call: str): + """""" + try: + with sqlite3.connect(self.database) as conn: + conn.row_factory = self.row_factory + cursor = conn.cursor() + cursor.execute(f"select * from CALLHISTORY where call='{call}';") + return cursor.fetchone() + except sqlite3.OperationalError as exception: + logger.debug("%s", exception) + return {} + def fetch_all_contacts_asc(self) -> list: """returns a list of dicts with contacts in the database.""" try: diff --git a/not1mm/lib/settings.py b/not1mm/lib/settings.py index 5bf005c..acfbd3e 100644 --- a/not1mm/lib/settings.py +++ b/not1mm/lib/settings.py @@ -19,6 +19,8 @@ def __init__(self, app_data_path, pref, parent=None): super().__init__(parent) self.logger = logging.getLogger("settings") uic.loadUi(app_data_path / "configuration.ui", self) + self.tabWidget.setTabVisible(5, False) + # self.group_tab.hide() self.buttonBox.accepted.connect(self.save_changes) self.usecwdaemon_radioButton.clicked.connect(self.set_cwdaemon_port_hint) self.usepywinkeyer_radioButton.clicked.connect(self.set_winkeyer_port_hint) @@ -39,7 +41,11 @@ def __init__(self, app_data_path, pref, parent=None): def setup(self): """setup dialog""" - self.use_esm.setChecked(bool(self.preference.get("use_esm"))) + self.use_call_history.setChecked( + bool(self.preference.get("use_call_history", False)) + ) + + self.use_esm.setChecked(bool(self.preference.get("use_esm", False))) value = self.preference.get("esm_agn", "DISABLED") index = self.esm_agn.findText(value) @@ -188,6 +194,7 @@ def save_changes(self): """ Write preferences to json file. """ + self.preference["use_call_history"] = self.use_call_history.isChecked() self.preference["use_esm"] = self.use_esm.isChecked() self.preference["esm_cq"] = self.esm_cq.currentText() self.preference["esm_agn"] = self.esm_agn.currentText() diff --git a/not1mm/plugins/arrl_ss_cw.py b/not1mm/plugins/arrl_ss_cw.py index b2fb6ab..426a9e6 100644 --- a/not1mm/plugins/arrl_ss_cw.py +++ b/not1mm/plugins/arrl_ss_cw.py @@ -134,6 +134,16 @@ def prefill(self): field.setText(serial_nr) +def check_call_history(self): + """""" + result = self.database.fetch_call_history(self.callsign.text()) + print(f"{result=}") + if result: + self.history_info.setText(f"{result.get('UserText','')}") + if self.other_2.text() == "": + self.other_2.setText(f" {result.get('CK','')}{result.get('Sect', '')}") + + def points(self): """Calc point""" return 2 diff --git a/pyproject.toml b/pyproject.toml index e30a802..ba13121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ classifiers = [ [tool.setuptools.packages.find] where = ["."] -exclude = ["not1mm.testing*", "not1mm.not1mm.testing*", "testing*", "test","usb_vfo_knob*",] +exclude = ["not1mm.testing*", "not1mm.not1mm.testing*", "testing*", "test", "usb_vfo_knob*", "research*",] [tool.setuptools.package-data] "not1mm.data" = ["*.json", "*.txt", "*.SCP", "*.ui", "*.ttf", "*.desktop", "*.png", "*.qss", "*.sql", "*.html",]