#!/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': 'webgui/shapes' } def get_share_dir(): program_dir = os.path.dirname(os.path.abspath(__file__)) source_ui = os.path.join(program_dir, "yms", "ui", "main.ui") if os.path.isfile(source_ui): return program_dir return os.path.join( os.path.dirname(os.path.dirname(program_dir)), "share", "yms" ) class Frontend(): def __init__(self): self.uipath = os.path.join(get_share_dir(), "yms", "ui") builder = Gtk.Builder() builder.add_from_file(os.path.join(self.uipath, "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 handle = Rsvg.Handle() 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" 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