def getSaveFileName(self, *args, **kwargs): """ analogue to QtWidgets.QFileDialog.getSaveFileNameAndFilter but returns the filename + chosen file ending even if not typed in gui """ if 'directory' not in kwargs: if self.opts['save']: if self.opts['save']: kwargs['directory'] = self.opts['save'] fname = QtWidgets.QFileDialog.getSaveFileName(**kwargs) if fname: if type(fname) == tuple: # only happened since qt5 # getSaveFileName returns (path, ftype) if not fname[0]: return p = PathStr(fname[0]) if not p.filetype(): ftyp = self._extractFtype(fname[1]) p = p.setFiletype(ftyp) else: p = PathStr(fname) self.opts['save'] = p.dirname() if self.opts['open'] is None: self.opts['open'] = self.opts['save'] return p
def getSaveFileName(self, *args, **kwargs): """ analogue to QtWidgets.QFileDialog.getSaveFileNameAndFilter but returns the filename + chosen file ending even if not typed in gui """ if 'directory' not in kwargs: if self.opts['save']: if self.opts['save']: kwargs['directory'] = self.opts['save'] fname = QtWidgets.QFileDialog.getSaveFileName(**kwargs) if fname: if type(fname) == tuple: #only happened since qt5 #getSaveFileName returns (path, ftype) if not fname[0]: return p = PathStr(fname[0]) if not p.filetype(): ftyp = self._extractFtype(fname[1]) p = p.setFiletype(ftyp) else: p = PathStr(fname) self.opts['save'] = p.dirname() if self.opts['open'] is None: self.opts['open'] = self.opts['save'] return p
def html2data(html): ''' extract either tables or images from html code ''' paths = [] # data = [] doc = lxml.html.fromstring(html) #images in html for img in doc.cssselect("img"): #doc.xpath('img'): # get the scr-path of the image: imgsrc = img.get('src') fname = PathStr(imgsrc).basename() if not hasattr(html2data, 'TMP_IMG_DIR'): html2data.TMP_IMG_DIR = PathStr(tempfile.mkdtemp('tmpImgDir')) fpath = html2data.TMP_IMG_DIR.join(fname) #in case img src is within HTML code: if not fname.filetype(): ftype = imgsrc[imgsrc.index('image/') + 6:imgsrc.index(';')] fpath = fpath.setFiletype(ftype) # download the image in a temporary folder: urllib.request.urlretrieve(imgsrc, fpath) paths.append(fpath) #TODO: doesnt work and table import from internet #...is not really needed # # tables # table = _html2PyTable(doc) # if table: # data.append(table) return paths #, data # def _html2PyTable(doc): # table = [] # try: # rows = doc.cssselect("tr") # # TODO: # except: # lxml.cccselect uses __import__ which doesnt work with pyinstaller # print('dynamic import error in lxml.cccselect') # return [] # for row in rows: # table.append(list()) # for td in row.cssselect("td"): # table[-1].append(str(td.text_content())) # return table
class Session(QtCore.QObject): """Session management to be accessible in QtWidgets.QApplication.instance().session * extract the opened (as pyz-zipped) session in a temp folder * create 2nd temp-folder for sessions to be saved * send a close signal to all child structures when exit * write a log file with all output * enable icons in menus of gnome-sessions [linux only] * gives option of debug mode """ # sigPathChanged = QtCore.Signal(object) #path sigSave = QtCore.Signal(object) # state dict sigRestore = QtCore.Signal(object) # state dict def __init__(self, args, **kwargs): """ Args: first_start_dialog (Optional[bool]): Show a different dialog for the first start. name (Optional[str]): The applications name. type (Optional[str]): The file type to be used for saving sessions. icon (Optional[str]): Path to the application icon. """ QtCore.QObject.__init__(self) # SESSION CONSTANTS: self.NAME = kwargs.get('name', __main__.__name__) self.FTYPE = kwargs.get('ftype', 'pyz') self.ICON = kwargs.get('icon', None) # hidden app-preferences folder: self.dir = PathStr.home().mkdir('.%s' % self.NAME) self.APP_CONFIG_FILE = self.dir.join('config.txt') self._tmp_dir_session = None # session specific options: self.opts = _Opts({ 'maxSessions': 3, 'enableGuiIcons': True, 'writeToShell': True, 'createLog': False, 'debugMode': False, 'autosave': False, 'autosaveIntervalMin': 15, 'server': False, }, self) # global options - same for all new and restored sessions: self.app_opts = {'showCloseDialog': True, 'recent sessions': []} if not self.APP_CONFIG_FILE.exists(): # allow different first start dialog: dialog = kwargs.get('first_start_dialog', FirstStart) f = dialog(self) f.exec_() if not f.result(): sys.exit() # create the config file with open(self.APP_CONFIG_FILE, 'w') as f: pass else: with open(self.APP_CONFIG_FILE, 'r') as f: r = f.read() if r: self.app_opts.update(eval(r)) self._icons_enabled = False self.log_file = None dirname = self.app_opts['recent sessions'] if dirname: dirname = PathStr(dirname[-1]).dirname() self.dialogs = Dialogs(dirname) self.saveThread = _SaveThread() self._createdAutosaveFile = None self.tmp_dir_save_session = None # a work-dir for temp. storage: # self.tmp_dir_work = PathStr(tempfile.mkdtemp('%s_work' % self.NAME)) pathName = self._inspectArguments(args) self.setSessionPath(pathName) if self.opts['createLog']: self._setupLogFile() # create connectable stdout and stderr signal: self.streamOut = StreamSignal('out') self.streamErr = StreamSignal('err') self._enableGuiIcons() # Auto-save timer: self.timerAutosave = QtCore.QTimer() self.timerAutosave.timeout.connect(self._autoSave) self.opts.activate() # first thing to do after start: QtCore.QTimer.singleShot(0, self.restoreCurrentState) def setSessionPath(self, path, statename=None): if path: # and path.endswith('.%s' %self.FTYPE): # this script was opened out from a zip-container (named as # '*.pyz') self.path = PathStr(path) self.dir = self.path.dirname().abspath() # extract the zip temporally ZipFile(self.path, 'r').extractall(path=self.tmp_dir_session) self.n_sessions = len(self.stateNames()) # SET STATE snames = self.stateNames() if statename is None: # last one self.current_session = snames[-1] elif statename in snames: self.current_session = statename else: raise Exception( "state '%s' not in saved states %s" % (statename, snames)) else: self.path = None self.n_sessions = 0 self.current_session = None def writeLog(self, write=True): if not self.log_file: return so = self.streamOut.message se = self.streamErr.message w = self.log_file.write if write: try: # ensure only connected once so.disconnect(w) se.disconnect(w) except TypeError: pass so.connect(w) se.connect(w) else: try: so.disconnect(w) se.disconnect(w) except TypeError: pass def _enableGuiIcons(self): # enable icons in all QMenuBars only for this program if generally # disabled if self.opts['enableGuiIcons']: if os.name == 'posix': # linux this_env = str(os.environ.get('DESKTOP_SESSION')) relevant_env = ( 'gnome', 'gnome-shell', 'ubuntustudio', 'xubuntu') if this_env in relevant_env: if 'false' in os.popen( # if the menu-icons on the gnome-desktop are disabled 'gconftool-2 --get /desktop/gnome/interface/menus_have_icons').read(): print('enable menu-icons') os.system( 'gconftool-2 --type Boolean --set /desktop/gnome/interface/menus_have_icons True') self._icons_enabled = True def _setupLogFile(self): lfile = self.tmp_dir_session.join('log.txt') if lfile.exists(): self.log_file = open(lfile, 'a') else: self.log_file = open(lfile, 'w') self.log_file.write(''' #################################### New run at %s #################################### ''' % strftime("%d.%m.%Y|%H:%M:%S", gmtime())) def checkMaxSessions(self, nMax=None): """ check whether max. number of saved sessions is reached if: remove the oldest session """ if nMax is None: nMax = self.opts['maxSessions'] l = self.stateNames() if len(l) > nMax: for f in l[:len(l) - nMax]: self.tmp_dir_session.remove(str(f)) def stateNames(self): """Returns: list: the names of all saved sessions """ if self.current_session: s = self.tmp_dir_session l = s.listdir() l = [x for x in l if s.join(x).isdir()] naturalSorting(l) else: l=[] # bring autosave to first position: if 'autoSave' in l: l.remove('autoSave') l.insert(0, 'autoSave') return l def restorePreviousState(self): s = self.stateNames() if s: i = s.index(self.current_session) if i > 1: self.current_session = s[i - 1] self.restoreCurrentState() def restoreNextState(self): s = self.stateNames() if s: i = s.index(self.current_session) if i < len(s) - 1: self.current_session = s[i + 1] self.restoreCurrentState() def restoreStateName(self, name): """restore the state of given [name]""" self.current_session = name self.restoreCurrentState() def renameState(self, oldStateName, newStateName): s = self.tmp_dir_session.join(oldStateName) s.rename(newStateName) if self.current_session == oldStateName: self.current_session = newStateName print("==> State [%s] renamed to [%s]" % (oldStateName, newStateName)) def _recusiveReplacePlaceholderWithArray(self, state, arrays): def recursive(state): for key, val in list(state.items()): if isinstance(val, dict): recursive(val) elif isinstance(val, str) and val.startswith('arr_'): state[key] = arrays[val] recursive(state) def restoreCurrentState(self): if self.current_session: orig = self.tmp_dir_save_session path = self.tmp_dir_save_session = self.tmp_dir_session.join( self.current_session) with open(path.join('state.pickle'), "rb") as f: state = pickle.load(f) p = path.join('arrays.npz') if p.exists(): arrays = np.load(path.join('arrays.npz')) self._recusiveReplacePlaceholderWithArray(state, arrays) self.dialogs.restoreState(state['dialogs']) self.opts.update(state['session']) self.sigRestore.emit(state) self.tmp_dir_save_session = orig print( "==> State [%s] restored from '%s'" % (self.current_session, self.path)) def addSession(self): self.current_session = self.n_sessions self.n_sessions += 1 self.tmp_dir_save_session = self.tmp_dir_session.join( str(self.n_sessions)).mkdir() self.checkMaxSessions() def quit(self): print('exiting...') # RESET ICONS if self._icons_enabled: print('disable menu-icons') os.system( # restore the standard-setting for seeing icons in the menus 'gconftool-2 --type Boolean --set /desktop/gnome/interface/menus_have_icons False') # WAIT FOR PROMT IF IN DEBUG MODE if self.opts['debugMode']: input("Press any key to end the session...") # REMOVE TEMP FOLDERS try: self.tmp_dir_session.remove() # self.tmp_dir_work.remove() except OSError: pass # in case the folders are used by another process with open(self.APP_CONFIG_FILE, 'w') as f: f.write(str(self.app_opts)) # CLOSE LOG FILE if self.log_file: self.writeLog(False) self.log_file.close() def _inspectArguments(self, args): """inspect the command-line-args and give them to appBase""" if args: self.exec_path = PathStr(args[0]) else: self.exec_path = None session_name = None args = args[1:] openSession = False for arg in args: if arg in ('-h', '--help'): self._showHelp() elif arg in ('-d', '--debug'): print('RUNNGING IN DEBUG-MODE') self.opts['debugMode'] = True elif arg in ('-l', '--log'): print('CREATE LOG') self.opts['createLog'] = True elif arg in ('-s', '--server'): self.opts['server'] = True elif arg in ('-o', '--open'): openSession = True elif openSession: session_name = arg else: print("Argument '%s' not known." % arg) return self._showHelp() return session_name def _showHelp(self): sys.exit(''' %s-sessions can started with the following arguments: [-h or --help] - show the help-page [-d or --debug] - run in debugging-mode [-l or --log] - create log file [-n or --new] - start a new session, don'l load saved properties [-exec [cmd]] - execute python code from this script/executable ''' % self.__class__.__name__) def save(self): """save the current session override, if session was saved earlier""" if self.path: self._saveState(self.path) else: self.saveAs() def saveAs(self, filename=None): if filename is None: # ask for filename: filename = self.dialogs.getSaveFileName(filter="*.%s" % self.FTYPE) if filename: self.path = filename self._saveState(self.path) if self._createdAutosaveFile: self._createdAutosaveFile.remove() print( "removed automatically created '%s'" % self._createdAutosaveFile) self._createdAutosaveFile = None def replace(self, path): """ replace current session with one given by file path """ self.setSessionPath(path) self.restoreCurrentState() def open(self): """open a session to define in a dialog in an extra window""" filename = self.dialogs.getOpenFileName(filter="*.%s" % self.FTYPE) if filename: self.new(filename) def new(self, filename=None): """start a session an independent process""" path = (self.exec_path,) if self.exec_path.filetype() in ('py', 'pyw', 'pyz', self.FTYPE): # get the absolute path to the python-executable p = find_executable("python") path = (p, 'python') + path else: # if run in frozen env (.exe): # first arg if execpath of the next session: path += (self.exec_path,) if filename: path += ('-o', filename) os.spawnl(os.P_NOWAIT, *path) def registerMainWindow(self, win): win.setWindowIcon(QtGui.QIcon(self.ICON)) self._mainWindow = win win.show = self._showMainWindow win.hide = self._hideMainWindow if self.opts['server']: server_ = Server(win) win.hide() else: win.show() @property def tmp_dir_session(self): #only create folder if needed if self._tmp_dir_session is None: # make temp-dir # the directory where the content of the *pyz-file will be copied: self._tmp_dir_session = PathStr( tempfile.mkdtemp( '%s_session' % self.NAME)) return self._tmp_dir_session def _showMainWindow(self): try: # restore autosave del self._autosave except AttributeError: pass self._mainWindow.__class__.show(self._mainWindow) def _hideMainWindow(self): # disable autosave on hidden window self._autosave = self.opts['autosave'] self.opts['autosave'] = False self._mainWindow.__class__.hide(self._mainWindow) def _saveState(self, path): """save current state and add a new state""" self.addSession() # next session self._save(str(self.n_sessions), path) def _autoSave(self): """save state into 'autosave' """ a = 'autoSave' path = self.path if not path: path = self.dir.join('%s.%s' % (a, self.FTYPE)) self._createdAutosaveFile = path self.tmp_dir_save_session = self.tmp_dir_session.join(a).mkdir() self._save(a, path) def blockingSave(self, path): """ saved session to file - returns after finish only called by interactiveTutorial-save at the moment """ self.tmp_dir_save_session = self.tmp_dir_session.join('block').mkdir() state = {'session': dict(self.opts), 'dialogs': self.dialogs.saveState()} self.saveThread.prepare('0', path, self.tmp_dir_session, state) self.sigSave.emit(self) self.saveThread.run() def _save(self, stateName, path): """save into 'stateName' to pyz-path""" print('saving...') state = {'session': dict(self.opts), 'dialogs': self.dialogs.saveState()} self.sigSave.emit(state) self.saveThread.prepare(stateName, path, self.tmp_dir_session, state) self.saveThread.start() self.current_session = stateName r = self.app_opts['recent sessions'] try: # is this session already exists: remove it r.pop(r.index(path)) except ValueError: pass # add this session at the beginning r.insert(0, path)