#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2020 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) .
This application is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : / / www . gnu . org / licenses / > .
"""
import logging ; logger = logging . getLogger ( __name__ ) ; logger . info ( " import " )
#Standard Library
#Engine
import engine . api as api
#Third Party
from PyQt5 import QtCore , QtWidgets
class PromptWidget ( QtWidgets . QDialog ) :
wordlist = None
minlen = None
def __init__ ( self , parent ) :
super ( ) . __init__ ( parent )
layout = QtWidgets . QFormLayout ( )
#layout = QtWidgets.QVBoxLayout()
updateWordlist ( ) #this is a fast index update, we can call that every time to be sure. Otherwise newly added executable-PATHs will not be reflected in the dialog without a program-database update, which takes too long to be convenient.
self . setLayout ( layout )
self . setWindowFlag ( QtCore . Qt . Popup , True )
self . comboBox = QtWidgets . QComboBox ( self )
self . comboBox . setEditable ( True )
self . comboBox . currentTextChanged . connect ( self . check ) #not called when text is changed programatically
if PromptWidget . wordlist :
completer = QtWidgets . QCompleter ( PromptWidget . wordlist )
completer . setModelSorting ( QtWidgets . QCompleter . CaseInsensitivelySortedModel )
completer . setCaseSensitivity ( QtCore . Qt . CaseInsensitive )
completer . setCompletionMode ( QtWidgets . QCompleter . PopupCompletion )
completer . activated . connect ( self . process ) #To avoid double-press enter to select one we hook into the activated signal and trigger process
self . comboBox . setCompleter ( completer )
self . comboBox . setMinimumContentsLength ( PromptWidget . minlen )
labelString = QtCore . QCoreApplication . translate ( " PromptWidget " , " Type in the name of an executable file on your system. " )
else :
labelString = QtCore . QCoreApplication . translate ( " PromptWidget " , " No program database found. Please update through Control menu. " )
label = QtWidgets . QLabel ( labelString )
layout . addWidget ( label )
self . comboBox . setSizeAdjustPolicy ( QtWidgets . QComboBox . AdjustToMinimumContentsLength )
layout . addWidget ( self . comboBox )
errorString = QtCore . QCoreApplication . translate ( " PromptWidget " , " Command not found or not accepted!<br>Parameters, --switches and relative paths are not allowed.<br>Use nsm-proxy or write a starter-script instead. " )
errorString = " <i> " + errorString + " </i> "
self . errorLabel = QtWidgets . QLabel ( errorString )
layout . addWidget ( self . errorLabel )
self . errorLabel . hide ( ) #shown in process or check
self . buttonBox = QtWidgets . QDialogButtonBox ( QtWidgets . QDialogButtonBox . Ok | QtWidgets . QDialogButtonBox . Cancel )
self . buttonBox . accepted . connect ( self . process )
self . buttonBox . rejected . connect ( self . reject )
layout . addWidget ( self . buttonBox )
self . exec ( ) #blocks until the dialog gets closed
def abortHandler ( self ) :
pass
def check ( self , currentText ) :
""" Called every keypress.
We do preliminary error and collision checking here , so the engine does not have to throw
an error """
self . errorLabel . hide ( ) #start in good faith
ok = self . buttonBox . button ( QtWidgets . QDialogButtonBox . Ok )
if currentText in PromptWidget . wordlist : #this is taken literally. Any extra char or whitespace counts as false
ok . setEnabled ( True )
else :
ok . setEnabled ( False )
self . errorLabel . show ( )
def process ( self ) :
""" Careful! Calling this eats python errors without notice.
Make sure your objects exists and your syntax is correct """
assert PromptWidget . wordlist
assert PromptWidget . minlen
curText = self . comboBox . currentText ( )
if not curText or curText == " " : #TODO: qt weirdness. This is a case when focus is lost from a valid entry. The field is filled from a chosen value, from the list. But it says " ".
#Do not show the errorLabel. This is a qt bug or so.
return
if curText in PromptWidget . wordlist :
api . clientAdd ( curText )
logger . info ( f " Prompt accepted { curText } and will send it to clientAdd " )
self . done ( True )
else :
logger . info ( f " Prompt did not accept { curText } .Showing info to the user. " )
self . errorLabel . show ( )
def updateWordlist ( ) :
""" in case programs are installed while the session is running the user can
manually call a database update """
PromptWidget . wordlist = api . getUnfilteredExecutables ( )
if PromptWidget . wordlist :
PromptWidget . minlen = len ( max ( PromptWidget . wordlist , key = len ) )
else :
logger . error ( " Executable list came back empty! Most likely an error in application database build. Not trivial! " )
def askForExecutable ( parent ) :
PromptWidget ( parent )