#!/usr/bin/env python3
import sys
import os
from urllib.request import Request,urlopen,urlretrieve
from pathlib import Path
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QPushButton,QVBoxLayout,\
QDialog,QGridLayout,QHBoxLayout,QFormLayout,QLineEdit,QComboBox,\
QStatusBar,QFileDialog,QDialogButtonBox,QScrollBar,QScrollArea,QListWidget,\
QListWidgetItem,QStackedWidget,QButtonGroup,QComboBox,QTableWidget,QTableWidgetItem,\
QHeaderView,QMessageBox
from PyQt5 import QtGui
from PyQt5.QtCore import QSize,pyqtSlot,Qt, QPropertyAnimation,QThread,QRect,QTimer,pyqtSignal,QSignalMapper,QProcess,QEvent
from edupals.ui import QAnimatedStatusBar
from appconfig.appConfig import appConfig
import gettext
try:
confText=gettext.translation("python3-appconfig")
_ = confText.gettext
except:
gettext.textdomain('python3-appconfig')
_ = gettext.gettext
QString=type("")
QInt=type(0)
BTN_MENU_SIZE=24
class appConfigScreen(QWidget):
keybind_signal=pyqtSignal("PyQt_PyObject")
update_signal=pyqtSignal("PyQt_PyObject")
def __init__(self,appName,parms={}):
super().__init__()
self.dbg=False
self.level='user'
exePath=sys.argv[0]
if os.path.islink(sys.argv[0]):
exePath=os.path.realpath(sys.argv[0])
baseDir=os.path.abspath(os.path.dirname(exePath))
os.chdir(baseDir)
self.rsrc="%s/rsrc"%baseDir
self.parms=parms
self.modules=[]
self.appName=appName
self.wikiPage=appName
self.background="%s/background.png"%self.rsrc
self.banner="%s/%s"%(self.rsrc,"banner.png")
self.last_index=0
self.stacks={0:{'name':_("Options"),'icon':'icon'}}
self.appConfig=appConfig()
self.config={}
self.textDomain=self.appName.lower().replace(" ","_")
#def init
def _debug(self,msg):
if self.dbg:
print("%s"%msg)
#def _debug
def setWiki(self,url):
self.wikiPage=url
#def setWiki
def setTextDomain(self,textDomain):
self.textDomain=textDomain
#def setTextDomain
def setRsrcPath(self,rsrc):
if os.path.isdir(rsrc):
self.rsrc=rsrc
else:
self._debug("%s doesn't exists")
self._debug("Resources dir: %s"%self.rsrc)
#def setRsrcPath
def setIcon(self,icon):
self._debug("Icon: %s"%icon)
icn=icon
if not os.path.isfile(icon):
sw_ko=False
self._debug("%s not found"%icon)
if QtGui.QIcon.fromTheme(icon):
icn=QtGui.QIcon.fromTheme(icon)
if icn.name()=="":
self._debug("%s not present at theme"%icon)
sw_ko=True
else:
self._debug("%s found at theme"%icon)
self._debug("Name: %s found at theme"%icn.name())
elif os.path.isfile("%s/%s"%(self.rsrc,icon)):
icon="%s/%s"%(self.rsrc,icon)
self._debug("%s found at rsrc folder"%icon)
icn=QtGui.QIcon(icon)
else:
icn=QtGui.QIcon.fromTheme("application-menu")
self._debug("Icon not found at %s"%self.rsrc)
if sw_ko:
icn=QtGui.QIcon.fromTheme("application-menu")
self._debug("Icon %s not found at theme"%icon)
self.setWindowIcon(icn)
#def setIcon
def setBanner(self,banner):
if not os.path.isfile(banner):
if os.path.isfile("%s/%s"%(self.rsrc,banner)):
banner="%s/%s"%(self.rsrc,banner)
else:
banner=""
self._debug("Banner not found at %s"%self.rsrc)
self.banner=banner
#def setBanner
def setBackgroundImage(self,background):
if not os.path.isfile(background):
if os.path.isfile("%s/%s"%(self.rsrc,background)):
background="%s/%s"%(self.rsrc,background)
else:
background=""
self._debug("Background not found at %s"%self.rsrc)
self.background=background
#def setBanner
def setConfig(self,confDirs,confFile):
self.appConfig.set_baseDirs(confDirs)
self.appConfig.set_configFile(confFile)
#def setConfig(self,confDirs,confFile):
def _searchWiki(self):
url=""
baseUrl="https://wiki.edu.gva.es/lliurex/tiki-index.php?page="
if self.wikiPage.startswith("http"):
url=self.wikiPage
else:
url="%s%s"%(baseUrl,self.wikiPage)
#try:
# req=Request(url)
# content=urlopen(req).read()
#except:
# self._debug("Wiki not found at %s"%url)
# url=""
return(url)
#def _searchWiki
def _get_default_config(self):
data={}
data=self.appConfig.getConfig('system')
self.level=data['system'].get('config','user')
if self.level!='system':
data=self.appConfig.getConfig(self.level)
level=data[self.level].get('config','n4d')
if level!=self.level:
self.level=level
data=self.appConfig.getConfig(level)
data[self.level]['config']=self.level
self._debug("Read level from config: %s"%self.level)
return (data)
#def _get_default_config(self,level):
def getConfig(self,level=None,exclude=[]):
data=self._get_default_config()
if not level:
level=self.level
if level!='system':
data={}
data=self.appConfig.getConfig(level,exclude)
self.config=data.copy()
self._debug("Read level from config: %s"%level)
return (data)
#def getConfig(self,level):
def Show(self):
if self.config=={}:
self.getConfig()
self.setStyleSheet(self._define_css())
if os.path.isdir("stacks"):
for mod in os.listdir("stacks"):
if mod.endswith(".py"):
mod_name=mod.split(".")[0]
mod_import="from stacks.%s import *"%mod_name
try:
exec(mod_import)
self.modules.append(mod_name)
self._debug("Load stack %s"%mod_name)
except Exception as e:
# self._debug("Unable to load %s: %s"%(mod_name,e))
print("Unable to load %s: %s"%(mod_name,e))
idx=1
for mod_name in self.modules:
try:
mod=eval("%s(self)"%mod_name)
except Exception as e:
# self._debug("Import failed for %s: %s"%(mod_name,e))
print("Import failed for %s: %s"%(mod_name,e))
continue
if type(mod.index)==type(0):
if mod.index>0:
idx=mod.index
try:
if mod.enabled==False:
continue
except:
pass
while idx in self.stacks.keys():
idx+=1
self._debug("New idx for %s: %s"%(mod_name,idx))
if 'parm' in mod.__dict__.keys():
try:
if mod.parm:
self._debug("Setting parms for %s"%mod_name)
self._debug("self.parms['%s']"%mod.parm)
mod.apply_parms(eval("self.parms['%s']"%mod.parm))
except Exception as e:
self._debug("Failed to pass parm %s to %s: %s"%(mod.parm,mod_name,e))
try:
mod.setTextDomain(self.textDomain)
except Exception as e:
print("Can't set textdomain for %s: %s"%(mod_name,e))
try:
mod.setAppConfig(self.appConfig)
except Exception as e:
print("Can't set appConfig for %s: %s"%(mod_name,e))
try:
if mod.visible==False:
visible=False
else:
visible=True
except:
visible=True
self.stacks[idx]={'name':mod.description,'icon':mod.icon,'tooltip':mod.tooltip,'module':mod,'visible':visible}
try:
mod.message.connect(self._show_message)
except:
pass
self._render_gui()
return(False)
def _render_gui(self):
self.getConfig()
box=QGridLayout()
img_banner=QLabel()
if os.path.isfile(self.banner):
img=QtGui.QPixmap(self.banner)
img_banner.setPixmap(img)
img_banner.setAlignment(Qt.AlignCenter)
img_banner.setObjectName("banner")
self.statusBar=QAnimatedStatusBar.QAnimatedStatusBar()
self.lst_options=QListWidget()
self.stk_widget=QStackedWidget()
box.addWidget(self.statusBar,0,0,1,2)
box.addWidget(img_banner,0,0,1,2)
l_panel=self._left_panel()
box.addWidget(l_panel,1,0,1,1,Qt.Alignment(1))
r_panel=self._right_panel()
box.addWidget(r_panel,1,1,1,1)
self.setLayout(box)
self.stk_widget.setCurrentIndex(0)
self.show()
#def _render_gui
def _left_panel(self):
panel=QWidget()
box=QVBoxLayout()
btn_menu=QPushButton()
icn=QtGui.QIcon.fromTheme("application-menu")
btn_menu.setIcon(icn)
btn_menu.setIconSize(QSize(BTN_MENU_SIZE,BTN_MENU_SIZE))
btn_menu.setMaximumWidth(BTN_MENU_SIZE)
btn_menu.setMaximumHeight(BTN_MENU_SIZE)
btn_menu.setToolTip(_("Options"))
btn_menu.setObjectName("menuButton")
# box.addWidget(btn_menu,Qt.Alignment(1))
indexes=[]
for index,option in self.stacks.items():
idx=index
lst_widget=QListWidgetItem()
lst_widget.setText(option['name'])
mod=option.get('module',None)
if mod:
try:
idx=mod.index
except:
pass
if idx>0:
icn=QtGui.QIcon.fromTheme(option['icon'])
lst_widget.setIcon(icn)
if 'tooltip' in option.keys():
lst_widget.setToolTip(option['tooltip'])
while idx in indexes:
idx+=1
indexes.append(index)
self.stacks[idx]['widget' ]=lst_widget
orderedStacks={}
orderedStacks[0]=self.stacks[0]
self.lst_options.addItem(orderedStacks[0]['widget'])
cont=1
indexes.sort()
for index in indexes:
if index:
orderedStacks[cont]=self.stacks[index].copy()
if self.stacks[index].get('visible',True)==True:
self.lst_options.addItem(orderedStacks[cont]['widget'])
cont+=1
self.stacks=orderedStacks.copy()
box.addWidget(self.lst_options)
self.lst_options.itemClicked.connect(self._show_stack)
panel.setLayout(box)
self.resize(self.size().width()+box.sizeHint().width(),self.size().height()+box.sizeHint().height()/2)
return(panel)
#def _left_panel
def _right_panel(self):
panel=QWidget()
box=QVBoxLayout()
idx=0
text=[
_("Welcome to the configuration of ")+self.appName,
_("From here you can:
")]
orderIdx=list(self.stacks.keys())
for idx in orderIdx:
data=self.stacks[idx]
stack=data.get('module',None)
if stack:
stack.setLevel(self.level)
stack.setConfig(self.config)
stack._load_screen()
text.append(" * %s"%(idx,stack.menu_description))
try:
self.stk_widget.insertWidget(idx,stack)
except:
self.stk_widget.insertWidget(idx,stack.init_stack())
stack=QWidget()
stack.setObjectName("panel")
s_box=QVBoxLayout()
lbl_txt=QLabel()
lbl_txt.setTextFormat(Qt.RichText)
lbl_txt.setText("
".join(text))
lbl_txt.linkActivated.connect(self._linkStack)
lbl_txt.setObjectName("desc")
lbl_txt.setAlignment(Qt.AlignTop)
lbl_txt.setTextInteractionFlags(Qt.TextBrowserInteraction)
s_box.addWidget(lbl_txt,1)
#Get wiki page
url=self._searchWiki()
if url:
desc=_("Wiki help")
lbl_wiki=QLabel("%s"%(url,desc))
lbl_wiki.setOpenExternalLinks(True)
s_box.addWidget(lbl_wiki,0,Qt.AlignRight)
stack.setLayout(s_box)
self.stk_widget.insertWidget(0,stack)
self.stacks[0]['module']=stack
box.addWidget(self.stk_widget)
panel.setLayout(box)
return(panel)
#def _right_panel
def _linkStack(self,*args):
stack=args[0].split('/')[-1]
self.gotoStack(int(stack),'')
def gotoStack(self,idx,parms):
self._show_stack(idx=idx,parms=parms)
def _show_stack(self,item=None,idx=None,parms=None):
if ((self.last_index==self.lst_options.currentRow()) and (idx==self.last_index or idx==None)):
return
try:
if self.stacks[self.last_index]['module'].getChanges():
if self._save_changes(self.stacks[self.last_index]['module'])==QMessageBox.Cancel:
self.lst_options.setCurrentRow(self.last_index)
return
else:
self.stacks[self.last_index]['module'].setChanged("",False)
self.stacks[self.last_index]['module'].initScreen()
if self.stacks[self.last_index]['module'].refresh:
self._debug("Refresh config")
self.getConfig()
except Exception as e:
print(e)
if idx==None:
idx=self.lst_options.currentRow()
self.last_index=idx
try:
self.stacks[idx]['module'].setConfig(self.config)
except:
pass
self.stk_widget.setCurrentIndex(idx)
self.statusBar.hide()
if parms:
self.stacks[idx]['module'].setParms(parms)
#def _show_stack
def closeEvent(self,event):
try:
if self.stacks[self.last_index]['module'].getChanges():
if self._save_changes(self.stacks[self.last_index]['module'])==QMessageBox.Cancel:
event.ignore()
except:
pass
def _show_message(self,msg,status=None):
self.statusBar.setText(msg)
# if status:
# self.statusBar.show(status)
# else:
self.statusBar.show(status)
#def _show_message
def _save_changes(self,module):
dia=QMessageBox(QMessageBox.Question,_("Apply changes"),_("There're changes not saved at current screen.\nDiscard them and continue?"),QMessageBox.Discard|QMessageBox.Cancel,self)
return(dia.exec_())
def _define_css(self):
css="""
QPushButton{
padding: 6px;
margin:6px;
font: 14px Roboto;
}
QPushButton#menu:active{
background:none;
}
QStatusBar{
background:red;
color:white;
font: 14px Roboto bold;
}
QLabel{
padding:6px;
margin:6px;
}
#dlgLabel{
font:12px Roboto;
margin:0px;
border:0px;
padding:3px;
}
QLineEdit{
border:0px;
border-bottom:1px solid grey;
padding:1px;
font:14px Roboto;
margin-right:6px;
}
#panel{
background-image:url("%s");
background-size:stretch;
background-repeat:no-repeat;
background-position:center;
}
#desc{
background-color:rgba(255,255,255,0.7);
color:black;
}
#banner{
padding:1px;
margin:1px;
border:0px;
}
"""%self.background
return(css)
#def _define_css