UI improvements for graphs, change colors, insert waves etc

This commit is contained in:
Stefan Frederik 2022-01-27 20:47:27 +01:00
parent bf624cfc77
commit 038ef39bb5
7 changed files with 247 additions and 54 deletions

View File

@ -40,7 +40,8 @@ static int waves_selected(int event, int key, int state, int button)
r = &xctx->rect[GRIDLAYER][i];
if(!(r->flags & 1) ) continue;
if( (xctx->ui_state & GRAPHPAN) ||
POINTINSIDE(xctx->mousex, xctx->mousey, r->x1 + 20, r->y1 + 20, r->x2 - 30, r->y2 - 10) ) {
POINTINSIDE(xctx->mousex, xctx->mousey, r->x1, r->y1, r->x2 - 40, r->y1 + 20) ||
POINTINSIDE(xctx->mousex, xctx->mousey, r->x1 + 20, r->y1, r->x2 - 30, r->y2 - 10) ) {
is_inside = 1;
tclvareval(xctx->top_path, ".drw configure -cursor tcross" , NULL);
}
@ -185,7 +186,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
Graph_ctx *gr;
char s[30];
const char *val;
int i, need_redraw = 0, dataset = 0;
int i, need_all_redraw = 0, need_redraw = 0, dataset = 0;
double xx1, xx2, yy1, yy2;
double delta_threshold = 0.25;
double zoom_m = 0.5;
@ -241,21 +242,26 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
xctx->graph_flags |= 32; /* Start move cursor2 */
}
}
if((key == 'q') ) {
char s[30];
my_snprintf(s, S(s), "%d", i);
tclvareval("graph_edit_properties ", s, NULL);
if(event == -3 && button == Button1) {
if(!edit_wave_attributes(i, gr)) {
char s[30];
my_snprintf(s, S(s), "%d", i);
tclvareval("graph_edit_properties ", s, NULL);
}
setup_graph_data(i, xctx->graph_flags, 0, gr);
draw_graph(i, 1 + 8 + (xctx->graph_flags & 6), gr); /* draw data in each graph box */
}
/* x cursor1 toggle */
else if((key == 'a') ) {
xctx->graph_flags ^= 2;
need_redraw = 1;
need_all_redraw = 1;
if(xctx->graph_flags & 2) xctx->graph_cursor1_x = G_X(xctx->mousex);
}
/* x cursor2 toggle */
else if((key == 'b') ) {
xctx->graph_flags ^= 4;
need_redraw = 1;
need_all_redraw = 1;
if(xctx->graph_flags & 4) xctx->graph_cursor2_x = G_X(xctx->mousex);
}
/* measurement tooltip */
@ -293,6 +299,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
for(i=0; i< xctx->rects[GRIDLAYER]; i++) {
xRect *r;
r = &xctx->rect[GRIDLAYER][i];
need_redraw = 0;
if( !(r->flags & 1) ) continue;
gr->gx1 = gr->master_gx1;
gr->gx2 = gr->master_gx2;
@ -731,7 +738,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
}
}
} /* else if( event == ButtonRelease) */
if(need_redraw) {
if(need_redraw || need_all_redraw) {
setup_graph_data(i, xctx->graph_flags, 0, gr);
draw_graph(i, 1 + 8 + (xctx->graph_flags & 6), gr); /* draw data in each graph box */
}
@ -1573,10 +1580,6 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key,
if(key=='q' && state==0) /* edit attributes */
{
if(xctx->semaphore >= 2) break;
if(waves_selected(event, key, state, button)) {
waves_callback(event, mx, my, key, button, aux, state);
break;
}
edit_property(0);
break;
}
@ -2537,6 +2540,10 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key,
}
break;
case -3: /* double click : edit prop */
if( waves_selected(event, key, state, button)) {
waves_callback(event, mx, my, key, button, aux, state);
break;
}
if(xctx->semaphore >= 2) break;
dbg(1, "callback(): DoubleClick ui_state=%ld state=%d\n",xctx->ui_state,state);
if(button==Button1) {

View File

@ -1723,6 +1723,7 @@ static void draw_graph_points(int v, int first, int last,
double s2;
double c, c1;
register SPICE_DATA *gv = xctx->graph_values[v];
int hilight_wave = -1;
digital = gr->digital;
if(digital) {
@ -1745,10 +1746,20 @@ static void draw_graph_points(int v, int first, int last,
poly_npoints++;
}
/* plot data */
if(xctx->draw_window)
if(xctx->draw_window) {
if(hilight_wave == wcnt) XSetLineAttributes (display, xctx->gc[wave_col],
3 * INT_WIDTH(xctx->lw) ,LineSolid, CapRound , JoinRound);
XDrawLines(display, xctx->window, xctx->gc[wave_col], point, poly_npoints, CoordModeOrigin);
if(xctx->draw_pixmap)
if(hilight_wave == wcnt) XSetLineAttributes (display, xctx->gc[wave_col],
INT_WIDTH(xctx->lw) ,LineSolid, CapRound , JoinRound);
}
if(xctx->draw_pixmap) {
if(hilight_wave == wcnt) XSetLineAttributes (display, xctx->gc[wave_col],
3 * INT_WIDTH(xctx->lw) ,LineSolid, CapRound , JoinRound);
XDrawLines(display, xctx->save_pixmap, xctx->gc[wave_col], point, poly_npoints, CoordModeOrigin);
if(hilight_wave == wcnt) XSetLineAttributes (display, xctx->gc[wave_col],
INT_WIDTH(xctx->lw) ,LineSolid, CapRound , JoinRound);
}
} else dbg(1, "skipping wave: %s\n", xctx->graph_names[v]);
}
@ -2169,6 +2180,79 @@ int read_embedded_rawfile(void)
return res;
}
/* when double clicking in a graph if this happens on a wave label
* look up the wave and call tcl "graph_edit_wave <graph> <wave>"
* with graph index and wave index
* return 1 if a wave was found
*/
int edit_wave_attributes(int i, Graph_ctx *gr)
{
char *node = NULL, *color = NULL, *sweep = NULL;
int sweep_idx = 0;
int n_nodes; /* number of variables to display in a single graph */
char *saven, *savec, *saves, *nptr, *cptr, *sptr;
const char *ntok, *ctok, *stok;
int wcnt = 0, ret = 0;
xRect *r = &xctx->rect[GRIDLAYER][i];
/* get plot data */
my_strdup2(1491, &node, get_tok_value(r->prop_ptr,"node",0));
my_strdup2(1492, &color, get_tok_value(r->prop_ptr,"color",0));
my_strdup2(1493, &sweep, get_tok_value(r->prop_ptr,"sweep",0));
nptr = node;
cptr = color;
sptr = sweep;
n_nodes = count_items(node, " \t\n");
/* process each node given in "node" attribute, get also associated color/sweep var if any */
while( (ntok = my_strtok_r(nptr, "\n\t ", &saven)) ) {
ctok = my_strtok_r(cptr, " ", &savec);
stok = my_strtok_r(sptr, " ", &saves);
nptr = cptr = sptr = NULL;
dbg(1, "ntok=%s ctok=%s\n", ntok, ctok? ctok: "NULL");
if(stok && stok[0]) {
sweep_idx = get_raw_index(stok);
if( sweep_idx == -1) sweep_idx = 0;
}
if(gr->digital) {
double xt1 = gr->rx1; /* <-- waves_selected() is more restrictive than this */
double xt2 = gr->x1 - 20 * gr->txtsizelab;
double s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */
double s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */
double yt1 = s1 * (double)(n_nodes - wcnt) * gr->gh - (gr->gy1 - gr->gh * 0.1) * s2;
double yt2 = yt1 + s1 * gr->gh;
if(yt1 <= gr->ypos2 && yt1 >= gr->ypos1) {
double tmp = DW_Y(yt1);
yt1 = DW_Y(yt2);
yt2 = tmp;
if(POINTINSIDE(xctx->mousex_snap, xctx->mousey_snap, xt1, yt1, xt2, yt2)) {
char s[30];
ret = 1;
my_snprintf(s, S(s), "%d %d", i, wcnt);
tclvareval("graph_edit_wave ", s, NULL);
}
}
} else {
double xt1 = gr->rx1 + 2 + gr->rw / n_nodes * wcnt;
double yt1 = gr->ry1;
double xt2 = xt1 + gr->rw / n_nodes;
double yt2 = gr->y1;
if(POINTINSIDE(xctx->mousex_snap, xctx->mousey_snap, xt1, yt1, xt2, yt2)) {
char s[50];
ret = 1;
my_snprintf(s, S(s), "%d %d", i, wcnt);
tclvareval("graph_edit_wave ", s, NULL);
}
}
wcnt++;
} /* while( (ntok = my_strtok_r(nptr, "\n\t ", &saven)) ) */
my_free(1494, &node);
my_free(1495, &color);
my_free(1496, &sweep);
return ret;
}
/* flags:
* 1: do final XCopyArea (copy 2nd buffer areas to screen)
* If draw_graph_all() is called from draw() no need to do XCopyArea, as draw() does it already.
@ -2179,7 +2263,7 @@ int read_embedded_rawfile(void)
*/
void draw_graph(int i, const int flags, Graph_ctx *gr)
{
int wave_color = 5;
int wave_color = 4;
char *node = NULL, *color = NULL, *sweep = NULL;
int sweep_idx = 0;
int n_nodes; /* number of variables to display in a single graph */
@ -2194,6 +2278,8 @@ void draw_graph(int i, const int flags, Graph_ctx *gr)
if(RECT_OUTSIDE( gr->sx1, gr->sy1, gr->sx2, gr->sy2,
xctx->areax1, xctx->areay1, xctx->areax2, xctx->areay2)) return;
/* draw stuff */
if(flags & 8) {
/* graph box, gridlines and axes */
@ -2365,6 +2451,7 @@ void draw_graph_all(int flags)
int bbox_set = 0;
const char *tmp;
int save_bbx1, save_bby1, save_bbx2, save_bby2;
dbg(0, "draw_graph_all(): flags=%d\n", flags);
/* save bbox data, since draw_graph_all() is called from draw() which may be called after a bbox(SET) */
sch_loaded = schematic_waves_loaded();
tmp = tclgetvar("hide_empty_graphs");

View File

@ -1472,7 +1472,7 @@ void propagate_logic()
}
if(!found) break;
/* get out from infinite loops (circuit is oscillating) */
tclvareval("update; if {$::tclstop == 1} {return 1} else {return 0}", NULL);
tclvareval("update; expr {$::tclstop == 1}", NULL);
if( tclresult()[0] == '1') break;
iter++;
} /* while(1) */

