queued MDI: support in task & Axis
For MDI, the interpreter is now fed through mdi_input_queue. The test for enabling an MDI input window now is: interp_state == INTERP_IDLE or (mdi_input_queue.len() < maxlen && exec_mode == MDI) see axis.tcl and axis.py diffs for details.
This commit is contained in:
parent
394be3e659
commit
55d93a8fea
8 changed files with 134 additions and 24 deletions
|
|
@ -2001,11 +2001,18 @@ proc update_state {args} {
|
|||
set ::position [concat [_ "Position:"] $coord_str $display_str]
|
||||
}
|
||||
|
||||
if {$::task_state == $::STATE_ON && $::interp_state == $::INTERP_IDLE} {
|
||||
if {$::last_interp_state != $::INTERP_IDLE || $::last_task_state != $::task_state} {
|
||||
set_mode_from_tab
|
||||
}
|
||||
enable_group $::manual
|
||||
if {$::task_state == $::STATE_ON} {
|
||||
if {$::interp_state == $::INTERP_IDLE} {
|
||||
if {$::last_interp_state != $::INTERP_IDLE || $::last_task_state != $::task_state} {
|
||||
set_mode_from_tab
|
||||
}
|
||||
enable_group $::manual
|
||||
}
|
||||
if {$::queued_mdi_commands < $::max_queued_mdi_commands } {
|
||||
enable_group $::manual
|
||||
} else {
|
||||
disable_group $::manual
|
||||
}
|
||||
} else {
|
||||
disable_group $::manual
|
||||
}
|
||||
|
|
@ -2085,6 +2092,8 @@ set motion_mode 0
|
|||
set kinematics_type -1
|
||||
set metric 0
|
||||
set max_speed 1
|
||||
set queued_mdi_commands 0
|
||||
set max_queued_mdi_commands 10
|
||||
trace variable taskfile w update_title
|
||||
trace variable machine w update_title
|
||||
trace variable taskfile w queue_update_state
|
||||
|
|
@ -2104,6 +2113,7 @@ trace variable motion_mode w queue_update_state
|
|||
trace variable kinematics_type w queue_update_state
|
||||
trace variable on_any_limit w queue_update_state
|
||||
trace variable motion_mode w joint_mode_switch
|
||||
trace variable queued_mdi_commands w queue_update_state
|
||||
|
||||
set editor_deleted 0
|
||||
|
||||
|
|
|
|||
|
|
@ -269,6 +269,9 @@ int emcFormat(NMLTYPE type, void *buffer, CMS * cms)
|
|||
case EMC_TASK_PLAN_END_TYPE:
|
||||
((EMC_TASK_PLAN_END *) buffer)->update(cms);
|
||||
break;
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
((EMC_TASK_PLAN_EXECUTE_INTERNAL *) buffer)->update(cms);
|
||||
break;
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
((EMC_TASK_PLAN_EXECUTE *) buffer)->update(cms);
|
||||
break;
|
||||
|
|
@ -625,6 +628,8 @@ const char *emc_symbol_lookup(long type)
|
|||
return "EMC_TASK_PLAN_CLOSE";
|
||||
case EMC_TASK_PLAN_END_TYPE:
|
||||
return "EMC_TASK_PLAN_END";
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
return "EMC_TASK_PLAN_EXECUTE_INTERNAL";
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
return "EMC_TASK_PLAN_EXECUTE";
|
||||
case EMC_TASK_PLAN_INIT_TYPE:
|
||||
|
|
@ -2634,10 +2639,22 @@ void EMC_TRAJ_SET_MODE::update(CMS * cms)
|
|||
}
|
||||
|
||||
/*
|
||||
* NML/CMS Update function for EMC_TASK_PLAN_EXECUTE
|
||||
* NML/CMS Update function for EMC_TASK_PLAN_EXECUTE_INTERNAL
|
||||
* Automatically generated by NML CodeGen Java Applet.
|
||||
* on Sat Oct 11 13:45:17 UTC 2003
|
||||
*/
|
||||
void EMC_TASK_PLAN_EXECUTE_INTERNAL::update(CMS * cms)
|
||||
{
|
||||
|
||||
EMC_TASK_CMD_MSG::update(cms);
|
||||
cms->update(command, 256);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* NML/CMS Update function for EMC_TASK_PLAN_EXECUTE
|
||||
* manually generated by mhaberler Thu May 24 15:43:22
|
||||
*/
|
||||
void EMC_TASK_PLAN_EXECUTE::update(CMS * cms)
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,9 @@ class PM_CARTESIAN;
|
|||
#define EMC_TASK_PLAN_OPEN_TYPE ((NMLTYPE) 506)
|
||||
#define EMC_TASK_PLAN_RUN_TYPE ((NMLTYPE) 507)
|
||||
#define EMC_TASK_PLAN_READ_TYPE ((NMLTYPE) 508)
|
||||
#define EMC_TASK_PLAN_EXECUTE_TYPE ((NMLTYPE) 509)
|
||||
// this enables distinguishing MDI commands coming from the UI from
|
||||
// internally generated execute commands
|
||||
#define EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE ((NMLTYPE) 509)
|
||||
#define EMC_TASK_PLAN_PAUSE_TYPE ((NMLTYPE) 510)
|
||||
#define EMC_TASK_PLAN_STEP_TYPE ((NMLTYPE) 511)
|
||||
#define EMC_TASK_PLAN_RESUME_TYPE ((NMLTYPE) 512)
|
||||
|
|
@ -175,6 +177,7 @@ class PM_CARTESIAN;
|
|||
#define EMC_TASK_PLAN_SET_OPTIONAL_STOP_TYPE ((NMLTYPE) 517)
|
||||
#define EMC_TASK_PLAN_SET_BLOCK_DELETE_TYPE ((NMLTYPE) 518)
|
||||
#define EMC_TASK_PLAN_OPTIONAL_STOP_TYPE ((NMLTYPE) 519)
|
||||
#define EMC_TASK_PLAN_EXECUTE_TYPE ((NMLTYPE) 520)
|
||||
|
||||
#define EMC_TASK_STAT_TYPE ((NMLTYPE) 599)
|
||||
|
||||
|
|
|
|||
|
|
@ -1253,6 +1253,19 @@ class EMC_TASK_PLAN_READ:public EMC_TASK_CMD_MSG {
|
|||
void update(CMS * cms);
|
||||
};
|
||||
|
||||
class EMC_TASK_PLAN_EXECUTE_INTERNAL:public EMC_TASK_CMD_MSG {
|
||||
public:
|
||||
EMC_TASK_PLAN_EXECUTE_INTERNAL():EMC_TASK_CMD_MSG(EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE,
|
||||
sizeof(EMC_TASK_PLAN_EXECUTE_INTERNAL))
|
||||
{
|
||||
};
|
||||
|
||||
// For internal NML/CMS use only.
|
||||
void update(CMS * cms);
|
||||
|
||||
char command[LINELEN];
|
||||
};
|
||||
|
||||
class EMC_TASK_PLAN_EXECUTE:public EMC_TASK_CMD_MSG {
|
||||
public:
|
||||
EMC_TASK_PLAN_EXECUTE():EMC_TASK_CMD_MSG(EMC_TASK_PLAN_EXECUTE_TYPE,
|
||||
|
|
@ -1419,6 +1432,7 @@ class EMC_TASK_STAT:public EMC_TASK_STAT_MSG {
|
|||
// (only useful for new interpreter.)
|
||||
int task_paused; // non-zero means task is paused
|
||||
double delayLeft; // delay time left of G4, M66..
|
||||
int queuedMDIcommands; // current length of MDI input queue
|
||||
};
|
||||
|
||||
// declarations for EMC_TOOL classes
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ EMC_TASK_STAT_MSG(EMC_TASK_STAT_TYPE, sizeof(EMC_TASK_STAT))
|
|||
interpreter_errcode = 0;
|
||||
task_paused = 0;
|
||||
delayLeft = 0.0;
|
||||
queuedMDIcommands = 0;
|
||||
}
|
||||
|
||||
EMC_TOOL_STAT::EMC_TOOL_STAT():
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ static EMC_TOOL_SET_NUMBER *emc_tool_set_number_msg;
|
|||
static EMC_TASK_SET_MODE *mode_msg;
|
||||
static EMC_TASK_SET_STATE *state_msg;
|
||||
static EMC_TASK_PLAN_RUN *run_msg;
|
||||
static EMC_TASK_PLAN_EXECUTE *execute_msg;
|
||||
static EMC_TASK_PLAN_EXECUTE_INTERNAL *execute_msg;
|
||||
static EMC_TASK_PLAN_OPEN *open_msg;
|
||||
static EMC_TASK_PLAN_SET_OPTIONAL_STOP *os_msg;
|
||||
static EMC_TASK_PLAN_SET_BLOCK_DELETE *bd_msg;
|
||||
|
|
@ -433,6 +433,11 @@ static int mdi_execute_wait = 0;
|
|||
// Side queue to store MDI commands
|
||||
static NML_INTERP_LIST mdi_execute_queue;
|
||||
|
||||
// MDI input queue
|
||||
static NML_INTERP_LIST mdi_input_queue;
|
||||
#define MAX_MDI_QUEUE 10
|
||||
static int max_mdi_queued_commands = MAX_MDI_QUEUE;
|
||||
|
||||
/*
|
||||
checkInterpList(NML_INTERP_LIST *il, EMC_STAT *stat) takes a pointer
|
||||
to an interpreter list and a pointer to the EMC status, pops each NML
|
||||
|
|
@ -712,8 +717,10 @@ static void mdi_execute_hook(void)
|
|||
!mdi_execute_wait &&
|
||||
!mdi_execute_next) {
|
||||
|
||||
if (emc_debug & EMC_DEBUG_INTERP)
|
||||
rcs_print("mdi_execute_hook: MDI command '%s' done\n", emcStatus->task.command);
|
||||
// finished. Check for dequeuing of queued MDI command is done in emcTaskPlan().
|
||||
if (emc_debug & EMC_DEBUG_TASK_ISSUE)
|
||||
rcs_print("mdi_execute_hook: MDI command '%s' done (remaining: %d)\n",
|
||||
emcStatus->task.command, mdi_input_queue.len());
|
||||
emcStatus->task.command[0] = 0;
|
||||
emcStatus->task.interpState = EMC_TASK_INTERP_IDLE;
|
||||
}
|
||||
|
|
@ -724,7 +731,7 @@ static void mdi_execute_hook(void)
|
|||
|
||||
mdi_execute_next = 0;
|
||||
|
||||
EMC_TASK_PLAN_EXECUTE msg;
|
||||
EMC_TASK_PLAN_EXECUTE_INTERNAL msg;
|
||||
msg.command[0] = (char) 0xff;
|
||||
|
||||
interp_list.append(msg);
|
||||
|
|
@ -955,7 +962,7 @@ static int emcTaskPlan(void)
|
|||
|
||||
// queued commands
|
||||
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
// resynch the interpreter, since we may have moved
|
||||
// externally
|
||||
emcTaskIssueCommand(&taskPlanSynchCmd);
|
||||
|
|
@ -983,8 +990,8 @@ static int emcTaskPlan(void)
|
|||
// otherwise we can't handle it
|
||||
|
||||
default:
|
||||
emcOperatorError(0, _("can't do that (%s) in manual mode"),
|
||||
emc_symbol_lookup(type));
|
||||
emcOperatorError(0, _("can't do that (%s:%d) in manual mode"),
|
||||
emc_symbol_lookup(type),(int) type);
|
||||
retval = -1;
|
||||
break;
|
||||
|
||||
|
|
@ -1039,7 +1046,7 @@ static int emcTaskPlan(void)
|
|||
case EMC_TASK_PLAN_INIT_TYPE:
|
||||
case EMC_TASK_PLAN_OPEN_TYPE:
|
||||
case EMC_TASK_PLAN_RUN_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
case EMC_TASK_PLAN_PAUSE_TYPE:
|
||||
case EMC_TASK_PLAN_RESUME_TYPE:
|
||||
case EMC_TASK_PLAN_SET_OPTIONAL_STOP_TYPE:
|
||||
|
|
@ -1196,7 +1203,7 @@ static int emcTaskPlan(void)
|
|||
case EMC_TASK_SET_MODE_TYPE:
|
||||
case EMC_TASK_SET_STATE_TYPE:
|
||||
case EMC_TASK_ABORT_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
case EMC_TASK_PLAN_PAUSE_TYPE:
|
||||
case EMC_TASK_PLAN_RESUME_TYPE:
|
||||
case EMC_TASK_PLAN_SET_OPTIONAL_STOP_TYPE:
|
||||
|
|
@ -1263,7 +1270,7 @@ static int emcTaskPlan(void)
|
|||
case EMC_SPINDLE_INCREASE_TYPE:
|
||||
case EMC_SPINDLE_DECREASE_TYPE:
|
||||
case EMC_SPINDLE_CONSTANT_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
case EMC_TASK_PLAN_PAUSE_TYPE:
|
||||
case EMC_TASK_PLAN_RESUME_TYPE:
|
||||
case EMC_TASK_PLAN_SET_OPTIONAL_STOP_TYPE:
|
||||
|
|
@ -1311,6 +1318,27 @@ static int emcTaskPlan(void)
|
|||
break; // case EMC_TASK_MODE_AUTO
|
||||
|
||||
case EMC_TASK_MODE_MDI: // ON, MDI
|
||||
|
||||
if (emcStatus->task.interpState == EMC_TASK_INTERP_IDLE &&
|
||||
// emcCommand == NULL &&
|
||||
mdi_input_queue.len() > 0) {
|
||||
// MDI done, and queued command(s) remaining:
|
||||
EMC_TASK_PLAN_EXECUTE *mdicmd = (EMC_TASK_PLAN_EXECUTE *) mdi_input_queue.get();
|
||||
if (mdicmd) {
|
||||
emcCommand = mdicmd;
|
||||
type = emcCommand->type;
|
||||
if (emc_debug & EMC_DEBUG_TASK_ISSUE)
|
||||
rcs_print("emcTaskPlan: MDI: dequeueing '%s' (remaining: %d)\n",
|
||||
mdicmd->command, mdi_input_queue.len());
|
||||
} else {
|
||||
rcs_print_error("emcTaskPlan: MDI: dequeueing NULL command? (remaining: %d)\n",
|
||||
mdi_input_queue.len());
|
||||
type = 0;
|
||||
}
|
||||
} else
|
||||
emcCommand = emcCommandBuffer->get_address();
|
||||
emcStatus->task.queuedMDIcommands = mdi_input_queue.len();
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
case EMC_NULL_TYPE:
|
||||
|
|
@ -1350,7 +1378,7 @@ static int emcTaskPlan(void)
|
|||
case EMC_TASK_SET_STATE_TYPE:
|
||||
case EMC_TASK_PLAN_INIT_TYPE:
|
||||
case EMC_TASK_PLAN_OPEN_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
case EMC_TASK_PLAN_PAUSE_TYPE:
|
||||
case EMC_TASK_PLAN_SET_OPTIONAL_STOP_TYPE:
|
||||
case EMC_TASK_PLAN_SET_BLOCK_DELETE_TYPE:
|
||||
|
|
@ -1368,6 +1396,29 @@ static int emcTaskPlan(void)
|
|||
retval = emcTaskIssueCommand(emcCommand);
|
||||
break;
|
||||
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
// in MDI, and a command currently executing.
|
||||
if (emcStatus->task.interpState != EMC_TASK_INTERP_IDLE) {
|
||||
// in MDI, and a command currently executing.
|
||||
if (mdi_input_queue.len() < max_mdi_queued_commands) {
|
||||
// space left to queue it.
|
||||
mdi_input_queue.append(emcCommand);
|
||||
emcStatus->task.queuedMDIcommands = mdi_input_queue.len();
|
||||
if (emc_debug & EMC_DEBUG_TASK_ISSUE) {
|
||||
rcs_print("MDI: queueing '%s' (queue len=%d)\n",
|
||||
execute_msg->command,mdi_input_queue.len());
|
||||
}
|
||||
} else {
|
||||
emcOperatorError(0, _("maximum number of queued MDI commands exceeded (%d)"),
|
||||
max_mdi_queued_commands);
|
||||
}
|
||||
} else {
|
||||
emcStatus->task.queuedMDIcommands = 0;
|
||||
// MDI input queue empty. Go ahead and issue right away.
|
||||
retval = emcTaskIssueCommand(emcCommand);
|
||||
}
|
||||
break;
|
||||
|
||||
case EMC_TOOL_LOAD_TOOL_TABLE_TYPE:
|
||||
case EMC_TOOL_SET_OFFSET_TYPE:
|
||||
// send to IO
|
||||
|
|
@ -1380,8 +1431,8 @@ static int emcTaskPlan(void)
|
|||
|
||||
// otherwise we can't handle it
|
||||
default:
|
||||
emcOperatorError(0, _("can't do that (%s) in MDI mode"),
|
||||
emc_symbol_lookup(type));
|
||||
emcOperatorError(0, _("can't do that (%s:%d) in MDI mode"),
|
||||
emc_symbol_lookup(type),(int) type);
|
||||
|
||||
retval = -1;
|
||||
break;
|
||||
|
|
@ -1499,7 +1550,7 @@ static int emcTaskCheckPreconditions(NMLmsg * cmd)
|
|||
case EMC_TASK_PLAN_INIT_TYPE:
|
||||
case EMC_TASK_PLAN_RUN_TYPE:
|
||||
case EMC_TASK_PLAN_SYNCH_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
return EMC_TASK_EXEC_WAITING_FOR_MOTION_AND_IO;
|
||||
break;
|
||||
|
||||
|
|
@ -2089,10 +2140,11 @@ static int emcTaskIssueCommand(NMLmsg * cmd)
|
|||
}
|
||||
break;
|
||||
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
stepping = 0;
|
||||
steppingWait = 0;
|
||||
execute_msg = (EMC_TASK_PLAN_EXECUTE *) cmd;
|
||||
execute_msg = (EMC_TASK_PLAN_EXECUTE_INTERNAL *) cmd;
|
||||
if (!all_homed() && !no_force_homing) { //!no_force_homing = force homing before MDI
|
||||
emcOperatorError(0, _("Can't issue MDI command when not homed"));
|
||||
retval = -1;
|
||||
|
|
@ -2367,7 +2419,7 @@ static int emcTaskCheckPostconditions(NMLmsg * cmd)
|
|||
case EMC_TASK_PLAN_END_TYPE:
|
||||
case EMC_TASK_PLAN_INIT_TYPE:
|
||||
case EMC_TASK_PLAN_SYNCH_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_TYPE:
|
||||
case EMC_TASK_PLAN_EXECUTE_INTERNAL_TYPE:
|
||||
case EMC_TASK_PLAN_OPTIONAL_STOP_TYPE:
|
||||
return EMC_TASK_EXEC_DONE;
|
||||
break;
|
||||
|
|
@ -3132,6 +3184,11 @@ static int iniLoad(const char *filename)
|
|||
io_error = strdup(inistring);
|
||||
}
|
||||
|
||||
// max number of queued MDI commands
|
||||
if (NULL != (inistring = inifile.Find("MDI_QUEUED_COMMANDS", "TASK"))) {
|
||||
max_mdi_queued_commands = atoi(inistring);
|
||||
}
|
||||
|
||||
// close it
|
||||
inifile.Close();
|
||||
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ static PyMemberDef Stat_members[] = {
|
|||
{(char*)"input_timeout", T_BOOL, O(task.input_timeout), READONLY},
|
||||
{(char*)"rotation_xy", T_DOUBLE, O(task.rotation_xy), READONLY},
|
||||
{(char*)"delay_left", T_DOUBLE, O(task.delayLeft), READONLY},
|
||||
{(char*)"queued_mdi_commands", T_INT, O(task.queuedMDIcommands), READONLY},
|
||||
|
||||
// motion
|
||||
// EMC_TRAJ_STAT traj
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ try:
|
|||
except TclError:
|
||||
print root_window.tk.call("set", "errorInfo")
|
||||
raise
|
||||
|
||||
|
||||
program_start_line = 0
|
||||
program_start_line_last = -1
|
||||
|
||||
|
|
@ -752,6 +754,7 @@ class LivePlotter:
|
|||
root_window.update_idletasks()
|
||||
vupdate(vars.exec_state, self.stat.exec_state)
|
||||
vupdate(vars.interp_state, self.stat.interp_state)
|
||||
vupdate(vars.queued_mdi_commands, self.stat.queued_mdi_commands)
|
||||
if hal_present == 1 :
|
||||
set_manual_mode = comp["set-manual-mode"]
|
||||
if self.set_manual_mode != set_manual_mode:
|
||||
|
|
@ -858,7 +861,7 @@ initiated action (whether an MDI command or a jog) is acceptable.
|
|||
This means this function returns True when the mdi tab is visible."""
|
||||
if do_poll: s.poll()
|
||||
if s.task_state != linuxcnc.STATE_ON: return False
|
||||
return s.interp_state == linuxcnc.INTERP_IDLE
|
||||
return s.interp_state == linuxcnc.INTERP_IDLE or (s.task_mode == linuxcnc.MODE_MDI and s.queued_mdi_commands < vars.max_queued_mdi_commands.get())
|
||||
|
||||
# If LinuxCNC is not already in one of the modes given, switch it to the
|
||||
# first mode
|
||||
|
|
@ -2545,6 +2548,8 @@ vars = nf.Variables(root_window,
|
|||
("touch_off_system", StringVar),
|
||||
("machine", StringVar),
|
||||
("on_any_limit", BooleanVar),
|
||||
("queued_mdi_commands", IntVar),
|
||||
("max_queued_mdi_commands", IntVar),
|
||||
)
|
||||
vars.linuxcnctop_command.set(os.path.join(os.path.dirname(sys.argv[0]), "linuxcnctop"))
|
||||
vars.highlight_line.set(-1)
|
||||
|
|
@ -3261,6 +3266,8 @@ _tk_seticon.seticon(widgets.about_window, icon)
|
|||
_tk_seticon.seticon(widgets.help_window, icon)
|
||||
|
||||
vars.kinematics_type.set(s.kinematics_type)
|
||||
vars.max_queued_mdi_commands.set(int(inifile.find("TASK", "MDI_QUEUED_COMMANDS") or 10))
|
||||
|
||||
def balance_ja():
|
||||
w = max(widgets.axes.winfo_reqwidth(), widgets.joints.winfo_reqwidth())
|
||||
h = max(widgets.axes.winfo_reqheight(), widgets.joints.winfo_reqheight())
|
||||
|
|
|
|||
Loading…
Reference in a new issue