Files
YMS/ymsgui.py

282 lines
9.9 KiB
Python
Executable File

#!/usr/bin/python3
"""
YMS native GUI.
Runs as user SYSTEM with id=0
Configuration file located at ~/.config/ymsgui.conf:
[DB]
host = localhost
db = yms
user = ymsgui
pass = topsecret
[Documents]
basepath = /var/local/yms
"""
import os
import sys
import locale
import gettext
import configparser
os.environ["NO_AT_BRIDGE"] = "1" # fix dbus warnings
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('Rsvg', '2.0')
from gi.repository import GLib, Gtk, Rsvg
from yms import database
from yms import gtkutils, combobox, treeview
from yms import user
cfg = {
'cfgfile': '~/.config/ymsgui.conf',
'host': 'localhost',
'db': 'yms',
'user': 'yms',
'docpath': '/var/local/yms',
'shapedir': '/usr/local/bin/yms/shapes'
}
def get_uiprefix():
path = os.path.realpath(__file__)
if path.startswith("/usr/local/"):
return "/usr/local/share"
if path.startswith("/usr/"):
return "/usr/share"
return ""
class Frontend():
def __init__(self):
builder = Gtk.Builder()
builder.add_from_file(os.path.join(get_uiprefix(), "yms", "ui", "main.ui"))
builder.connect_signals(self)
self.window = builder.get_object("wnd_main")
gtkutils.init_named_controls(self, builder)
# get current vessel id
db.cur.execute("SELECT valint FROM settings WHERE userid=0 AND sno=1")
row = db.cur.fetchone()
if not row:
self.vid = 0
self.vname = "Ghost"
db.cur.execute("INSERT INTO settings (userid, sno, valint) VALUES (0, 1, 0)")
db.conn.commit()
else:
self.vid = row[0]
# get vessel infos
if self.vid > 0:
db.cur.execute("SELECT vesselname, model FROM vessel WHERE vid=%s", (self.vid, ))
self.vessel = db.cur.fetchone()
print(self.vessel['vesselname'])
else:
self.vname = "Ghost(0)"
# get list of vessels
vessel = {}
db.dcur.execute("SELECT vid, vesselname, model FROM vessel ORDER BY vid")
for row in db.dcur.fetchall():
vessel[row['vid']] = row['vesselname'] + " - " + row['model']
combobox.load_from_dict(self.combo['vessel'], vessel, self.vid)
# load shape of vessel if available
if self.vid > 0:
db.cur.execute("SELECT shapefile FROM vessel WHERE vid=%s", (self.vid, ))
filename = db.cur.fetchone()[0]
handle = Rsvg.Handle()
self._svg = handle.new_from_file(os.path.join(cfg['shapedir'], filename))
#self._svg = handle.new_from_file(os.path.join(sys.path[0], cfg['shapedir'], "elizabethan29.svg"))
#viewport = Rsvg.Rectangle()
#viewport.x = 0
#viewport.y = 0
#viewport.width = 320
#viewport.height = 240
#self._svg.render_document(ctx, viewport)
# combos
roles = {'captain': 'Captain', 'helmsman': 'Helmsman', 'sailor': 'Sailor'}
combobox.load_from_dict(self.combo['user_role'], roles)
langs = {'de': 'Deutsch', 'en': 'English'}
combobox.load_from_dict(self.combo['user_lang'], langs)
# prepare lists
self.lst_user = Gtk.ListStore(int, str)
db.cur.execute("SELECT userid, login FROM user WHERE userid > 0 ORDER BY login")
for row in db.cur.fetchall():
self.lst_user.append(row)
treeview.add_column(self.tree['user'], 'ID', 0)
treeview.add_column(self.tree['user'], 'Login', 1)
self.tree['user'].set_model(self.lst_user)
# current selected objects
self._user = user.User(db.conn, 0)
print(self._user)
def run(self):
self.label['vessel'].set_text(self.vname)
self.window.show_all()
Gtk.main()
def user_display(self):
self.entry['user_login'].set_text(self._user.login)
self.entry['user_displayname'].set_text(self._user.displayname)
combobox.activate_row(self.combo['user_role'], self._user.role)
combobox.activate_row(self.combo['user_lang'], self._user.language)
self.check['user_locked'].set_active('locked' in self._user.flags)
self.check['user_deleted'].set_active('deleted' in self._user.flags)
self.entry['user_pass'].set_text('')
def on_tvw_user_row_activated(self, widget, path, column):
# load selected user
self._user.user_id = treeview.get_value(widget, 0)
self._user.load()
self.user_display()
def on_but_user_new_clicked(self, widget):
# get first free user id and create a new record with name "user<nnn>"
self._user.create()
self.lst_user.append((self._user.user_id, self._user.login))
def on_but_user_del_clicked(self, widget):
print("Delete user")
answer = gtkutils.questionbox(
_("Delete user!\n"
"Are you sure?"
), title="YMS")
if answer == False:
return
self._user.delete()
selection = self.tree['user'].get_selection()
model, tree_iter = selection.get_selected()
model.remove(tree_iter)
# TODO Select some other user from new list selection
self._user.user_id = treeview.get_value(self.tree['user'])
self._user.load()
self.user_display()
def on_but_user_newpass_clicked(self, widget):
self.entry['user_pass'].set_text(user.User.genpass())
# self.lst_user.
def on_but_user_save_clicked(self, widget):
newlogin = self.entry['user_login'].get_text()
# check if changed login exists and throw error in that case
if newlogin != self._user.login:
sql = "SELECT COUNT(login) FROM user WHERE login=%s"
db.cur.execute(sql, (newlogin, ))
if db.cur.fetchone()[0] > 0:
# TODO set error indicator
print("Error: Login already exists!")
return
sql = "UPDATE user SET login=%s WHERE userid=%s"
db.cur.execute(sql, (newlogin, self._user.user_id))
treeview.set_value(self.tree['user'], 1, newlogin)
self._user.displayname = self.entry['user_displayname'].get_text()
self._user.role = combobox.get_list_value(self.combo['user_role'])
self._user.language = combobox.get_list_value(self.combo['user_lang'])
self._user.flags = []
if self.check['user_locked'].get_active(): self._user.flags.append('locked')
if self.check['user_deleted'].get_active(): self._user.flags.append('deleted')
self._user.save()
# password field is different. if filled set new password
newpass = self.entry['user_pass'].get_text()
if len(newpass) >= 8:
try:
self._user.setpass(newpass)
finally:
newpass = None
self.entry['user_pass'].set_text('')
else:
# Password to short
pass
def on_but_info_clicked(self, widget):
# Userlist
sql = ("SELECT userid, login, displayname FROM user ORDER BY login")
db.cur.execute(sql)
for row in db.cur.fetchall():
print(row)
def on_stk_companies_notify(self, stack, param):
print("Stack notify")
print(stack, param)
def on_ntb_modules_switch_page(self, notebook, page, page_num):
print("Neue Seite:", page_num)
# TODO here re-load lists
def on_but_equip_detail_clicked(self, widget):
self.stack['equipment'].set_visible_child(self.box['equip_detail'])
def on_but_equip_edit_clicked(self, widget):
self.stack['equipment'].set_visible_child(self.box['equip_edit'])
def on_but_equip_save_clicked(self, widget):
pass
def on_but_equip_cancel_clicked(self, widget):
self.stack['equipment'].set_visible_child(self.box['equipment'])
def on_but_equip_cancel_clicked(self, widget):
self.stack['equipment'].set_visible_child(self.box['equipment'])
def on_destroy(self, widget, data=None):
Gtk.main_quit()
def create_config(cfgfile):
# create inital config file
with open(cfgfile, 'w') as fh:
fh.write('[DB]\n')
fh.write('host = {}\n'.format(cfg['host']))
fh.write('db = {}\n'.format(cfg['db']))
fh.write('user = {}\n'.format(cfg['user']))
fh.write('pass = changeme!\n\n')
fh.write('[documents]\n')
fh.write('basepath = {}\n'.format(cfg['docpath']))
# secure configuration
os.chmod(cfgfile, 0o600)
if __name__ == "__main__":
locale.setlocale(locale.LC_ALL, '')
gettext.bindtextdomain('ymsgui', 'locale')
gettext.textdomain('ymsgui')
_ = gettext.gettext
config = configparser.ConfigParser()
cfgfile = os.path.expanduser(cfg['cfgfile'])
if not config.read(cfgfile):
create_config(cfgfile)
gtkutils.messagebox(
_("Configuration file '{}' not found.\n"
"Default configuration has been created.\n"
"Please edit that file according to your needs.\n\n"
"Then restart this program."
).format(cfgfile), title="YMS")
else:
cfg['host'] = config.get('DB', 'host')
cfg['db'] = config.get('DB', 'db')
db = database.Database(cfg['host'], cfg['db'])
connected = False
if cfg['host'] == 'socket':
connected = db.connect_local()
else:
cfg['user'] = config.get('DB', 'user')
cfg['pass'] = config.get('DB', 'pass')
connected = db.connect(cfg['user'], cfg['pass'])
if connected:
app = Frontend()
app.run()
db.disconnect()
else:
errno, errtxt = db.last_error()
messagebox(_("Database connection cannot be established.\n\n"
"Server: %s\nDatabase: %s\n"
"Error: %s (%d)"
) % (cfg['host'], cfg['db'], errtxt, errno), title="YMS")
# Another fine product of the sirius cybernetics corporation