Commit 31fb7b8a authored by Michael Rudolf's avatar Michael Rudolf
Browse files

Bug fixes:

 - VST plots are now saved with the correct file extension.
 - Fixed a bug that occured when trying to set the icon of the app.
Known issues:
 - The scripts for tdms files in 'FileConversion' are outdated.
parent 20a0610e
......@@ -788,9 +788,9 @@ def plotVST(path, data, pfit, perr, name_fit):
edgecolor='k',
loc='upper right',
framealpha=0.5)
fig.suptitle(data['name'], fontsize=14, y=0.95)
plt.savefig(path + data['name'],
fname = os.path.splitext(data['name'])[0]
fig.suptitle(fname, fontsize=14, y=0.95)
plt.savefig(os.path.join(path, fname),
bbox_inches='tight',
edgecolor='w')
plt.close()
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: M. Rudolf, M. Warsitzka
# @Date: 2019-02-20 12:00:00
# @Last Modified by: M. Rudolf
# @Last Modified time: 2021-01-22 13:46:43
"""
RST_pick_GUI.py
GUI based approach for RSTpicking
"""
import configparser
import json
import logging
import os
import re
import subprocess
import sys
import tkinter as tk
import warnings
from pathlib import Path
from tkinter import filedialog, messagebox
import RSTpicking.RST_Func as rfnc
warnings.filterwarnings("ignore")
log = logging.getLogger()
log.setLevel(logging.INFO)
class RST_pick_GUI(tk.Tk):
"""Main Window for the RST Picking"""
def __init__(self, parent):
tk.Tk.__init__(self, parent)
# == VARIABLES
self.parent = parent
self.projectname = tk.StringVar()
self.path_in = tk.StringVar()
self.path_out = tk.StringVar()
self.path_cfg = tk.StringVar()
self.proc = tk.StringVar()
self.plot_ts = tk.IntVar()
self.save_ts = tk.IntVar()
self.rev_pick = tk.IntVar()
self.is_VST = tk.IntVar()
self.pred_norm = tk.IntVar()
self.rst = tk.IntVar()
self.cfg = configparser.ConfigParser()
self.cfg.optionxform = str
self.path_cfg.set('rst_evaluation_')
self.params = dict()
self.protocol('WM_DELETE_WINDOW', self.closing_menu)
# Initialize Object
self.initalize()
def initalize(self):
# Read or create config and set entries from config
self.refresh_entries_from_cfg(init=True)
# == WIDGETS and INTERFACE ==
self.grid()
# Project name
self.label_projname = tk.Label(self,
text='Project:',
anchor='e',
width=20)
self.label_projname.grid(column=0, row=0, sticky='EW')
self.entry_projname = tk.Entry(self,
textvariable=self.projectname)
self.entry_projname.grid(column=1, row=0, sticky='EW')
# Input path
self.label_input = tk.Label(self,
text='Input Folder:',
anchor='e')
self.label_input.grid(column=0, row=1, sticky='EW')
self.entry_input = tk.Entry(self,
width=80,
textvariable=self.path_in)
self.entry_input.grid(column=1, row=1, sticky='EW')
self.button_input = tk.Button(self,
text='...')
self.button_input.grid(column=2, row=1)
self.button_input.bind('<ButtonRelease-1>', self.folder_browser)
# Output path
self.label_output = tk.Label(self,
text='Output Folder:',
anchor='e')
self.label_output.grid(column=0, row=2, sticky='EW')
self.entry_output = tk.Entry(self,
textvariable=self.path_out)
self.entry_output.grid(column=1, row=2, sticky='EW')
self.button_output = tk.Button(self,
text='...')
self.button_output.grid(column=2, row=2)
self.button_output.bind('<ButtonRelease-1>', self.folder_browser)
# Options
self.label_options = tk.Label(self,
text='Options:',
anchor='w',
font=('Helvetica', 12, 'bold'))
self.label_options.grid(column=0, row=3, columnspan=3, sticky='EW')
# Pick Data
self.opt_pick = tk.Checkbutton(self,
text='Automatic Picking',
anchor='w',
variable=self.rst)
self.opt_pick.grid(column=0, row=4, sticky='EW')
# Save Data
self.opt_save = tk.Checkbutton(self,
text='Save Data',
anchor='w',
variable=self.save_ts)
self.opt_save.grid(column=0, row=5, sticky='EW')
# Plot Data
self.opt_plot = tk.Checkbutton(self,
text='Plot time series',
anchor='w',
variable=self.plot_ts)
self.opt_plot.grid(column=0, row=6, sticky='EW')
# Review Picking
self.opt_revpick = tk.Checkbutton(self,
text='Review picking during run',
anchor='w',
variable=self.rev_pick)
self.opt_revpick.grid(column=1, row=4, sticky='EW')
# Use predefined normal stress
self.opt_pred = tk.Checkbutton(self,
text='Use predefined normal stress',
anchor='w',
variable=self.pred_norm)
self.opt_pred.grid(column=1, row=5, sticky='EW')
# Is a VST-Test
self.opt_revpick = tk.Checkbutton(self,
text='VST-Test',
anchor='w',
variable=self.is_VST)
self.opt_revpick.grid(column=1, row=6, sticky='EW')
# More Options
self.button_options = tk.Button(self,
text='More options...')
self.button_options.grid(column=1, row=7, sticky='w')
self.button_options.bind('<ButtonRelease-1>', self.more_options)
# Start processing
self.button_start = tk.Button(self,
text='Start Processing',
padx=20)
self.button_start.grid(column=0, row=7, columnspan=3, sticky='w')
self.button_start.bind('<ButtonRelease-1>', self.start_processing)
self.make_dpi_aware()
def get_HWND_dpi(self):
"""Detects high dpi displays and rescales gui in Windows
Adapted from the user 'dingles at stack-overflow"""
if os.name == "nt":
from ctypes import pointer, windll, wintypes
try:
windll.shcore.SetProcessDpiAwareness(1)
except Exception as _:
logging.info('High-DPI scaling failed')
pass # this will fail on Win Server and maybe early Windows
DPI100pc = 96 # DPI 96 is 100% scaling
DPI_type = 0
# MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2
winH = wintypes.HWND(self.winfo_id())
monitorhandle = windll.user32.MonitorFromWindow(winH,
wintypes.DWORD(2))
# MONITOR_DEFAULTTONEAREST = 2
X = wintypes.UINT()
Y = wintypes.UINT()
try:
windll.shcore.GetDpiForMonitor(monitorhandle,
DPI_type,
pointer(X),
pointer(Y))
return X.value, Y.value, (X.value + Y.value) / (2 * DPI100pc)
except Exception:
logging.info('Assuming standard dpi scaling.')
return 96, 96, 1 # Assume standard Windows DPI & scaling
else:
return None, None, 1 # What to do for other OSs?
def make_dpi_aware(self):
"""Adapted from the user 'dingles at stack-overflow"""
def TkGeometryScale(s, cvtfunc):
# format "WxH+X+Y"
patt = r"(?P<W>\d+)x(?P<H>\d+)\+(?P<X>\d+)\+(?P<Y>\d+)"
R = re.compile(patt).search(s)
G = str(cvtfunc(R.group("W"))) + "x"
G += str(cvtfunc(R.group("H"))) + "+"
G += str(cvtfunc(R.group("X"))) + "+"
G += str(cvtfunc(R.group("Y")))
return G
self.DPI_X, self.DPI_Y, self.DPI_scaling = self.get_HWND_dpi()
self.TkScale = lambda v: int(float(v) * self.DPI_scaling)
self.TkGeometryScale = lambda s: TkGeometryScale(s, self.TkScale)
def resize_all_elements(self):
"""
Resizes all elements according to the DPI scaling of the window
(WIP)
-----
"""
pass
def plot_window(self, event):
"""Shows a plot window"""
plot_win = tk.Toplevel(self)
plot_win.wm_title('Plot Window')
return
# == CALLBACKS ==
def folder_browser(self, event):
"""Callback for folderbrowser."""
f_path = filedialog.askdirectory()
if event.widget._name == '!button': # Input path
self.entry_input.delete(0, 'end')
self.entry_input.insert(0, f_path + '/')
self.entry_output.delete(0, 'end')
self.entry_output.insert(0, f_path + '/')
self.entry_projname.delete(0, 'end')
pname = f_path.split('/')[-1]
self.entry_projname.insert(0, pname)
self.path_cfg.set(f_path + '/' + pname + '.cfg')
self.refresh_entries_from_cfg(path=f_path + '/' + pname + '.cfg')
elif event.widget._name == '!button2': # Output path
self.entry_output.delete(0, 'end')
self.entry_output.insert(0, f_path + '/')
else:
print(event.widget._name)
def start_processing(self, event):
"""Initiates Processing of data"""
# Create Var dictionary and save to config:
Vars = dict()
for item in self.params:
try:
Vars[item] = float(self.params[item].get())
except ValueError as _:
Vars[item] = json.loads(self.params[item].get())
self.cfg['parameters'][item] = self.params[item].get()
self.cfg['options']['plot_ts'] = str(self.plot_ts.get())
self.cfg['options']['save_ts'] = str(self.save_ts.get())
self.cfg['options']['rst'] = str(self.rst.get())
self.cfg['options']['rev_pick'] = str(self.rev_pick.get())
self.cfg['options']['is_VST'] = str(self.is_VST.get())
self.cfg['options']['pred_norm'] = str(self.pred_norm.get())
Vars['options'] = dict()
for opt in self.cfg['options'].keys():
Vars['options'][opt] = self.cfg['options'][opt]
with open(self.path_cfg.get(), 'w') as f:
self.cfg.write(f)
# Other Variables
path_in = self.path_in.get()
path_out = self.path_out.get()
projectname = self.projectname.get()
# Generate a list containing all files
supported_filetypes = ['.asc', '.dat', '.tdms']
file_list = [
f for f in os.listdir(path_in)
if os.path.splitext(f)[-1] in supported_filetypes
]
# Data is stored as a list of dictionaries containing the data
exp_data = [rfnc.convert(path_in, f, Vars) for f in file_list]
if self.is_VST.get() == 0:
if self.rst.get() == 1:
# Evaluate all data sets and store results in a list
if self.rev_pick.get() == 1:
eval_data = [
rfnc.eval_shearstress(
cur_dat,
Vars,
review='auto'
)
for cur_dat in exp_data
]
else:
eval_data = [
rfnc.eval_shearstress(cur_dat, Vars)
for cur_dat in exp_data
]
print('Automatic analysis successful')
else:
print('Automatic analysis skipped, initiating manual picking')
eval_data = [
rfnc.eval_shearstress(cur_dat, Vars, review='manual')
for cur_dat in exp_data
]
# Create lists of parameters for analysis
normal_stress = [ev[0] for ev in eval_data]
peak_stress = [ev[1][1] for ev in eval_data]
dyn_stress = [ev[2][1] for ev in eval_data]
stat_stress = [ev[3][1] for ev in eval_data]
# dilation = [ev[4] for ev in eval_data]
weak = [ev[5] for ev in eval_data]
weak_p = [ev[6] for ev in eval_data]
p_ind = [ev[7] for ev in eval_data]
# ==========ANALYSIS OF FRICTION COEFFICIENTS AND COHESIONS=======
# ===========Analysis 1: Mutual linear regression=================
peakfric_mut, peakdata_mut = rfnc.rst_analmut(
normal_stress,
peak_stress
)
dynfric_mut, dyndata_mut = rfnc.rst_analmut(
normal_stress,
dyn_stress
)
reactfric_mut, reactdata_mut = rfnc.rst_analmut(
normal_stress,
stat_stress
)
# ===========ANALYSIS 2: Standard regression of all data pairs ==
peakfric_std = rfnc.rst_analstd(normal_stress, peak_stress)
dynfric_std = rfnc.rst_analstd(normal_stress, dyn_stress)
reactfric_std = rfnc.rst_analstd(normal_stress, stat_stress)
# ==========MERGE DATA===========================================
strength = (normal_stress, peak_stress, dyn_stress,
stat_stress, weak, weak_p)
fric_mut = (peakfric_mut, dynfric_mut, reactfric_mut)
data_mut = (peakdata_mut, dyndata_mut, reactdata_mut)
fric_std = (peakfric_std, dynfric_std, reactfric_std)
# ===================PLOT DATA=====================================
rfnc.plotstd(path_out, projectname, strength, fric_std)
rfnc.plothist(path_out, projectname, strength, data_mut)
print('>>> Friction data plotted')
if self.plot_ts.get() == 1:
rfnc.plotts(path_out, projectname, exp_data, normal_stress)
print('>>> Time series data plotted')
# ====================Save DATA===================================
if self.rst.get() == 1:
rfnc.saveStrength(path_out, projectname, strength)
rfnc.saveFric(path_out, projectname, fric_mut, fric_std)
lid_pos = rfnc.savelidpos(
path_out,
projectname,
p_ind,
exp_data
)
print('>>> Friction data saved')
prop_file = [
path_in + f
for f in os.listdir(path_in)
if f.endswith('csv')
]
if prop_file:
print(
'>>> Prop file detected! Starting dilation analysis...'
)
rfnc.dilation(
path_out, projectname,
lid_pos, exp_data,
prop_file[0], self.cfg
)
else:
print('>>> There is no prop file! Going on as usual.')
if self.save_ts.get() == 1:
rfnc.saveTS(path_out, projectname, exp_data, normal_stress)
print('>>> Time series data saved')
else:
print('Velocity Stepping Test')
for exp in exp_data:
pfit, perr, name_fit = rfnc.vst_analysis(exp)
rfnc.plotVST(path_out, exp, pfit, perr, name_fit)
if sys.platform == "linux" or sys.platform == "linux2":
p = subprocess.Popen(
["xdg-open", "%s" % path_out],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.communicate()
elif sys.platform == "darwin":
p = subprocess.Popen(
["open", "%s" % path_out],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.communicate()
elif sys.platform == "win32":
os.startfile(path_out) # pylint: disable=no-member
def closing_menu(self):
"""Shows a confirmation dialogue for closing"""
if tk.messagebox.askyesno('Close Program', 'Quit?'):
sys.exit(0)
def more_options(self, event):
"""Shows all additional options for the processing"""
opt_dlg = tk.Toplevel(self)
opt_dlg.wm_title('More Options')
opt_dlg.grid()
opt_dlg.label_options = tk.Label(opt_dlg,
text='Options:')
opt_dlg.label_options.grid(column=0, row=0, sticky='EW')
# Dynamically generate options based on config entries
for (i, item) in enumerate(self.cfg['parameters'].items()):
opt_dlg.label_item = tk.Label(opt_dlg,
text=item[0] + ':')
opt_dlg.label_item.grid(column=0, row=i + 1, sticky='e')
opt_dlg.entry_item = tk.Entry(opt_dlg,
textvariable=self.params[item[0]],
justify='right',
width=45)
opt_dlg.entry_item.grid(column=1, row=i + 1, sticky='EW')
opt_dlg.label_unit = tk.Label(opt_dlg,
text=self.cfg['units'][item[0]])
opt_dlg.label_unit.grid(column=2, row=i + 1, sticky='w')
def set_data(self):
''' Set contents of fields and variables according to config '''
self.projectname.set(self.cfg['paths']['projectname'])
self.path_in.set(self.cfg['paths']['path_in'])
self.path_out.set(self.cfg['paths']['path_out'])
self.path_cfg.set(self.cfg['paths']['path_cfg'])
self.plot_ts.set(self.cfg.getint('options', 'plot_ts'))
self.save_ts.set(self.cfg.getint('options', 'save_ts'))
self.rst.set(self.cfg.getint('options', 'rst'))
self.rev_pick.set(self.cfg.getint('options', 'rev_pick'))
self.is_VST.set(self.cfg.getint('options', 'is_VST'))
self.pred_norm.set(self.cfg.getint('options', 'pred_norm'))
# Read parameters and store them as a dictionary of StringVars
# This automatically links them to changes made in the Options dialog.
for item in self.cfg['parameters'].items():
self.params[item[0]] = tk.StringVar()
self.params[item[0]].set(item[1])
def refresh_entries_from_cfg(self, path=None, init=False):
"""Refresh entries from config file"""
if init:
try:
c = self.cfg.read(self.path_cfg.get())
if not(c):
self.create_default_config()
self.cfg.read(self.path_cfg.get())
print('created default config')
print('read default config')
except:
self.create_default_config()
self.cfg.read(self.path_cfg.get())
print('created default config')
if path:
try:
c = self.cfg.read(self.path_cfg.get())
if not(c):
self.create_default_config(path=path)
self.cfg.read(self.path_cfg.get())
print('created project config')
print('read project config')
except:
self.create_default_config(path=path)
self.cfg.read(self.path_cfg.get())
print('created project config')
self.set_data()
def create_default_config(self, path=None):
"""Creates default config in the given path"""
config = configparser.ConfigParser()
config.optionxform = str
if not(path):
path = os.path.join(str(Path.home()), 'rst_evaluation_default.ini')
projectname = 'example'
path_in = 'input/'
path_out = 'output/'
else:
projectname = self.projectname.get()
path_in = self.path_in.get()
path_out = self.path_out.get()
config['paths'] = {
'projectname': projectname,
'path_in': path_in,
'path_out': path_out,
'path_cfg': path
}
config['options'] = {
'plot_ts': 1, # if 1: data plotted, if 0: not
'save_ts': 1, # if 1: data stored in .txt files, if 0: not
'rst': 1, # if 1: automatic picking
'rev_pick': 1, # if 1: the user can review the picked peaks
'is_VST': 0, # if 1: the file is a VST-Test file
'pred_norm': 1, # if 1: uses predefined normal stresses
}
config['parameters'] = {
'A': 0.022619, # area of shear zone (area of lid) (m^2)
'li': 0.0776, # inner lever (center cell-center shear zone)
'lo': 0.1250, # outer lever (center cell-hinge point)
'vel': 30, # shear velocity (mm/min)
'prec': 500, # precision for normal stress in filenames
'velnoise': 1e-2,
'stressnoise': 0.025,
'cellweight': 2185.3, # weight of shear cell
'smoothwindow': 5, # Smoothing Factor for plots
'pred_norms': [500, 1000, 2000, 4000, 8000, 16000, 20000],
'nsamples': 10000, # Maximum number of samples to use
'downsample_filter': 0, # Use downsampling filter or not.
}
config['units'] = {
'A': 'm^2',
'li': 'm',
'lo': 'm',
'vel': 'mm/min',
'prec': 'Pa',
'velnoise': 'mm/min',
'stressnoise': 'Pa',
'cellweight': 'g',
'smoothwindow': 'samples',
'pred_norms': 'Pa',
'nsamples': 'samples',
'downsample_filter': '(0 = No, 1 = Yes)',
}
self.path_cfg.set(config['paths']['path_cfg'])
with open(self.path_cfg.get(), 'w') as f:
config.write(f)
self.cfg = config
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
base_path = getattr(
sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__))
)
return os.path.join(base_path, relative_path)
def run():
app = RST_pick_GUI(None)
app.title('RSTpick GUI')
image_path = resource_path(
os.path.join('images', 'rst-evaluation_x256.ico')
)
app.iconbitmap(image_path)
app.mainloop()
if __name__ == '__main__':
run()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: M. Rudolf, M. Warsitzka
# @Date: 2019-02-20 12:00:00
# @Last Modified by: M. Rudolf
# @Last Modified time: 2021-04-30 14:09:52
"""
RST_pick_GUI.py
GUI based approach for RSTpicking
"""
import configparser
import json
import logging
import os