Work on native gui. Display records and add missing tasks and measurements.

This commit is contained in:
2026-09-30 16:18:44 +02:00
parent c3109b9d2a
commit 131af7d9e7
45 changed files with 6043 additions and 1966 deletions
+167 -8
View File
@@ -7,23 +7,35 @@ Notebook mit Vessel, User, Lists, Settings
"""
import os
import pyudev
from gi.repository import GLib, Gtk, Gdk, Rsvg
from datetime import datetime
import subprocess
import locale
from .view import View
from yms import gtkutils, combobox, treeview
from yms.lookup import Dropdown
from yms.model import user
class BaseView(View):
def __init__(self, context):
super().__init__(context, _("Base Data"), "base.ui")
#self.title = _("Base Data")
#builder = Gtk.Builder()
#builder.set_translation_domain(context.textdomain)
#builder.add_from_file(os.path.join(context.ui_path, "base.ui"))
#builder.connect_signals(self)
#self.topwidget = builder.get_object("topobject")
#gtkutils.init_named_controls(self, builder)
self.context = context
self.user = user.User(context.db) # current user record
self.newuser = False
self.backupdevice = None # USB-Stick detected only on demand
# Settings
self.sys_currency = context.db.load_setting(4)[0] or '€'
self.entry['sys_currency'].set_text(self.sys_currency)
self.sys_max_width = context.db.load_setting(20)[1] or 1024
self.spin['sys_max_width'].set_value(self.sys_max_width)
self.sys_max_height = context.db.load_setting(21)[1] or 768
self.spin['sys_max_height'].set_value(self.sys_max_height)
# combos
roles = {'captain': 'Captain', 'helmsman': 'Helmsman', 'sailor': 'Sailor'}
@@ -31,6 +43,14 @@ class BaseView(View):
langs = {'de': 'Deutsch', 'en': 'English'}
combobox.load_from_dict(self.combo['user_lang'], langs)
today = datetime.now()
datedict = {
key: f"{prefix} - {today.strftime(fmt)}"
for key, (fmt, prefix) in context.datefmt_map.items()
}
self.datefmt = context.db.load_setting(2)[0] or 'Y-m-d'
combobox.load_from_dict(self.combo['datefmt'], datedict, self.datefmt)
# shipyard
self.db.cur.execute("SELECT compid, compname FROM company WHERE comptype=3 ORDER BY compname")
shipyards = dict(self.db.cur.fetchall())
@@ -129,6 +149,132 @@ class BaseView(View):
storages = dict(self.db.cur.fetchall())
combobox.refresh_dict(self.combo['storage'], {-1: _("─── none ───")} | storages)
def find_usb_sticks(self):
result = []
for name in os.listdir("/sys/block"):
device = "/sys/block/" + name
removable = device + "/removable"
if not os.path.exists(removable):
continue
with open(removable) as f:
if f.read().strip() != "1":
continue
device_path = os.path.realpath(device + "/device")
if "/usb" in device_path.lower():
result.append(name)
return result
def is_mounted(self, device):
with open("/proc/mounts") as f:
for line in f:
mounted_device = line.split()[0]
if mounted_device == device:
return True
return False
def on_but_usbdevice_clicked(self, widget):
self.backupdevice = None
devlist = self.find_usb_sticks()
if len(devlist) == 0:
self.label['usb_device'].set_text('n/a')
return
if len(devlist) != 1:
print(devlist)
print("Multiple devices not supported yet")
self.label['usb_device'].set_text('multiple')
return
usbdev = devlist[0]
self.label['usb_device'].set_text(f"/dev/{usbdev}")
"""
serial number via python3-pyudev:
import pyudev
Settings
7 - usb backup device serial
8 - usb backup partition uuid
9 - last backup timestamp
"""
# first get stick hardware data
udevctx = pyudev.Context()
device = pyudev.Devices.from_sys_path(udevctx, os.path.join("/sys/class/block", usbdev))
serial = device.get("ID_SERIAL_SHORT")
vendor = device.get("ID_VENDOR")
model = device.get("ID_MODEL")
self.label['usb_serial'].set_text(serial or 'n/a')
self.label['usb_vendor'].set_text(vendor or 'n/a')
self.label['usb_model'].set_text(model or 'n/a')
# TODO second get partition, filesystem type and label
# there must be only one ext4 partition on disk
partitions = [
child for child in device.children
if child.device_type == "partition"
]
partition = partitions[0]
print(f"Partition={partition}")
if len(partitions) != 1:
print("error: more than one partition!")
return
partuuid = partition.properties.get("ID_PART_ENTRY_UUID")
fstype = partition.get("ID_FS_TYPE")
plabel = partition.get("ID_FS_LABEL")
self.label['usb_partition'].set_text(plabel)
self.label['usb_uuid'].set_text(partuuid)
self.label['usb_fstype'].set_text(fstype)
if fstype != "ext4":
print(f"error: invalid fileystem type {fstype}, ext4 expected")
return
self.backupdevice = f"/dev/{usbdev}1" # partition!
# check if device is mounted
if not self.is_mounted(self.backupdevice):
print("device is not mounted. trying to mount now")
pass
# For backup there must be an directory names ymsbackup which contains
# backup description and backup sets
# Desription and identification file backup.json?
# if plabel != 'BACKUP' or not os.path.isdir('ymsbackup')
mountpoint = os.path.join("/media", os.getlogin(), "BACKUP")
if not os.path.isdir(mountpoint):
print(f"Device is not mounted at {mountpoint}")
def on_but_usbinit_clicked(self, widget):
if self.backupdevice is None:
return
# UUID of devive -> settings
# check if backups already exist
answer = gtkutils.questionbox(
_("Initialize USB-Stick\n"
"All data on that device will be lost!\n"
"Are you really sure?"
), title="YMS")
if answer == False:
return
if self.label['usb_fstype'].get_text() != 'ext4':
print("formatting {self.backupdevice} with ext4")
#try:
# subprocess.run(["sudo", "mkfs.ext4", "-L", "BACKUP", self.backupdevice], check=True)
#except subprocess.CalledProcessError as e:
# print("formatting failed: ", e)
myself = os.getlogin()
mountpoint = os.path.join("/media", myself, "backup")
basedir = os.path.join(mountpoint, 'ymsbackup')
print(basedir)
# mkdir basedir
# chown (os.getlogin(), 'yms')
# chmod 770
else:
#
pass
def on_cmb_vessel_changed(self, widget):
self.context.vessel.load(combobox.get_list_value(widget))
sql = ("UPDATE settings SET valint=%s WHERE userid=0 AND sno=1")
@@ -189,7 +335,20 @@ class BaseView(View):
def display_vessel(self, vessel):
self.label['vesselname'].set_text(vessel.vesselname)
self.label['model'].set_text(vessel.model or '')
self.label['year_built'].set_text(self.format_int(vessel.year_built, default='n/a'))
self.label['shipyard'].set_text(str(vessel.shipyard))
self.label['year_built'].set_text(self.format_int(vessel.year_built, False, default='n/a'))
self.label['loa'].set_text(self.format_float(vessel.loa, 2, suffix="m"))
self.label['lwl'].set_text(self.format_float(vessel.lwl, 2, suffix="m"))
self.label['beam'].set_text(self.format_float(vessel.beam, 2, suffix="m"))
self.label['draught'].set_text(self.format_float(vessel.draught, 2, suffix="m"))
self.label['draught_min'].set_text(self.format_float(vessel.draught_min, 2, suffix="m"))
self.label['displacement'].set_text(self.format_int(vessel.displacement, 2, suffix="kg"))
self.label['ballast'].set_text(self.format_int(vessel.ballast, 2, suffix="kg"))
self.label['belt_bow'].set_text(self.format_float(vessel.beltpos_bow, 2, suffix="m"))
self.label['belt_aft'].set_text(self.format_float(vessel.beltpos_aft, 2, suffix="m"))
self.label['hull_shape'].set_text(vessel.shapefile or '')
self.label['storage_def'].set_text(str(vessel.sid_default))
self.label['remarks'].set_text(vessel.remarks or '')
def edit_vessel(self, vessel):
self.entry['vesselname'].set_text(vessel.vesselname)