📄 et_viewer.erl
字号:
_ -> 10 end, if S#state.is_suspended == true -> {noreply, S, infinity}; S#state.max_events == infinity -> display_more_events(Try, S); true -> Needed = S#state.max_events - queue_length(S#state.events), if Needed =< 0 -> {noreply, S, infinity}; Needed > 10 -> display_more_events(Try, S); Needed =< 10 -> display_more_events(Needed, S) end end;handle_info({'EXIT', Pid, Reason}, S) -> if Pid == S#state.collector_pid -> unlink(Pid), gs:destroy(S#state.win), {stop, Reason, S}; Pid == S#state.parent_pid -> unlink(Pid), gs:destroy(S#state.win), {stop, Reason, S}; true -> noreply(S) end;handle_info(Info, S) -> ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n", [?MODULE, self(), Info, S]), noreply(S).%%----------------------------------------------------------------------%% Func: terminate/2%% Purpose: Shutdown the server%% Returns: any (ignored by gen_server)%%----------------------------------------------------------------------terminate(_Reason, _S) -> ignore.%%----------------------------------------------------------------------%% Func: code_change/3%% Purpose: Convert process state when code is changed%% Returns: {ok, NewState}%%----------------------------------------------------------------------code_change(_OldVsn, S, _Extra) -> {ok, S}.%%%----------------------------------------------------------------------%%% Handle suspend/resume%%%----------------------------------------------------------------------reply(Reply, S) -> case queue_length(S#state.events) of _ when S#state.is_suspended == true -> {reply, Reply, S, infinity}; _ when S#state.max_events == infinity -> {reply, Reply, S, 500}; N when N >= S#state.max_events -> {reply, Reply, S, infinity}; _ -> {reply, Reply, S, 0} end.noreply(S) -> case queue_length(S#state.events) of _ when S#state.is_suspended == true -> {noreply, S, infinity}; _ when S#state.max_events == infinity -> {noreply, S, 500}; N when N >= S#state.max_events -> {noreply, S, infinity}; _ -> {noreply, S, 0} end.do_suspend(S) -> config_suspend(S#state{is_suspended = true}).do_resume(S) -> config_suspend(S#state{is_suspended = false}).config_suspend(S) -> Suspended = S#state.is_suspended, gs:config(refresh, [{enable, not Suspended}]), gs:config(refresh_all, [{enable, not Suspended}]), gs:config(clear_all, [{enable, not Suspended}]), S.refresh_main_window(S) -> Pid = S#state.collector_pid, Key = S#state.first_event, case et_collector:iterate(Pid, Key, -1) of Prev when Prev == Key -> scroll_first(S); _Prev -> S2 = S#state{last_event = S#state.first_event}, clear_canvas(S2) end. scroll_first(S) -> S2 = S#state{first_event = first, last_event = first}, clear_canvas(S2).scroll_prev(S) -> Try = case S#state.max_events of infinity -> -10; Max -> -Max end, Key = et_collector:iterate(S#state.collector_pid, S#state.first_event, Try), S2 = S#state{first_event = Key, last_event = Key}, clear_canvas(S2).scroll_next(S) -> S2 = S#state{first_event = S#state.last_event}, clear_canvas(S2).scroll_up(S) -> Key = et_collector:iterate(S#state.collector_pid, S#state.first_event, -5), S2 = S#state{first_event = Key, last_event = Key}, clear_canvas(S2).scroll_down(S) -> Key = et_collector:iterate(S#state.collector_pid, S#state.first_event, 5), S2 = S#state{first_event = Key, last_event = Key}, clear_canvas(S2).scroll_last(S) -> S2 = S#state{first_event = last, last_event = last}, clear_canvas(S2).change_display_mode(Mode, S) -> case Mode of all -> S2 = S#state{display_mode = Mode}, refresh_main_window(S2); {search_actors, _Dir, _Key, []} -> S2 = S#state{display_mode = all}, refresh_main_window(S2); {search_actors, _Dir, Key, Actors} when list(Actors) -> Pid = S#state.collector_pid, Prev = et_collector:iterate(Pid, Key, -1), S2 = S#state{first_event = Prev, last_event = Prev, display_mode = Mode}, clear_canvas(S2) end.close_all(S) -> et_collector:multicast(S#state.collector_pid, close), timer:sleep(timer:seconds(1)), spawn(et_collector, stop, [S#state.collector_pid]), gs:destroy(S#state.win), {stop, shutdown, S}.close_all_others(S) -> Fun = fun({{subscriber, Pid}, _}) -> if Pid == self() -> ignore; true -> unlink(Pid), Pid ! {et, close} end end, All = et_collector:dict_match(S#state.collector_pid, {{subscriber, '_'}, '_'}), lists:foreach(Fun, All), noreply(S).click_error(Click, S) -> gs:config(S#state.canvas, beep), io:format("~p: ignored: ~p~n", [?MODULE, Click]).%%%----------------------------------------------------------------------%%% Clone viewer%%%----------------------------------------------------------------------open_viewer(Scale, FilterName, Actors, S) -> Filters = [{dict_insert, {filter, F#filter.name}, F#filter.function} || F <- S#state.filters], Options = [{parent_pid, S#state.parent_pid}, {title, S#state.title}, {collector_pid, S#state.collector_pid}, {is_suspended, S#state.is_suspended}, {detail_level, S#state.detail_level}, {active_filter, FilterName}, {event_order, S#state.event_order}, {first_event, S#state.first_event}, {max_events, S#state.max_events}, {max_actors, S#state.max_actors}, {hide_actions, S#state.hide_actions}, {hide_unknown, S#state.hide_unknown}, {is_suspended, S#state.is_suspended}, {actors, Actors}, {scale, Scale}, {width, S#state.width}, {height, S#state.height} | Filters], case start_link(Options) of {ok, ViewerPid} -> unlink(ViewerPid), ok; {error, Reason} -> ok = error_logger:format("~p: Failed to start a new window: ~p~n", [?MODULE, Reason]) end.%%%----------------------------------------------------------------------%%% Handle graphics%%%----------------------------------------------------------------------create_main_window(S) -> Font = select_font(S#state.scale), GS = gs:start(), Name = name_to_string(S#state.active_filter), Title = case S#state.title of undefined -> atom_to_list(?MODULE); Explicit -> name_to_string(Explicit) end, WinOpt = [{title, Title ++ " (filter: " ++ Name ++ ")"}, {configure, true}, {width, S#state.width}, {height, S#state.height}], Win = gs:window(GS, WinOpt), Bar = gs:menubar(Win, []), create_file_menu(Bar), create_viewer_menu(Bar), create_collector_menu(Bar), gs:menubutton(filter_button, Bar, [{label, {text, "Filter"}}]), create_filter_menu(S#state.active_filter, S#state.filters), create_help_menu(Bar), config_suspend(S), PackerOpt = [{packer_x, [{fixed, 5}, {fixed, 40}, {fixed, 40}, {stretch, 1}, {fixed, 5}]}, {packer_y, [{fixed, 30}, {fixed, 30}, {stretch, 1}, {fixed, 30}]}, {x, 0}, {y, 30}], Packer = gs:frame(Win, PackerOpt), gs:checkbutton(suspended, Packer, [{label,{text,"Freeze"}}, {x, 10}, {y, 0}, {width, 120}, {align, w}, {select, S#state.is_suspended}]), gs:checkbutton(hide_actions, Packer, [{label,{text,"Hide From=To"}}, {x, 10}, {y, 20}, {width, 120}, {align, w}, {select, S#state.hide_actions}]), gs:checkbutton(hide_unknown, Packer, [{label,{text,"Hide Unknown"}}, {x, 10}, {y, 40}, {width, 120}, {align, w}, {select, S#state.hide_unknown}]), gs:scale(Packer, [{text,"Detail Level"}, {range, {?detail_level_min, ?detail_level_max}}, {orient, horizontal}, {x, 150}, {y, 0}, {height, 65}, {width, 200}, {pos, S#state.detail_level}, {data, detail_level}]), CanvasW = calc_canvas_width(S), CanvasH = calc_canvas_height(S), CanOpt = [{pack_xy, {{2, 4}, 3}}, {vscroll, right}, {hscroll, bottom}, {scrollregion, {2, 2, CanvasW, CanvasH}}], Canvas = gs:canvas(Packer, CanOpt), gs:config(Canvas, [{buttonpress, true}, {buttonrelease, true}]), gs:config(Packer, [{width, S#state.width}, {height, S#state.height}]), gs:config(Win, [{map, true}, {keypress, true}]), S2 = S#state{title = Title, win = Win, font = Font, packer = Packer, canvas_width = CanvasW, canvas_height = CanvasH, canvas = Canvas, y_pos = ?initial_y * S#state.scale}, draw_all_actors(S2).select_font(Scale) when integer(Scale) -> case Scale of 1 -> {courier, 7}; 2 -> {courier, 10}; 3 -> {courier, 12}; 4 -> {courier, 14}; S -> {courier, S * 4} end.create_file_menu(Bar) -> Button = gs:menubutton(Bar, [{label, {text, "File"}}]), Menu = gs:menu(Button, []), gs:menuitem(close_all, Menu, [{label, {text, "Close Collector and all Viewers (C) "}}]), gs:menuitem(close_all_others, Menu, [{label, {text, "Close other Viewers, but keep Collector (c)"}}]), gs:menuitem(close, Menu, [{label, {text, "Close this Viewer, but keep Collector"}}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(clear_all, Menu, [{label, {text, "Clear Collector"}}]), gs:menuitem(load_all, Menu, [{label, {text, "Load Collector from the file \"et_viewer.log\""}}]), gs:menuitem(save_all, Menu, [{label, {text, "Save Collector to the file \"et_viewer.log\""}}]).create_viewer_menu(Bar) -> Button = gs:menubutton(Bar, [{label, {text, "Viewer"}}]), Menu = gs:menu(Button, []), gs:menuitem(Menu, [{label, {text, "Scroll this Viewer"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(first, Menu, [{label, {text, "First (f)"}}]), gs:menuitem(prev, Menu, [{label, {text, "Prev (p)"}}]), gs:menuitem(next, Menu, [{label, {text, "Next (n)"}}]), gs:menuitem(last, Menu, [{label, {text, "Last (l)"}}]), gs:menuitem(refresh, Menu, [{label, {text, "Refresh (r)"}}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(up, Menu, [{label, {text, "Up 5 (Up)"}}]), gs:menuitem(down, Menu, [{label, {text, "Down 5 (Down)"}}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(Menu, [{label, {text, "Search in this Viewer"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem({mode, all}, Menu, [{label, {text, "Abort search. Display all (a)"}}]).create_collector_menu(Bar) -> Button = gs:menubutton(Bar, [{label, {text, "Collector"}}]), Menu = gs:menu(Button, []), gs:menuitem(Menu, [{label, {text, "Scroll all Viewers"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(first_all, Menu, [{label, {text, "First (F)"}}]), gs:menuitem(prev_all, Menu, [{label, {text, "Prev (P)"}}]), gs:menuitem(next_all, Menu, [{label, {text, "Next (N)"}}]), gs:menuitem(last_all, Menu, [{label, {text, "Last (L)"}}]), gs:menuitem(refresh_all, Menu, [{label, {text, "Refresh (R)"}}]).create_filter_menu(ActiveFilterName, Filters) -> Menu = gs:menu(filter_menu, filter_button, []), Item = fun(F, N) when F#filter.name == collector -> Label = lists:concat([pad_string(F#filter.name, 20), "(0)"]), gs:menuitem(Menu, [{label, {text, Label}}, {data, F}]), N + 1; (F, N) -> Label = lists:concat([pad_string(F#filter.name, 20), "(", N, ")"]), gs:menuitem(Menu, [{label, {text, Label}}, {data, F}]), N + 1 end, gs:menuitem(Menu, [{label, {text, "Same Filter New Scale"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), {value, Filter} = lists:keysearch(ActiveFilterName, #filter.name, Filters), Same = lists:concat([pad_string(ActiveFilterName, 20), "(=)"]), Larger = lists:concat([pad_string(ActiveFilterName, 20), "(+)"]), Smaller = lists:concat([pad_string(ActiveFilterName, 20), "(-)"]), gs:menuitem(Menu, [{label, {text, Same}}, {data, Filter}]), gs:menuitem(Menu, [{label, {text, Smaller}}, {data, Filter}]), gs:menuitem(Menu, [{label, {text, Larger}}, {data, Filter}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(Menu, [{label, {text, "New Filter Same Scale"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), lists:foldl(Item, 1, Filters).create_help_menu(Bar) -> Button = gs:menubutton(Bar, [{label, {text, "Help"}}]), Menu = gs:menu(Button, []), gs:menuitem(Menu, [{label, {text, "Display details of an event"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{label, {text, " Single click on the name tag or the arrow (Mouse-1)"}}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(Menu, [{label, {text, "Toggle actor search"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{label, {text, " Single click on the name tag (Mouse-1)"}}, {enable,false}]), gs:menuitem(Menu, [{itemtype, separator}]), gs:menuitem(Menu, [{label, {text, "Move actor"}}, {bg, lightblue}, {enable,false}]), gs:menuitem(Menu, [{label, {text, " se drag and drop on name tag (Mouse-1)"}}, {enable,false}]).clear_canvas(S) -> gs:destroy(S#state.canvas), CanvasW = calc_canvas_width(S), CanvasH = calc_canvas_height(S), CanOpt = [{pack_xy, {{2, 4}, 3}}, {vscroll, right}, {hscroll, bottom}, {scrollregion, {2, 2, CanvasW, CanvasH}}], Canvas = gs:canvas(S#state.packer, CanOpt), gs:config(S#state.packer, [{width, S#state.width}, {height, S#state.height}]), gs:config(Canvas, [{buttonpress, true}, {buttonrelease, true}]), S2 = S#state{refresh_needed = false, y_pos = ?initial_y * S#state.scale, canvas = Canvas, canvas_width = CanvasW, canvas_height = CanvasH, events = queue_new()}, draw_all_actors(S2).calc_canvas_width(S) -> Min = calc_min_actors(S), CanvasW = ((2 * ?initial_x) + (Min * ?incr_x)) * S#state.scale, lists:max([CanvasW, S#state.width - (15 * S#state.scale), S#state.canvas_width]).calc_canvas_height(S) -> Min = calc_min_events(S), CanvasH = ((2 * ?initial_y) + (Min * ?incr_y)) * S#state.scale, lists:max([CanvasH, S#state.height - (4 * 30), S#state.canvas_height]).calc_min_actors(S) -> Max = S#state.max_actors,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -