Luc <
[email protected]d> wrote:
On Sun, 31 Mar 2024 20:18:02 -0000 (UTC), Rich wrote:
Also, why try to 'smuggle' when you can simply pass them to a proc that >>lives in the namespace
Because I don't want ::testpkg::proc. I want proc. In the global scope.
Which you get if you:
1) in the package, 'namespace export' the proc
2) after "package requiring" the package, "namespace import" the
exported procs
Then the rest of your code can use the proc as a global proc (and so
can anyone else who wants to). And those who don't want to can call it
as "testpkg::testpkg".
I don't want to "protect" the command. I want the command to be global.
I want the variables to be protected. OK, so maybe I don't need the namespace, the proc should suffice? I will think about that.
It sounds like you want the namespace to be specific for that one
widget. In which case, use an object. You get an isolated namespace
for the object's variables automatically from the system without you
having to do anything (other than learn a bit of how to use tcloo).
Your 'smuggling' will leave folks scratching their head wondering
*why* did you do that because it is a very unusual way to pass
parameters to a proc.
I am not passing parameters to a proc. I am passing parameters to
a namespace. And that is the only way.
Sigh.... Again... A namespace is not a thing you can call or "pass parameters" to (passing parameters refers to calling things).
Namespaces are simply containers (like a jar or box). They are not
active in and of themselves, and you can't "pass things" to them
because you don't "call them".
Given the docs, you don't accidentally produce two testpkg
namespaces, but you do still pollute the global with your extra proc
that ultimately creates the namespace anyway.
Does 'text' pollute the global scope?
Does 'scrollbar' pollute the global scope?
Does 'label' pollute the global scope?
Those are not considered "pollution" because they existed globally
before Tcl had namespaces, so they remained globals afterwards for
backwards compatibility.
It's supposed to be about the same thing.
And, if you have wishes about this becoming an "official widget" then
you'll eventually have to follow some of the conventions. I.e., for
'tcllib' one of those conventions is your module/package resides
totally within its own namespace:
https://wiki.tcl-lang.org/page/Tcllib+Contribution+%26+Feedback?R=0
* The module must use a namespace for its commands and variables
Ok, so you have two possible alternatives that are standard fare:
1) mark the inner testpkg proc as exportable, and let the package user >>decide which they want to do:
::testpkg::testpkg ...
or
namespace import ::testpkg::*
testpkg ...
That sounds like a lot of unnecessary extra work.
It is one extra line of code for you to write, once.
It is meant to be a first class command. Sure, only after it is package-required, but still a first class command. If you want to
use it and you already have a proc with the same name, you are just
going to have to rename your proc. I mean, the odds are really slim.
Really slim odds happen remarkably often when large numbers are
concerned. By this I mean if it were to gain signifant use (large
numbers of users) those slim odds will tend towards 100% chance of
conflict.
2) setup the namespace to be an ensemble, with reasonable naming of the >>ensenble procs, and then you can do:
package require testpkg
testpkg create-widget ...
That makes the command global like I intend to, but requires that the user/developer call two commands instead of one which doesn't make
sense because the package only has one command. It has -parameters
or -options, but only one command. Just like all the Tk widgets we
are all used to.
It's not two commands, it is command and subcommand (and, yes, a bit
much for a single command package). But it is another alternative.
Hmm, actually, it is supposed to accept other commands, but I designed
a different interface for them, a trace on the ::testpkg::command var.
And I believe that is the only possible approach because the widget
has to be able to receive the commands after it has been packed/gridded
and is already/still doing its job. These commands cannot be passed at
the time the widget is run/created. That is a pretty good reason to
keep the namespace. I can't put that traced var in the global scope.
That's one way, but that's also significantly harder than the
conventional way, which is you set your widget up to work like the rest
of the tk widgets, where creating it (your widget) creates a proc that
can then be called to configure/update/etc. the widget.
I.e., like how 'text' creates a command named after the window path,
and then you call that command to "do things" to the text widget:
i.e.:
text .txtwidget
.txtwidget insert end "Hello World"
And, note that the ".txtwidget" command invocation looks remarkably
similar to how procs in a namespace ensemble are called. That should
give you a hint as to what 'namespace ensemble' is useful for.
Yes, but at the same time the user of the package has a reasonable >>expectation that the package will create its "stuff" within its
boundaries, rather than outside those boundaries.
The command will be global. The only remaining "boundaries" is for
variables, and namespaces is the way to do that, right? And there are
no other procs, 'testpkg' is the only proc.
Unless the variables are only needed when creating the widget, then
they can just be proc locals. But if the variables have to live on and
hold data for the widget while it is doing its thing, then namespaces
are one way to do that. Objects are another way to also do that.
But why are you checking its parent? If you just care to emit an
error message when the path your code has been asked to use is not
proper, then catching errors on creating your 'subframe' works just
fine:
try {
frame $wpath.testpkg-frame
} on error {msg opts} {
error "testpkg: Provided widget path: $wpath is invalid.\n$msg"
}
Or, you just go an create the 'frame' and let any errors propagate back >>normally.
I guess that would work, but sounds like trading six for half a dozen
to me. My way also works. Or is there something really wrong with it?
It is unnecessarially confusing trying to suss out what you are doing
with the splits and lranges for something that you don't need to do.
Just use the user's path, and let it succeed or error out.
One of the reasons for the addition of namespaces to Tcl was to
prevent name conflicts within plural packages that each wanted to use
the same name for part of their operation.
Do you not, often, create more than one "frame" or "button" or even
more than one single "text" widget? If you are thinking of this as >>possibly becoming an official 'widget' one day, then you have consider
a broader usage than what you believe your own usage might be. Someone >>else very well may want sixteen of these widgets across eight different >>toplevel windows all at the same time.
OK, maybe that is a risk. So instead of creating a 'testpkg' namespace,
I guess I will create a "testpkg_[clock milliseconds]" namespace. Do we
have a deal? :-)
If you want an object, then just create it as an object.
testpkg2 directory:
$ ls -1 testpkg2
pkgIndex.tcl
testpkg2.tcl
Contents of pkgIndex.tcl (created by "pkg_mkIndex ."):
$ cat testpkg2/pkgIndex.tcl
# Tcl package index file, version 1.1
# This file is generated by the "pkg_mkIndex" command
# and sourced either when an application starts up or
# by a "package unknown" script. It invokes the
# "package ifneeded" command to set up package-related
# information so that packages will be loaded automatically
# in response to "package require" commands. When this
# script is sourced, the variable $dir must contain the
# full path name of this file's directory.
package ifneeded testpkg2 1.0 [list source [file join $dir testpkg2.tcl]]
Contents of testpkg2.tcl
$ cat testpkg2/testpkg2.tcl
package requir Tk
package require TclOO
namespace eval testpkg2 {
oo::class create testpkg2 {
constructor {wpath} {
puts stderr "testpkg->constructor: my namespace is [namespace current]"
frame $wpath -height 100 -width 20
pack $wpath -fill both -expand 1
set fb $wpath.filelistbox
text $fb
$fb configure -font {Freesans 14} -height 2 -width 20
pack $fb -fill both -expand 1
$fb insert end "This is a test.\nLine 2."
}
# An extra 'method' as an example
method whoami {} {
puts stderr "I am object [self object] using namespace [namespace current]"
}
}
namespace export testpkg2
}
package provide testpkg2 1.0
Note, the "namespace export testpkg2" line above is what enables
importing the "create proc" for the object (technically the 'class').
Interactive session using the 'object' (well... technically, the
'class' as it is itself 'creating objects'):
Load a wish:
$ rlwrap wish
Setup auto_path
% lappend auto_path .
/usr/lib64/tcl8.6 /usr/lib64 /usr/lib /usr/lib64/tk8.6 /usr/lib64/tk8.6/ttk .
Package require the 'testpkg2' package:
% package require testpkg2
1.0
Perform the namespace import to make ::testpkg2::testpkg2 callable as
just "testpkg2":
% namespace import testpkg2::*
Create a new toplevel to hold a first instance of the widget:
% toplevel .t1
.t1
Create a 'widget' in the new toplevel, giving the widget a name of
"myobjname":
% testpkg2 create myobjname .t1.tp2
testpkg->constructor: my namespace is ::oo::Obj22
::myobjname
Ask the 'widget' who it is (i.e., calling the example method). This is
how you'd communcate those "runtime" things to the widget, just with
different method names to do the things you want to do to it at
runtime:
% myobjname whoami
I am object ::myobjname using namespace ::oo::Obj22
Create a second toplevel:
% toplevel .t2
.t2
Create another (independent) widget in the second toplevel, this time
letting TclOO assign an object name:
% testpkg2 new .t2.tp2
testpkg->constructor: my namespace is ::oo::Obj23
::oo::Obj23
Ask the second widget who it is:
% ::oo::Obj23 whoami
I am object ::oo::Obj23 using namespace ::oo::Obj23
%
Two separate objects, each with their own private namespaces (for their variables/data/settings/state). Note the different namespace names
output by the puts line in the constructor when each was created.
And, if you go through this exercise above, you'll end up with the
default . window that wish creates, and two additional toplevels, each
with one copy of the 'widget' inside.
--- SoupGate-Win32 v1.05
* Origin: fsxNet Usenet Gateway (21:1/5)