Work on native gui: list views for objects; set version to 0.2.2

This commit is contained in:
2026-09-21 19:07:59 +02:00
parent d61faf2e71
commit 0adb07d75b
34 changed files with 3199 additions and 1133 deletions
+10 -53
View File
@@ -2,14 +2,11 @@
"""
Spezial:
Notebook mit Vessel, User, Lists, Company
Notebook mit Vessel, User, Lists, Settings
"""
import os
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('Rsvg', '2.0')
from gi.repository import GLib, Gtk, Gdk, Rsvg
from .view import View
@@ -28,22 +25,6 @@ class BaseView(View):
#self.topwidget = builder.get_object("topobject")
#gtkutils.init_named_controls(self, builder)
# company types
self.db.cur.execute("SELECT ddval, ddtext FROM dropdown WHERE ddid=2 ORDER BY ddval")
self.comptype = dict(self.db.cur.fetchall())
combobox.load_from_dict(self.combo['flt_type'], {-2: _("─── all ───")} | self.comptype, -1)
# companies
self.lst_companies = Gtk.ListStore(int, str, str, str)
self.db.cur.execute("SELECT compid, compname, comptype, remarks FROM company ORDER BY compname")
for row in self.db.cur.fetchall():
self.lst_companies.append((row[0], row[1], self.comptype[row[2]], row[3]))
treeview.add_column(self.tree['companies'], _('ID'), 0)
treeview.add_column(self.tree['companies'], _('Name'), 1)
treeview.add_column(self.tree['companies'], _('Type'), 2)
treeview.add_column(self.tree['companies'], _('Remarks'), 3)
self.tree['companies'].set_model(self.lst_companies)
# combos
roles = {'captain': 'Captain', 'helmsman': 'Helmsman', 'sailor': 'Sailor'}
combobox.load_from_dict(self.combo['user_role'], roles)
@@ -118,6 +99,9 @@ class BaseView(View):
treeview.add_column(self.tree['ddlists'], _('Records'), 2)
self.tree['ddlists'].set_model(self.lst_lists)
# Settings date format selection list
# self.context.datefmt_map
# load shape of vessel if available
if self.context.vessel.vid > 0:
self.db.cur.execute("SELECT shapefile FROM vessel WHERE vid=%s", (self.context.vessel.vid, ))
@@ -186,9 +170,7 @@ class BaseView(View):
vessel.draught_min = self.spin['draught_min'].get_value()
vessel.displacement = self.spin['displacement'].get_value_as_int()
vessel.ballast = self.spin['ballast'].get_value_as_int()
txtbuf = self.text['vessel_remarks'].get_buffer()
vessel.remarks = txtbuf.get_text(txtbuf.get_start_iter(), txtbuf.get_end_iter(), True)
vessel.remarks = gtkutils.get_text_contents(self.text['vessel_remarks'])
vessel.save()
self.stack['vessel'].set_visible_child(self.box['vessel'])
@@ -198,20 +180,11 @@ class BaseView(View):
def on_but_vessel_delete_clicked(self, widget):
pass
def on_but_comp_new_clicked(self, widget):
print("New company")
def on_but_comp_edit_clicked(self, widget):
print("Edit company")
def on_but_comp_detail_clicked(self, widget):
print("Company details")
def on_but_comp_save_clicked(self, widget):
print("Save company")
def on_but_comp_cancel_clicked(self, widget):
pass
def on_but_settings_save_clicked(self, widget):
print("Save settings")
#self.entry['sys_currency']
#self.spin['sys_max_width']
#self.spin['sys_max_height']
def display_vessel(self, vessel):
self.label['vesselname'].set_text(vessel.vesselname)
@@ -242,22 +215,6 @@ class BaseView(View):
self.check['user_deleted'].set_active('deleted' in user.flags)
self.entry['user_pass'].set_text('')
def display_company(self, company):
self.entry['compname'].set_text(company.compname)
# Type
# Type2
self.entry['shortname'].set_text(company.shortname)
self.entry['street'].set_text(company.street)
self.entry['zip'].set_text(company.zip)
self.entry['city'].set_text(company.city)
self.entry['country'].set_text(company.country)
self.entry['contact'].set_text(company.contact)
self.entry['phone'].set_text(company.phone)
self.entry['email'].set_text(company.email)
self.entry['web'].set_text(company.web)
self.entry['customerno'].set_text(company.customerno)
self.entry['contractno'].set_text(company.contractno)
def on_tvw_user_row_activated(self, widget, path, column):
# load selected user
self.context.user.user_id = treeview.get_value(widget, 0)
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Gdk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from .view import View
+73
View File
@@ -0,0 +1,73 @@
# SPDX-License-Identifier: WTFPL
import os
from gi.repository import Gtk
from .view import View
from yms import gtkutils, combobox, treeview
from yms.model import company
class CompaniesView(View):
def __init__(self, context):
super().__init__(context, _("Companies"), "companies.ui")
self.comp = company.Company(context.db) # current record
self.newrecord = False
# company types
self.db.cur.execute("SELECT ddval, ddtext FROM dropdown WHERE ddid=2 ORDER BY ddval")
self.comptype = dict(self.db.cur.fetchall())
combobox.load_from_dict(self.combo['flt_type'], {-2: _("─── all ───")} | self.comptype, -2)
# companied
self.lst_companies = Gtk.ListStore(int, str, str, str)
self.db.cur.execute("SELECT compid, compname, comptype, remarks FROM company ORDER BY compname")
for row in self.db.cur.fetchall():
self.lst_companies.append((row[0], row[1], self.comptype[row[2]], row[3]))
treeview.add_column(self.tree['companies'], _('ID'), 0)
treeview.add_column(self.tree['companies'], _('Name'), 1)
treeview.add_column(self.tree['companies'], _('Type'), 2)
treeview.add_column(self.tree['companies'], _('Remarks'), 3)
self.tree['companies'].set_model(self.lst_companies)
def display_company(self, company):
self.entry['compname'].set_text(company.compname)
# Type
# Type2
self.entry['shortname'].set_text(company.shortname)
self.entry['street'].set_text(company.street)
self.entry['zip'].set_text(company.zip)
self.entry['city'].set_text(company.city)
self.entry['country'].set_text(company.country)
self.entry['contact'].set_text(company.contact)
self.entry['phone'].set_text(company.phone)
self.entry['email'].set_text(company.email)
self.entry['web'].set_text(company.web)
self.entry['customerno'].set_text(company.customerno)
self.entry['contractno'].set_text(company.contractno)
# buttons on list page
def on_but_new_clicked(self, widget):
self.topwidget.set_visible_child(self.box['comp_edit'])
def on_but_edit_clicked(self, widget):
self.topwidget.set_visible_child(self.box['comp_edit'])
def on_but_detail_clicked(self, widget):
self.topwidget.set_visible_child(self.box['comp_detail'])
# buttons on detail page
# edit2 uses edit callback
def on_but_close_clicked(self, widget):
self.topwidget.set_visible_child(self.box['companies'])
def on_but_delete_clicked(self, widget):
self.topwidget.set_visible_child(self.box['companies'])
# buttons on edit page
def on_but_save_clicked(self, widget):
pass
def on_but_cancel_clicked(self, widget):
self.topwidget.set_visible_child(self.box['companies'])
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Gdk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Gdk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Gdk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from .view import View
-2
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from .view import View
+93 -3
View File
@@ -1,10 +1,9 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import re
from gi.repository import Gtk, Rsvg
import xml.etree.ElementTree as ET
from yms import gtkutils, combobox, treeview
@@ -21,6 +20,7 @@ class StatusView:
gtkutils.init_named_controls(self, builder)
# Generic information
self.label['version'].set_text("YMS version {}".format(context.__version__))
self.label['dbinfo'].set_text("Database: {} on {}".format(context.cfg['db'], context.cfg['host']))
# Load table status info
@@ -42,6 +42,96 @@ class StatusView:
contents = re.sub(r'(^\s*pass\s*=\s*).*$', r'\1********', contents, flags=re.MULTILINE)
self.text ['config'].get_buffer().set_text(contents)
# data for svg deck plan
self.svg = None
self.svg_width = 0
self.svg_height = 0
self.svg_scale = 1.0
self.svg_x = 0.0
self.svg_y = 0.0
self.svg_centerline_y = 0.0
self.load_svg()
def load_svg(self):
if not self.context.vessel.shapefile:
print("No Shapefile defined for current vessel")
return
shapefile = os.path.join(self.context.cfg['shapedir'], self.context.vessel.shapefile)
if not (os.path.isfile(shapefile) or os.access(shapefile, os.R_OK)):
print(f"Shapefile '{shapefile}' not found or not readable")
return
try:
self.svg = Rsvg.Handle.new_from_file(shapefile)
success, width, height = self.svg.get_intrinsic_size_in_pixels()
if not success:
raise RuntimeError("SVG has no pixelsize")
self.svg_width = width
self.svg_height = height
print(f"SVG: {self.svg_width} x {self.svg_height}")
self.area['status'].queue_draw()
except Exception as e:
print("Error trying to load svg deck plan:", e)
return
# try to detect centerline
self.svg_centerline_y = None
try:
tree = ET.parse(shapefile)
except Exception as e:
print("Error reading centerline from svg:", e)
self.svg_centerline_y = self.svg_height / 2
return
root = tree.getroot()
found = False
for element in root.iter():
if element.get("id") == "centerline":
found = True
print("\n--- centerline gefunden ---")
print("Tag:", element.tag)
print("Attribute:", element.attrib)
y1 = element.get("y1")
y2 = element.get("y2")
if y1 is not None and y2 is not None:
y1 = float(y1)
y2 = float(y2)
if not math.isclose(y1, y2, abs_tol=0.001):
raise ValueError(f"centerline not horizontal: y1={y1}, y2={y2}")
# calc arithmetic mean as fallback
self.centerline_y = (y1 + y2) / 2
else:
self.centerline_y = y1
print(f"centerline: SVG Y = {self.centerline_y}")
return
if not found:
print("No centerline with id='centerline' found!")
def on_drw_status_draw(self, area, cr):
cr.set_source_rgb(0.92, 0.97, 1.0)
cr.paint()
allocation = area.get_allocation()
scale_x = allocation.width / self.svg_width
scale_y = allocation.height / self.svg_height
self.svg_scale = min(scale_x, scale_y)
width = self.svg_width * self.svg_scale
height = self.svg_height * self.svg_scale
self.svg_x = (allocation.width - width) / 2
self.svg_y = (allocation.height - height) / 2
cr.save()
cr.translate(self.svg_x, self.svg_y)
cr.scale(self.svg_scale, self.svg_scale)
viewport = Rsvg.Rectangle()
viewport.x = 0
viewport.y = 0
viewport.width = self.svg_width
viewport.height = self.svg_height
self.svg.render_document(cr, viewport)
cr.restore()
return False
def get_table_counts(self):
# Detect all tables in database
sql= ("SELECT table_name "
+65 -16
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: WTFPL
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Gdk
from .view import View
@@ -15,19 +13,13 @@ class StorageView(View):
def __init__(self, context):
super().__init__(context, _("Storage"), "storage.ui")
self.context = context
self.storage = storage.Storage(context.db) # current record
self.newrecord = False
self.lst_storage = Gtk.ListStore(int, str, str, str)
sql = (
"SELECT sid, sname, CONCAT(x, ',', y, ',', z) AS position, remarks "
"FROM storage "
"WHERE vid=%s "
"ORDER BY sname"
)
self.db.cur.execute(sql, (context.vessel.vid,))
for row in self.db.cur.fetchall():
self.lst_storage.append(row)
self.load_list()
treeview.add_column(self.tree['storage'], _('ID'), 0)
treeview.add_column(self.tree['storage'], _('Name'), 1)
@@ -35,18 +27,34 @@ class StorageView(View):
treeview.add_column(self.tree['storage'], _('Remarks'), 3)
self.tree['storage'].set_model(self.lst_storage)
combodict = self.context.lookup.dropdown(Dropdown.STORAGE_TYPE)
combobox.load_from_dict(self.combo['type'], combodict)
#combodict = self.context.lookup.dropdown(Dropdown.STORAGE_TYPE)
#combobox.load_from_dict(self.combo['type'], combodict)
combodict = self.context.lookup.dropdown(Dropdown.STORAGE_TYPE)
print(combodict)
combobox.load_from_dict(self.combo['type'], combodict)
combodict = self.context.lookup.enum('storage.capaunit')
combobox.load_from_dict(self.combo['unit'], combodict)
def load_list(self):
def add_row(self):
pass
def refresh_row(self):
pass
def load_list(self):
self.lst_storage.clear()
sql = (
"SELECT sid, sname, CONCAT(x, ',', y, ',', z) AS position, remarks "
"FROM storage "
"WHERE vid=%s "
"ORDER BY sname"
)
self.db.cur.execute(sql, (self.context.vessel.vid,))
for row in self.db.cur.fetchall():
self.lst_storage.append(row)
def display_record(self):
self.label['name'].set_text(self.storage.sname)
self.label['remarks'].set_text(self.storage.remarks or '')
@@ -56,12 +64,36 @@ class StorageView(View):
self.spin['x'].set_value(self.storage.x)
self.spin['y'].set_value(self.storage.y)
self.spin['z'].set_value(self.storage.z)
self.spin['wx'].set_value(self.storage.wx)
self.spin['wy'].set_value(self.storage.wy)
self.spin['wz'].set_value(self.storage.wz)
self.spin['angle'].set_value(self.storage.angle)
self.spin['capacity'].set_value(self.storage.capacity)
combobox.activate_row(self.combo['type'], self.storage.stype)
combobox.activate_row(self.combo['unit'], self.storage.capaunit)
self.text['remarks'].get_buffer().set_text(self.storage.remarks or '')
def clear_record(self):
self.storage.clear()
self.entry['sname'].set_text('')
self.spin['x'].set_value(0)
self.spin['y'].set_value(0)
self.spin['z'].set_value(0)
self.spin['wx'].set_value(0)
self.spin['wy'].set_value(0)
self.spin['wz'].set_value(0)
self.spin['angle'].set_value(0)
self.spin['capacity'].set_value(0)
combobox.activate_row(self.combo['type'], -1)
combobox.activate_row(self.combo['unit'], .1)
self.text['remarks'].get_buffer().set_text('')
# buttons on list page
def on_but_new_clicked(self, widget):
self.newrecord = True
self.label['title_edit'].set_text("Add new Storage")
self.clear_record()
self.storage.vid = self.context.vessel.vid
self.topwidget.set_visible_child(self.box['stor_edit'])
def on_but_edit_clicked(self, widget):
@@ -91,12 +123,29 @@ class StorageView(View):
# buttons on edit page
def on_but_save_clicked(self, widget):
self.newrecord = False
# check mandatory fields
sname = self.entry['sname'].get_text().strip()
if not sname:
# TODO error marker
return
# here be dragons
# get fields from ui form
self.storage.sname = sname
self.storage.stype = combobox.get_list_value(self.combo['type'])
self.storage.x = self.spin['x'].get_value()
self.storage.y = self.spin['y'].get_value()
self.storage.z = self.spin['z'].get_value()
self.storage.wx = self.spin['wx'].get_value()
self.storage.wy = self.spin['wy'].get_value()
self.storage.wz = self.spin['wz'].get_value()
self.storage.angle = self.spin['angle'].get_value()
self.storage.capacity = self.spin['capacity'].get_value()
self.storage.capaunit = combobox.get_list_value(self.combo['unit'])
self.storage.remarks = gtkutils.get_text_contents(self.text['remarks'])
self.storage.save()
if self.newrecord:
self.load_list()
self.newrecord = False
self.topwidget.set_visible_child(self.box['storage'])
def on_but_cancel_clicked(self, widget):
self.topwidget.set_visible_child(self.box['storage'])
-2
View File
@@ -7,8 +7,6 @@ SPDX-License-Identifier: WTFPL
"""
import os
import locale
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk, Gdk
from yms import gtkutils