View File

@ -29,6 +29,7 @@
* and writes stdout, return the result in dout[olen].
* Caller must free the returned buffer.
*/
#ifdef __unix__
int filter_data(const char *din, const size_t ilen,
char **dout, size_t *olen,
const char *cmd)
@ -102,6 +103,16 @@ int filter_data(const char *din, const size_t ilen,
signal(SIGPIPE, SIG_DFL); /* restore default SIGPIPE signal action */
return ret;
}
#else
int filter_data(const char* din, const size_t ilen,
char** dout, size_t* olen,
const char* cmd)
{
*dout = NULL;
*olen = 0;
return 1;
}
#endif
/* Caller should free returned buffer */
char *base64_encode(const unsigned char *data, const size_t input_length, size_t *output_length, int brk) {

View File

@ -2666,7 +2666,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
}
if(argc > 6)
my_strdup(1478, &r->prop_ptr, subst_token(r->prop_ptr, argv[5], argv[6]));
my_strdup(1486, &r->prop_ptr, subst_token(r->prop_ptr, argv[5], argv[6]));
else
my_strdup(1478, &r->prop_ptr, subst_token(r->prop_ptr, argv[5], NULL)); /* delete attr */
if(!fast) {
@ -2734,8 +2734,13 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
else if(argv[1][0] == 't') {
if(!strcmp(argv[1],"test"))
{
char s[30];
int c;
cmd_found = 1;
Tcl_ResetResult(interp);
c = xctx->strcmp("aaa","AAA");
my_snprintf(s, S(s), "%d", c);
Tcl_AppendResult(interp, s, NULL);
}
else if(!strcmp(argv[1],"toggle_colorscheme"))
@ -3088,6 +3093,10 @@ int tclvareval(const char *script, ...)
}
return_code = Tcl_EvalEx(interp, str, size, TCL_EVAL_GLOBAL);
va_end(args);
if(return_code != TCL_OK) {
dbg(0, "tclvareval(): error executing %s: %s\n", str, tclresult());
Tcl_ResetResult(interp);
}
my_free(1381, &str);
return return_code;
}

