Commit 775d39b8 authored by Michael Rudolf's avatar Michael Rudolf
Browse files

GUI Update

- Simplified checkboxes
- Added icons
- Switched to ttk widgets
- Some widgets respond to selections
resolves #9
parent a17373d6
......@@ -69,14 +69,20 @@ For a more detailed description of the other components see the
[Documentation](https://gitext.gfz-potsdam.de/analab-code/rst-evaluation/blob/master/Documentation/)
and the readme files in the individual folders.
## Acknowledgements
The original scripts for version 0.0.1 (4260ec45) in this repository have been provided by M. Warsitzka @warsitzka_at_ig.cas.cz and contain scripts that have been developed by M. Ritter.
Warsitzka, Michael; Ge, Zhiyuan; Schönebeck, Jan-Michael; Pohlenz, Andre; Kukowski, Nina (2019): Ring-shear test data of foam glass beads used for analogue experiments in the Helmholtz Laboratory for Tectonic Modelling (HelTec) at the GFZ German Research Centre for Geosciences in Potsdam and the Institute of Geosciences, Friedrich Schiller University Jena. GFZ Data Services. http://doi.org/10.5880/GFZ.4.1.2019.002
This software reuses images for the user interface that are part of matplotlib (Hunter, 2007).
## Citation
Please cite this repository as:
Rudolf, Michael; Warsitzka, Michael (2021): RST Evaluation - Scripts for analysing shear experiments from the Schulze RST.pc01 ring shear tester. GFZ Data Services. https://doi.org/10.5880/GFZ.4.1.2021.001
## References
[J. D. Hunter, "Matplotlib: A 2D Graphics Environment", Computing in Science & Engineering, vol. 9, no. 3, pp. 90-95, 2007.](https://doi.org/10.1109/MCSE.2007.55)
\ No newline at end of file
......@@ -12,25 +12,14 @@ GUI based approach for RSTpicking
"""
import os
import tkinter as tk
import matplotlib as mpl
from rstevaluation import mainGUI, utilities
from rstevaluation import mainGUI
def run():
app = mainGUI.RST_pick_GUI(None)
app.title('RSTpick GUI')
image_path = utilities.resource_path(
os.path.join('images', 'rst-evaluation_x256.ico')
)
try:
app.iconbitmap(image_path)
except tk.TclError:
pass
mpl.use('qt5agg')
app.mainloop()
......
""" Contains some custom classes for the use with the main GUI """
import tkinter as tk
from tkinter import ttk
class ToolTip(object):
"""
ToolTip object which is shown while hovering over an element.
Adapted from:
https://stackoverflow.com/questions/20399243/display-message-when-hovering-over-something-with-mouse-cursor-in-python
Possibly contains code by voidspace.org.uk (offline at the time of writing)
"""
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, text):
"Display text in tooltip window"
self.text = text
if self.tipwindow or not self.text:
return
x, y, cx, cy = self.widget.bbox("insert")
x = x + self.widget.winfo_rootx() + 57
y = y + cy + self.widget.winfo_rooty() + 27
self.tipwindow = tw = tk.Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
label = ttk.Label(
tw,
text=self.text, justify=tk.LEFT,
background="#ffffe0", relief=tk.SOLID, borderwidth=1,
font=("tahoma", "8", "normal")
)
label.pack(ipadx=1)
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw:
tw.destroy()
def CreateToolTip(widget, text):
toolTip = ToolTip(widget)
def enter(event):
toolTip.showtip(text)
def leave(event):
toolTip.hidetip()
widget.bind('<Enter>', enter)
widget.bind('<Leave>', leave)
......@@ -14,7 +14,7 @@ import numpy as np
import uncertainties as unc
from tqdm import tqdm
from . import data as rstdat
from rstevaluation import data as rstdat
def convert(path, file_in, config):
......
""" Provides PhotoImage objects for all images """
import os
import tkinter as tk
from importlib import resources
def icon_rst():
""" Returns main application icon """
with resources.path('rstevaluation.icons', 'rst-evaluation.png') as fpath:
phim = tk.PhotoImage(
file=fpath
)
return phim
def icon_load():
""" Returns an icon for loading from a folder """
with resources.path('rstevaluation.icons', 'home.png') as fpath:
phim = tk.PhotoImage(
file=fpath
)
return phim
def icon_save():
""" Returns an icon for saving to a folder """
with resources.path('rstevaluation.icons', 'filesave.png') as fpath:
phim = tk.PhotoImage(
file=fpath
)
return phim
def icon_options():
""" Returns an icon for options """
with resources.path('rstevaluation.icons', 'subplots.png') as fpath:
phim = tk.PhotoImage(
file=fpath
)
return phim
def icon_start():
""" Returns an icon for processing """
with resources.path(
'rstevaluation.icons', 'qt4_editor_options.png'
) as fpath:
phim = tk.PhotoImage(
file=fpath
)
return phim
......@@ -6,9 +6,12 @@ import pathlib
import re
import sys
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import filedialog, messagebox, ttk
from . import processing as rstprocess
import rstevaluation
from rstevaluation import RST_pick_GUI as main
from rstevaluation import get_icons as rsticons
from rstevaluation import processing as rstprocess
class RST_pick_GUI(tk.Tk):
......@@ -26,9 +29,9 @@ class RST_pick_GUI(tk.Tk):
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.stick_slip = tk.IntVar()
self.is_rst = tk.IntVar()
self.cfg = configparser.ConfigParser()
self.cfg.optionxform = str
self.path_cfg.set('rst_evaluation_')
......@@ -46,122 +49,175 @@ class RST_pick_GUI(tk.Tk):
self.grid()
# Project name
self.label_projname = tk.Label(
self.label_projname = ttk.Label(
self,
text='Project:',
anchor='e',
width=20
)
self.label_projname.grid(column=0, row=0, sticky='EW')
self.entry_projname = tk.Entry(
self.entry_projname = ttk.Entry(
self,
textvariable=self.projectname)
self.entry_projname.grid(column=1, row=0, sticky='EW')
# Input path
self.label_input = tk.Label(
self.label_input = ttk.Label(
self,
text='Input Folder:',
anchor='e')
)
self.label_input.grid(column=0, row=1, sticky='EW')
self.entry_input = tk.Entry(
self.entry_input = ttk.Entry(
self,
width=80,
textvariable=self.path_in)
self.entry_input.grid(column=1, row=1, sticky='EW')
self.button_input = tk.Button(
icon = rsticons.icon_load()
self.button_input = ttk.Button(
self,
text='...')
image=icon
)
self.button_input.image = icon
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.label_output = ttk.Label(
self,
text='Output Folder:',
anchor='e')
)
self.label_output.grid(column=0, row=2, sticky='EW')
self.entry_output = tk.Entry(
self.entry_output = ttk.Entry(
self,
textvariable=self.path_out)
self.entry_output.grid(column=1, row=2, sticky='EW')
self.button_output = tk.Button(
icon = rsticons.icon_save()
self.button_output = ttk.Button(
self,
text='...')
image=icon
)
self.button_output.icon = icon
self.button_output.grid(column=2, row=2)
self.button_output.bind('<ButtonRelease-1>', self.folder_browser)
# Options
self.label_options = tk.Label(
self.label_options = ttk.Label(
self,
text='Options:',
anchor='w',
font=('Helvetica', 12, 'bold'))
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(
# Is a RST-Test
self.opt_rst = ttk.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')
text='RST Test',
variable=self.is_rst
)
self.opt_rst.grid(column=0, row=4, sticky='EW')
self.opt_rst.bind('<ButtonRelease-1>', self.switch_pred_load)
# Use predefined normal stress
self.opt_pred = tk.Checkbutton(
self.opt_pred = ttk.Checkbutton(
self,
text='Use predefined normal stress',
anchor='w',
variable=self.pred_norm)
self.opt_pred.grid(column=1, row=5, sticky='EW')
self.opt_pred.grid(column=0, row=5, sticky='EW')
# Is a VST-Test
self.opt_revpick = tk.Checkbutton(
self.opt_vst = ttk.Checkbutton(
self,
text='VST Test',
variable=self.is_rst,
offvalue=1,
onvalue=0
)
self.opt_vst.grid(column=1, row=4, sticky='EW')
self.opt_vst.bind('<ButtonRelease-1>', self.switch_pred_load)
# Stick-Slip Detection
self.opt_stickslip = ttk.Checkbutton(
self,
text='VST-Test',
anchor='w',
variable=self.is_VST)
self.opt_revpick.grid(column=1, row=6, sticky='EW')
text='Stick-Slip Detection',
variable=self.stick_slip)
self.opt_stickslip.grid(column=1, row=5, sticky='EW')
# More Options
self.button_options = tk.Button(
icon = rsticons.icon_options()
self.button_options = ttk.Button(
self,
text='More options...')
self.button_options.grid(column=1, row=7, sticky='w')
text='More options',
image=icon,
compound=tk.LEFT
)
self.button_options.icon = icon
self.button_options.grid(column=0, row=7, sticky='w')
self.button_options.bind('<ButtonRelease-1>', self.more_options)
# Start processing
self.button_start = tk.Button(
icon = rsticons.icon_start()
self.button_start = ttk.Button(
self,
text='Start Processing',
padx=20)
self.button_start.grid(column=0, row=7, columnspan=3, sticky='w')
image=icon,
compound=tk.LEFT
)
self.button_start.icon = icon
self.button_start.grid(column=1, row=7, sticky='e')
self.button_start.bind('<ButtonRelease-1>', self.start_processing)
self.iconphoto(True, rsticons.icon_rst())
self.make_dpi_aware()
self.update()
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:
print('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:
print('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 plot_window(self, event):
"""Shows a plot window"""
plot_win = tk.Toplevel(self)
......@@ -198,28 +254,39 @@ class RST_pick_GUI(tk.Tk):
if messagebox.askyesno('Close Program', 'Quit?'):
sys.exit(0)
def switch_pred_load(self, event):
""" Switches the predefined normal load to inactive for VST """
try:
if self.is_rst.get():
self.opt_pred['state'] = tk.DISABLED
else:
self.opt_pred['state'] = tk.NORMAL
except AttributeError as _:
pass
# == CONFIG STUFF ==
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.label_options = ttk.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.label_item = ttk.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.entry_item = ttk.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.label_unit = ttk.Label(
opt_dlg,
text=self.cfg['units'][item[0]])
opt_dlg.label_unit.grid(column=2, row=i + 1, sticky='w')
......@@ -233,9 +300,8 @@ class RST_pick_GUI(tk.Tk):
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.is_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.
......@@ -254,9 +320,8 @@ class RST_pick_GUI(tk.Tk):
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']['rst'] = str(self.is_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())
self.cfg['paths']['path_in'] = self.path_in.get()
......@@ -374,3 +439,7 @@ class RST_pick_GUI(tk.Tk):
with open(self.path_cfg.get(), 'w') as f:
config.write(f)
self.cfg = config
if __name__ == '__main__':
main.run()
......@@ -8,7 +8,7 @@ import scipy.stats as stats
import uncertainties as unc
from uncertainties.core import ufloat
from . import analysis as rst_analysis
from rstevaluation import analysis as rst_analysis
# ===================LINEAR REGRESSION=========================================
......
......@@ -5,49 +5,32 @@ import os
import subprocess
import sys
from . import analysis as rstanalysis
from . import files as rstfiles
from . import picking as rstpicking
from . import plots as rstplots
from rstevaluation import analysis as rstanalysis
from rstevaluation import files as rstfiles
from rstevaluation import picking as rstpicking
from rstevaluation import plots as rstplots
def evaluate(config):
""" Evaluates the data coming from the config """
exp_data = get_data(config)
if config.getint('options', 'is_VST'):
if config.getint('options', 'rst'):
eval_data = [
rstpicking.eval_shearstress(
cur_dat,
config,
review='auto'
)
for cur_dat in exp_data
]
evaluate_data(eval_data, exp_data, config)
else:
print('Velocity Stepping Test')
for exp in exp_data:
pfit, perr, name_fit = rstanalysis.vst_analysis(exp)
rstplots.plotVST(
config['paths']['path_out'],
exp, pfit, perr, name_fit)
else:
if config.getint('options', 'rst'):
# Evaluate all data sets and store results in a list
if config.getint('options', 'rev_pick'):
eval_data = [
rstpicking.eval_shearstress(
cur_dat,
config,
review='auto'
)
for cur_dat in exp_data
]
else:
eval_data = [
rstpicking.eval_shearstress(cur_dat, config)
for cur_dat in exp_data
]
print('Automatic analysis successful')
else:
print('Automatic analysis skipped, initiating manual picking')
eval_data = [
rstpicking.eval_shearstress(
cur_dat, config, review='manual')
for cur_dat in exp_data
]