在回家后继续卷的时候,机缘巧合发现的。
想起以前项目中,花费那么多时间都没解决的问题,答案居然就在帮助文档中躺着,分享一下,虽然不是所需要的效果,但好歹迈出了第一步。
做了二次开发的小伙伴可能清楚,通过BCWindowCreate创建的window窗口会占用当前进程,在点击界面上其他命令的时候,窗口就会被摧毁。例如上述图片,编辑器也会变为灰色。这的确很苦恼,这就得要求在开发命令的时候,涉及到的操作全都得写在界面上,这很麻烦,很麻烦,很麻烦。。。。。怎么就不能像HyperWork一样,所创建的窗口互不干涉。
现在解决办法来了,这是23.1.0帮助文档里写的,监控ABAQUS计算进程的案例,低版本的能否运行,大家自行判断。重点来了,修改create_options_frame函数内的功能就可以实现创建控件,但是当控件所绑定的函数需要修改模型的操作,例如创建点,程序就会闪退。目前还不知道怎么解决。
from __future__ import annotations
import os
import re
import shlex
import subprocess
from ansa import base, session, guitk, constants, analysis_tools
_RE_FLOAT = r"[-+]?(?:(?:\d*\.?\d+)|(?:\d+\.?\d*))(?:[eE][-+]?\d+)?"
def _show_msg_win_critical(msg):
msg_win = guitk.BCMessageWindowCreate(guitk.constants.BCMessageBoxCritical, msg, False)
guitk.BCMessageWindowSetRejectButtonVisible(msg_win, False)
guitk.BCMessageWindowExecute(msg_win)
def _show_msg_win_information(msg):
msg_win = guitk.BCMessageWindowCreate(guitk.constants.BCMessageBoxInformation, msg, False)
guitk.BCMessageWindowSetRejectButtonVisible(msg_win, False)
guitk.BCMessageWindowExecute(msg_win)
def _show_msg_win_question(msg):
msg_win = guitk.BCMessageWindowCreate(guitk.constants.BCMessageBoxQuestion, msg, False)
return guitk.BCMessageWindowExecute(msg_win) == guitk.constants.BCRetKey
def _get_path_from_line_edit_path(ledit_path):
file_path = guitk.BCLineEditPathLineEditText(ledit_path)
if not file_path:
return ""
return os.path.abspath(file_path)
class AbqTextViewer(analysis_tools.RunSolverTextViewer):
def __init__(self, run_abq: RunAbq, name, is_console=False):
super().__init__(name=name, is_console=is_console, supports_errors=True,
supports_warnings=True, supports_notes=True)
self._run_abq = run_abq
def get_file_path(self):
dir_name = self._run_abq.get_output_dir_name()
job_name = self._run_abq.get_job_name()
file_path = os.path.join(dir_name, job_name) + "." + self.name.lower()
return file_path
def has_error_in_line(self, text):
return text.startswith(" ***ERROR") or text.startswith("***ERROR")
def has_warning_in_line(self, text):
return text.startswith(" ***WARNING") or text.startswith("***WARNING")
def has_note_in_line(self, text):
return text.startswith(" ***NOTE") or text.startswith("***NOTE")
class AbqTotalIterationsPlotViewer(analysis_tools.RunSolverPlotViewer):
def __init__(self, run_abq: RunAbq):
super().__init__(name="Total Iterations")
self._run_abq = run_abq
self._numbers_pattern = r"^\s*" + 5*(r"\d+\s+") + r"(\d+)\s+" + "("+_RE_FLOAT+")" +\
r"\s+" + _RE_FLOAT + r"\s+" + _RE_FLOAT + r"\s*"
def get_file_path(self):
return os.path.join(self._run_abq.get_output_dir_name(),
self._run_abq.get_job_name()) + ".sta"
def set_plot_options(self):
self.set_axis_title(pos="yleft", title="Total Iterations")
self.set_axis_title(pos="xbottom", title="Total Time")
self.set_curve_name(name="Total Iterations")
self.set_curve_color(r=0, g=0, b=255)
def file_updated(self, new_text, file_index):
points = []
for line in new_text.splitlines():
match = re.search(self._numbers_pattern, line)
if match:
total_iters = float(match.group(1))
total_time = float(match.group(2))
points.append((total_time, total_iters))
if len(points) > 0:
self.append_points_to_curve(points)
class RunAbq(analysis_tools.RunSolver):
def __init__(self):
super().__init__(window_caption="Run Abaqus", query_status_interval=1.0,
initial_actions="Start")
self._out_ledit_path = None
self._exec_ledit_path = None
self._scratch_dir = None
self._data_check_cbox = None
self._cpus_ledit = None
self._mem_size_ledit = None
self._mem_unit_combo = None
self._log_file = None
self._process = None
self._suspend_process = None
self._resume_process = None
def _get_output_file_path(self):
return _get_path_from_line_edit_path(self._out_ledit_path)
def get_output_dir_name(self):
return os.path.dirname(self._get_output_file_path())
def get_job_name(self):
return os.path.splitext(os.path.basename(self._get_output_file_path()))[0]
def _get_lock_file(self):
dir_name = self.get_output_dir_name()
job_name = self.get_job_name()
file_path = os.path.join(dir_name, job_name) + ".lck"
return file_path
def _get_exec_file_path(self):
return _get_path_from_line_edit_path(self._exec_ledit_path)
def _get_log_file_path(self):
return os.path.join(self.get_output_dir_name(), self.get_job_name()) + ".log"
def _get_mem_size(self):
mem_size = guitk.BCLineEditGetText(self._mem_size_ledit).strip()
if not mem_size:
return ""
mem_unit = guitk.BCComboBoxCurrentText(self._mem_unit_combo)
return mem_size + mem_unit
def _get_solver_args(self):
args = []
args.append(f"job={self.get_job_name()}")
args.append("interactive")
if guitk.BCCheckBoxIsChecked(self._data_check_cbox):
args.append("datacheck")
cpus_num = guitk.BCLineEditGetInt(self._cpus_ledit)
if cpus_num != guitk.constants.blank:
args.append(f"cpus={cpus_num}")
mem_size = self._get_mem_size()
if mem_size:
args.append(f"memory={mem_size}")
scratch_dir_name = _get_path_from_line_edit_path(self._scratch_dir)
if scratch_dir_name:
args.append(f"scratch={scratch_dir_name}")
additional_args = guitk.BCLineEditGetText(self._additional_args_ledit).strip()
if additional_args:
for arg in shlex.split(additional_args):
args.append(arg)
return args
def _check_user_input(self):
if not os.path.exists(self._get_exec_file_path()):
_show_msg_win_critical("Executable does not exist")
return False
if not self._get_output_file_path():
_show_msg_win_critical("Output file is empty")
return False
return True
def _init(self):
if not self._check_user_input():
return False
if os.path.exists(self._get_lock_file()):
ret = _show_msg_win_question("Lock file detected. Delete and continue?")
if not ret:
return False
os.remove(self._get_lock_file())
if base.OutputAbaqus(self._get_output_file_path()) == 0:
_show_msg_win_critical("Output failed")
return False
self._log_file = open(self._get_log_file_path(), "w")
return True
def handle_action(self, action):
if action == "Start":
if not self._init():
return
self.initialize()
self.set_busy()
args = [self._get_exec_file_path()] + self._get_solver_args()
self._process = subprocess.Popen(args, cwd=self.get_output_dir_name(),
stdout=self._log_file, stderr=subprocess.STDOUT)
elif action == "Terminate":
args = [self._get_exec_file_path(), "terminate", f"job={self.get_job_name()}"]
subprocess.Popen(args, cwd=self.get_output_dir_name(),
stdout=self._log_file, stderr=subprocess.STDOUT)
elif action == "Suspend":
args = [self._get_exec_file_path(), "suspend", f"job={self.get_job_name()}"]
self._suspend_process = subprocess.Popen(args, cwd=self.get_output_dir_name(),
stdout=self._log_file, stderr=subprocess.STDOUT)
elif action == "Resume":
self.set_busy()
args = [self._get_exec_file_path(), "resume", f"job={self.get_job_name()}"]
self._resume_process = subprocess.Popen(args, cwd=self.get_output_dir_name(),
stdout=self._log_file, stderr=subprocess.STDOUT)
else:
raise Exception("Invalid action:", action)
def query_status(self):
if self._process is not None:
ret = self._process.poll()
if ret is not None:
self._process = None
if ret == 0:
return "Completed"
else:
return "Failed"
if self._suspend_process is not None:
ret = self._suspend_process.poll()
if ret is not None:
self._suspend_process = None
if ret == 0:
return "Suspended"
else:
return "Running"
if self._resume_process is not None:
ret = self._resume_process.poll()
if ret is not None:
self._resume_process = None
if ret == 0:
return "Resumed"
else:
return "Suspended"
return "Running"
def status_changed(self, new_status):
if new_status == "Running":
self.show_only_actions(actions=("Terminate", "Suspend"))
elif new_status == "Resumed":
self.show_only_actions(actions=("Terminate", "Suspend"))
elif new_status == "Suspended":
self.set_idle()
self.show_only_actions(actions="Resume")
elif new_status == "Completed":
self.set_idle()
self.show_only_actions(actions="Start")
self._log_file.close()
_show_msg_win_information("Job was completed")
elif new_status == "Failed":
self.set_idle()
self.show_only_actions(actions="Start")
self._log_file.close()
_show_msg_win_critical("Job failed")
else:
raise Exception("Invalid new_status:", new_status)
def create_analysis_top_frame(self, parent):
g_layout = guitk.BCGridLayoutCreate(parent)
guitk.BCGridLayoutSetColStretch(g_layout, 0, 0)
guitk.BCGridLayoutSetColStretch(g_layout, 1, 1)
label = guitk.BCLabelCreate(g_layout, "Output file")
guitk.BCGridLayoutAddWidget(g_layout, label, 0, 0, guitk.constants.BCAlignAuto)
self._out_ledit_path = guitk.BCLineEditPathCreate(g_layout, guitk.constants.BCHistoryFiles,
"", guitk.constants.BCHistorySaveAs, "RunAbq_OutputPath")
guitk.BCLineEditPathAddFilter(self._out_ledit_path, "ABAQUS", "inp")
guitk.BCGridLayoutAddWidget(g_layout, self._out_ledit_path, 0, 1, guitk.constants.BCAlignAuto)
def create_options_frame(self, parent):
g_layout = guitk.BCGridLayoutCreate(parent)
guitk.BCGridLayoutSetColStretch(g_layout, 0, 0)
guitk.BCGridLayoutSetColStretch(g_layout, 1, 1)
label = guitk.BCLabelCreate(g_layout, "Executable")
guitk.BCGridLayoutAddWidget(g_layout, label, 0, 0, guitk.constants.BCAlignAuto)
self._exec_ledit_path = guitk.BCLineEditPathCreate(g_layout, guitk.constants.BCHistoryFiles,
"", guitk.constants.BCHistorySelect, "RunAbq_Executable")
guitk.BCGridLayoutAddWidget(g_layout, self._exec_ledit_path, 0, 1, guitk.constants.BCAlignAuto)
label = guitk.BCLabelCreate(g_layout, "Scratch directory")
guitk.BCGridLayoutAddWidget(g_layout, label, 1, 0, guitk.constants.BCAlignAuto)
self._scratch_dir = guitk.BCLineEditPathCreate(g_layout, guitk.constants.BCHistoryFiles,
"", guitk.constants.BCHistorySelect, "RunAbq_ScratchDirectory")
guitk.BCGridLayoutAddWidget(g_layout, self._scratch_dir, 1, 1, guitk.constants.BCAlignAuto)
self._data_check_cbox = guitk.BCCheckBoxCreate(g_layout, "Data check")
guitk.BCGridLayoutAddMultiCellWidget(g_layout, self._data_check_cbox, 2, 2, 0, 1,
guitk.constants.BCAlignAuto)
label = guitk.BCLabelCreate(g_layout, "Number of CPUs")
guitk.BCGridLayoutAddWidget(g_layout, label, 3, 0, guitk.constants.BCAlignAuto)
self._cpus_ledit = guitk.BCLineEditCreateInt(g_layout)
guitk.BCGridLayoutAddWidget(g_layout, self._cpus_ledit, 3, 1, guitk.constants.BCAlignAuto)
label = guitk.BCLabelCreate(g_layout, "Memory size")
guitk.BCGridLayoutAddWidget(g_layout, label, 4, 0, guitk.constants.BCAlignAuto)
frame = guitk.BCFrameCreate(g_layout)
h_layout = guitk.BCBoxLayoutCreate(frame, guitk.constants.BCHorizontal)
guitk.BCBoxLayoutSetMargin(h_layout, 0)
guitk.BCBoxLayoutSetSpacing(h_layout, 0)
self._mem_size_ledit = guitk.BCLineEditCreateInt(h_layout)
guitk.BCBoxLayoutSetStretchFactor(h_layout, self._mem_size_ledit, 1)
self._mem_unit_combo = guitk.BCComboBoxCreate(h_layout, ("MB", "GB", "%"))
guitk.BCBoxLayoutSetStretchFactor(h_layout, self._mem_unit_combo, 0)
guitk.BCGridLayoutAddWidget(g_layout, frame, 4, 1, guitk.constants.BCAlignAuto)
label = guitk.BCLabelCreate(g_layout, "Additional arguments")
guitk.BCGridLayoutAddWidget(g_layout, label, 5, 0, guitk.constants.BCAlignAuto)
self._additional_args_ledit = guitk.BCLineEditCreate(g_layout, "")
guitk.BCGridLayoutAddWidget(g_layout, self._additional_args_ledit, 5, 1,
guitk.constants.BCAlignAuto)
guitk.BCSpacerCreate(parent)
def create_run_abq_win():
run_abq = RunAbq()
run_abq.add_viewer(AbqTextViewer(run_abq, name="LOG", is_console=True))
run_abq.add_viewer(AbqTextViewer(run_abq, name="DAT"))
run_abq.add_viewer(AbqTextViewer(run_abq, name="MSG"))
run_abq.add_viewer(AbqTextViewer(run_abq, name="STA"))
run_abq.add_viewer_group(group="Plots")
run_abq.add_viewer(AbqTotalIterationsPlotViewer(run_abq), group="Plots")
run_abq.add_action(action="Start", icon="run_small.svg")
run_abq.add_action(action="Resume", icon="run_small.svg")
run_abq.add_action_group(group="Stop", icon="rect_red_small.svg")
run_abq.add_action(action="Terminate", icon="rect_red_small.svg", group="Stop")
run_abq.add_action(action="Suspend", icon="media_pause.svg", group="Stop")
run_abq.create_window()
create_run_abq_win ()