View File

@ -1018,6 +1018,7 @@ extern void free_rawfile(int dr);
extern int read_rawfile(const char *f);
extern double get_raw_value(int dataset, int idx, int point);
extern int schematic_waves_loaded(void);
extern int edit_wave_attributes(int i, Graph_ctx *gr);
extern void draw_graph(int i, int flags, Graph_ctx *gr);
extern void draw_graph_all(int flags);
extern void setup_graph_data(int i, const int flags, int skip, Graph_ctx *gr);

View File

@ -1284,6 +1284,44 @@ proc waves {} {
}
# ============================================================
# allow change color (via graph_change_wave_color) of double clicked wave
proc graph_edit_wave {n n_wave} {
global graph_sel_color graph_selected colors graph_sel_wave
set_ne graph_sel_color 4
set graph_selected $n
set graph_sel_wave $n_wave
set col [xschem getprop rect 2 $graph_selected color]
set node [xschem getprop rect 2 $graph_selected node]
set n_nodes [llength $node]
# add default colors if unspecified in col
set i 0
foreach graph_node $node {
if {[lindex $col $i] eq {}} { lappend col $graph_sel_color}
incr i
}
set graph_sel_color [lindex $col $graph_sel_wave]
xschem setprop rect 2 $graph_selected color $col fast
# puts "graph: $graph_selected , wave: $n_wave, n_nodes: $n_nodes"
# puts " node: $node, col: $col"
# puts "------"
toplevel .dialog
frame .dialog.f
button .dialog.ok -text OK -command {destroy .dialog}
button .dialog.cancel -text Cancel -command {destroy .dialog}
for {set i 4} {$i < 22} {incr i} {
radiobutton .dialog.f.r$i -value $i -bg [lindex $colors $i] \
-variable graph_sel_color -command {graph_change_wave_color $graph_sel_wave }
pack .dialog.f.r$i -side left -fill both -expand yes
}
grid .dialog.f - -sticky nsew
grid .dialog.ok .dialog.cancel -sticky ew
grid rowconfig .dialog 0 -weight 1
grid column .dialog 0 -weight 1
grid column .dialog 1 -weight 1
tkwait window .dialog
}
proc graph_add_nodes {} {
global graph_bus
set sel_idx [.dialog.center.left.list1 curselection]
@ -1322,36 +1360,50 @@ proc graph_get_signal_list {siglist pattern } {
return $result
}
proc graph_change_wave_color {{wave {}}} {
global graph_sel_color graph_selected
# get tag the cursor is on:
if { $wave eq {}} {
set tag [.dialog.center.right.text1 tag names insert]
if { [regexp {^t} $tag]} {
set index [string range $tag 1 end]
set col [xschem getprop rect 2 $graph_selected color]
set col [lreplace $col $index $index $graph_sel_color]
xschem setprop rect 2 $graph_selected color $col fast
graph_update_nodelist
}
# wave to change provided as parameter
} else {
set col [xschem getprop rect 2 $graph_selected color]
set col [lreplace $col $wave $wave $graph_sel_color]
xschem setprop rect 2 $graph_selected color $col
}
}
proc graph_update_nodelist {} {
global graph_selected colors graph_sel_color
set nodelist [.dialog.center.right.text1 get 1.0 end]
# xschem setprop rect 2 $graph_selected node $nodelist
# delete old tags
eval .dialog.center.right.text1 tag delete [ .dialog.center.right.text1 tag names]
# tagging nodes in text widget:
set col [xschem getprop rect 2 $graph_selected color]
set col [string trim $col { }]
set col [string trim $col " \n"]
set tt [.dialog.center.right.text1 search -all -nolinestop -regexp -count cc {[^ \n]+} 1.0]
set n 0
foreach t $tt c $cc {
set curr_color [expr {$n % 18 + 4}]
set b [lindex $col $n]
if {$b eq {}} {
if {$col ne {}} {append col { }}
append col $curr_color
set b [lindex $colors $curr_color]
} else {
set b [lindex $colors $b]
set col_idx [lindex $col $n]
if {$col_idx eq {}} {
set col_idx $graph_sel_color
lappend col $graph_sel_color
}
set b [lindex $colors $col_idx]
.dialog.center.right.text1 tag add t$n $t "$t + $c chars"
.dialog.center.right.text1 tag configure t$n -background $b
if { $n == 4 || $n == 5 || $n == 12 } {
if { $col_idx == 8 || $col_idx == 9 || $col_idx == 16 || $col_idx == 19} {
.dialog.center.right.text1 tag configure t$n -foreground black}
incr n
}
# get tag the cursor is on:
# .dialog.center.right.text1 tag names insert
puts $col
xschem setprop rect 2 $graph_selected color $col fast
}
proc fill_graph_listbox {} {
@ -1397,7 +1449,7 @@ proc graph_edit_properties {n} {
# center right frame
label .dialog.center.right.lab1 -text {Signals in graph}
text .dialog.center.right.text1 -wrap none -height 10 -bg grey50 -fg white -insertbackground grey40 \
text .dialog.center.right.text1 -wrap none -width 50 -height 10 -bg grey50 -fg white -insertbackground grey40 \
-yscrollcommand {.dialog.center.right.yscroll set} \
-xscrollcommand {.dialog.center.right.xscroll set}
scrollbar .dialog.center.right.yscroll -command {.dialog.center.right.text1 yview}
@ -1412,52 +1464,74 @@ proc graph_edit_properties {n} {
# bottom frame
button .dialog.bottom.cancel -text Cancel -command {
destroy .dialog
}
button .dialog.bottom.ok -text OK -command {
set node [string trim [.dialog.center.right.text1 get 1.0 end] " \n"]
graph_update_nodelist
# destroy .dialog
xschem setprop rect 2 $graph_selected y1 [.dialog.top.e2 get] fast
xschem setprop rect 2 $graph_selected y2 [.dialog.top.e3 get] fast
xschem setprop rect 2 $graph_selected node $node fast
destroy .dialog
}
pack .dialog.bottom.ok -side left
pack .dialog.bottom.cancel -side left
for {set i 4} {$i < 22} {incr i} {
radiobutton .dialog.bottom.r$i -value $i -bg [lindex $colors $i] -variable graph_sel_color
radiobutton .dialog.bottom.r$i -value $i -bg [lindex $colors $i] \
-variable graph_sel_color -command graph_change_wave_color
pack .dialog.bottom.r$i -side left
}
# top frame
label .dialog.top.l1 -text Search:
entry .dialog.top.e1 -width 20
checkbutton .dialog.top.c1 -text bus -variable graph_bus
checkbutton .dialog.top.c2 -text {Increasing sort} -variable graph_sort -indicatoron 1 \
entry .dialog.top.e1 -width 10
checkbutton .dialog.top.c1 -text bus -padx 2 -variable graph_bus
checkbutton .dialog.top.c2 -text {Incr. sort} -variable graph_sort -indicatoron 1 \
-command fill_graph_listbox
checkbutton .dialog.top.c3 -text {Digital} -variable graph_digital -indicatoron 1 \
-command {xschem setprop rect 2 $graph_selected digital $graph_digital}
-command {
xschem setprop rect 2 $graph_selected digital $graph_digital fast
}
label .dialog.top.l2 -text { Min Value:}
entry .dialog.top.e2 -width 5
label .dialog.top.l3 -text { Max Value:}
entry .dialog.top.e3 -width 5
button .dialog.top.b1 -text Clear -command {
button .dialog.top.b1 -text Clear -padx 2 -command {
.dialog.top.e1 delete 0 end
fill_graph_listbox
}
button .dialog.top.b2 -text Add -command {graph_add_nodes}
pack .dialog.top.l1 .dialog.top.e1 .dialog.top.b1 .dialog.top.b2 .dialog.top.c1 -side left
button .dialog.top.b2 -text Add -padx 2 -command {graph_add_nodes; graph_update_nodelist}
pack .dialog.top.l1 .dialog.top.e1 -side left
pack .dialog.top.c2 .dialog.top.c3 -side left
pack .dialog.top.l2 .dialog.top.e2 .dialog.top.l3 .dialog.top.e3 -side left
pack .dialog.top.b1 .dialog.top.b2 .dialog.top.c1 -side left
.dialog.top.e2 insert 0 [xschem getprop rect 2 $graph_selected y1]
.dialog.top.e3 insert 0 [xschem getprop rect 2 $graph_selected y2]
# binding
bind .dialog.top.e1 <KeyRelease> {
fill_graph_listbox
}
bind .dialog.center.left.list1 <Double-Button-1> {
.dialog.top.b2 invoke
}
bind .dialog.center.right.text1 <KeyRelease> {
graph_update_nodelist
}
# fill data in left listbox
eval .dialog.center.left.list1 insert 0 [graph_get_signal_list [xschem raw_query list] {}]
# fill data in right textbox
set plotted_nodes [xschem getprop rect 2 $n node]
if {[string index $plotted_nodes end] ne {\n}} {append plotted_nodes \n}
if {[string index $plotted_nodes end] ne "\n"} {append plotted_nodes \n}
.dialog.center.right.text1 insert 1.0 $plotted_nodes
graph_update_nodelist
# add stuff in textbox at end of line + 1 char (after newline)
# .dialog.center.right.text1 insert {insert lineend + 1 char} foo\n
# tkwait window .dialog
tkwait window .dialog
}
proc graph_show_measure {{action show}} {
@ -4112,7 +4186,7 @@ set tctx::global_list {
edit_prop_pos edit_prop_size editprop_sympath edit_symbol_prop_new_sel enable_dim_bg enable_stretch
en_hilight_conn_inst filetmp
flat_netlist fullscreen gaw_fd gaw_tcp_address globfilter graph_bus graph_digital
graph_sel_color graph_selected graph_sort
graph_sel_color graph_selected graph_sel_wave graph_sort
hide_empty_graphs hide_symbols hsize hspice_netlist
incr_hilight infowindow_text INITIALINSTDIR INITIALLOADDIR INITIALPROPDIR INITIALTEXTDIR
input_line_cmd input_line_data launcher_default_program light_colors line_width local_netlist_dir
@ -4124,7 +4198,7 @@ set tctx::global_list {
rawfile_loaded rcode recentfile replace_key retval retval_orig rotated_text save_initialfile search_exact
search_found search_select search_value selected_tok show_infowindow show_pin_net_names
simconf_default_geometry simconf_vpos
spiceprefix split_files svg_colors svg_font_name symbol symbol_width sym_txt tclcmd_txt
spiceprefix split_files svg_colors svg_font_name symbol symbol_width sym_txt tclcmd_txt tclstop
text_line_default_geometry textwindow_fileid textwindow_filename textwindow_w tmp_bus_char
toolbar_horiz toolbar_visible top_subckt transparent_svg undo_type
use_label_prefix use_lab_wire user_wants_copy_cell verilog_2001
@ -5099,6 +5173,10 @@ set_ne to_png {gm convert}
## ps to pdf conversion
set_ne to_pdf {ps2pdf}
# user clicked this wave
set_ne graph_sel_wave {}
# flag to force simulation stop (Esc key pressed)
set_ne tclstop 0
## undo_type: disk or memory
set_ne undo_type disk