I'm trying to make a package that is meant to be used as a widget.
This prototype is called testpkg. Here are the files:
-- pkgIndex.tcl
package ifneeded testpkg 1.0 [list source [file join [list $dir] testpkg.tcl]]
----------------
-- testpkg.tcl
package provide testpkg 1.0
namespace eval testpkg {}
proc testpkg {args} {
package require Tk
package require tile
set ::testpkg::args $args
namespace eval ::testpkg {
namespace export testpkg
set ::testpkg::version 1.0
# ----------------
# Processing arguments
set wpath [lindex $args 0]
set wparentsplit [split $wpath "."]
set wparentsplit [lrange $wparentsplit 0 [expr {[llength $wparentsplit] - 2}]]
set wparent [join $wparentsplit "."]
if {[winfo parent $wparent] == 0} {error "Invalid parent path $wpath"}
# ----------------
# Here be widgets
set of $wpath
frame $of -height 100 -width 20
pack $of -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."
}
}
----------------
If I 'set wpath .top' and run 'testpkg' at this point, the text widget
looks good.
So my question is, am I really doing this correctly?
Is this really how it's supposed to be done?
Will it work as expected from a widget?
Am I handling the widget path correctly?
Is it going to play ball nicely when inserted into someone else's Tk application?
Empty testpkg namespace, created not in the root/global namespace but
in whatever namespace the "package require" is run within. If this is
what you want, it is fine. If you meant for this to always be a
namespace anchored at the root (global) then you'll want ::testpkg to
force it to be anchored there.
proc testpkg {args} {A proc, created at the global level, outside of any namespace, which,
oddly, then goes on to create a namespace in the root (which may
produce two "testpkg" namespaces if the "package require" for this is
run within a namespace other than the root.
Why three lines of ugly split and lrange code to get the parent window
name, when [winfo parent] already gives the parent window name.
And, why four lines of "parent" games, and wparent is never again used?
The error call implies you were checking for something, but it is not
clear what you were trying to check for.
As well, your way of "passing arguments" in, while "it works" is not at
all the normal way of doing so. So that /could/ also be considered >"incorrect" in some definitions.
Also, every time you call your global proc, you are redefining the
namespace all over again, which while it isn't harmful in this simple >example will likely eventually create you great grief when you begin to
want to store some long term state in the namespace to support the
widget, but you destroy that state with each new widget you create.
On Sun, 31 Mar 2024 15:55:46 -0000 (UTC), Rich wrote:
Empty testpkg namespace, created not in the root/global namespace but
in whatever namespace the "package require" is run within. If this is >>what you want, it is fine. If you meant for this to always be a
namespace anchored at the root (global) then you'll want ::testpkg to >>force it to be anchored there.
1. That namespace declaration is empty just so the namespace exists
so I can smuggle the $args into it later.
2. Do I want it to be independent of root/global namespace? I honestly
don't know. I really don't suspect what the consequences will be in
either case. Do you recommend something?
proc testpkg {args} {A proc, created at the global level, outside of any namespace, which, >>oddly, then goes on to create a namespace in the root (which may
produce two "testpkg" namespaces if the "package require" for this is
run within a namespace other than the root.
That is a very good point.
The reason I did that is that I don't want the command to be
accessible as ::testpkg::testpkg. I've seen that approach in other
packages and I don't like it.
I want it to operate in the global scope like all the first class
built-in widgets.
I do have some hope that it will be accepted as an "official" Tk widget/command in the future.
Yes, it creates a testpkg proc, so what? If you run 'package require testpkg' in your code, you should know what you're doing.
You should be aware of the consequences. This is not ordinary user
input. It's something a developer is going to do.
Why three lines of ugly split and lrange code to get the parent
window name, when [winfo parent] already gives the parent window
name.
I tried [winfo parent $widget] but that didn't work for me. Tk
throws an error complaining that $widget doesn't exist. It doesn't.
It is going to be created on the next line, but it doesn't exist yet.
I am not checking $widget, I am checking its parent, but [winfo
parent] is not having it. So I had to isolate the parent so I could
check it.
And, why four lines of "parent" games, and wparent is never again
used? The error call implies you were checking for something, but it
is not clear what you were trying to check for.
I am checking if the parent exists. I can't append my widget to a
path that isn't valid.
As well, your way of "passing arguments" in, while "it works" is not
at all the normal way of doing so. So that /could/ also be
considered "incorrect" in some definitions.
What would the correct way of passing arguments be?
Also, every time you call your global proc, you are redefining the >>namespace all over again, which while it isn't harmful in this simple >>example will likely eventually create you great grief when you begin to >>want to store some long term state in the namespace to support the
widget, but you destroy that state with each new widget you create.
That sounds concerning, but I don't see how or why the widget/proc
would be called more than once in any code. You run it, interact with
it for as long as needed for it to do its job then it's destroyed.
Also, why try to 'smuggle' when you can simply pass them to a proc that >lives in the namespace
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.
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.
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 ...
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 ...
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.
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.
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.
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.
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.
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.
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?
It's supposed to be about the same thing.
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 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.
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.
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.
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.
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?
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? :-)
I'm trying to make a package that is meant to be used as a widget.
This prototype is called testpkg. Here are the files:
For widgets consider using a meta framework. I use Snit but there are
others based on TclOO. Small learning curve but saves a lot of code you >would have to write yourself (for example, composing, Tk option database)
On Mon, 1 Apr 2024 10:57:24 +0530, Ashok wrote:
For widgets consider using a meta framework. I use Snit but there
are others based on TclOO. Small learning curve but saves a lot of
code you would have to write yourself (for example, composing, Tk
option database)
I've no idea what a meta framework is. I suppose you mean this?
https://core.tcl-lang.org/tcllib/doc/tcllib-1-18/embedded/www/tcllib/files/modules/tool/tool.html
It seems it requires 8.6.
My current code will work as far back as 8.4, possibly 8.3 or maybe
even 8.0. I am very fond of backwards compatibility.
My current code will work as far back as 8.4, possibly 8.3 or maybe
even 8.0. I am very fond of backwards compatibility.
Which is fine, but you will also lose access to a lot of newer things
that will make the process a lot easier.
On Mon, 1 Apr 2024 18:45:16 -0000 (UTC), Rich wrote:
My current code will work as far back as 8.4, possibly 8.3 or maybe
even 8.0. I am very fond of backwards compatibility.
Which is fine, but you will also lose access to a lot of newer things
that will make the process a lot easier.
"make the process a lot easier" sounds like a big red flag to me.
A lot easier for whom? For the end user? Maybe. Maybe not.
What if they are stuck with an old OS or Tcl/Tk version that doesn't have snit or TclOO? Will snit or TclOO be "a lot easier" for that kind of user too?
Do you by any chance remember that I found I can't build Tk 8.6.13 or
later on my Debian 9 machine because it requires an X library version
that I don't have and cannot installe due to the cascading effect of dependencies?
Luc <luc@sep.invalid> wrote:
On Mon, 1 Apr 2024 18:45:16 -0000 (UTC), Rich wrote:
My current code will work as far back as 8.4, possibly 8.3 or maybe
even 8.0. I am very fond of backwards compatibility.
Which is fine, but you will also lose access to a lot of newer things
that will make the process a lot easier.
"make the process a lot easier" sounds like a big red flag to me.
A lot easier for whom? For the end user? Maybe. Maybe not.
A lot easier for you. Otherwise you'll have to provide all the extra scaffold code to make your new widget act like Tk coders expect widgets
to act.
What if they are stuck with an old OS or Tcl/Tk version that doesn't have
snit or TclOO? Will snit or TclOO be "a lot easier" for that kind of user
too?
There is (or at least there was) a snit backport for 8.3.4:
https://wiki.tcl-lang.org/page/Snit+under+Tcl%2FTK+8%2E3
And this is stated on the main Snit page (https://wiki.tcl-lang.org/page/Snit%27s+Not+Incr+Tcl?R=0&O=Snit&W=):
As of 2010, Tcllib ships both Tcl-8.5-depending Snit v2.3.1 and
Snit 1.x that works with Tcl 8.3 and 8.4.
So there indeed is a Snit that works back to 8.3. However if you want
things to work even further back, well, then you'll have to code for
yourself some of the things Snit provides as assistance.
Do you by any chance remember that I found I can't build Tk 8.6.13 or
later on my Debian 9 machine because it requires an X library version
that I don't have and cannot installe due to the cascading effect of
dependencies?
Vaguely. Was there some reason you had to stick with Debian 9 and
can't upgrade the whole install? Granted, I've got two systems with
older installs on them myself that are 'stuck in the past' largely
because I've simply not gotten around to upgrading their Slackware
installs. But that is totally my own fault for not having upgraded
them.
A lot easier for whom? For the end user? Maybe. Maybe not.
A lot easier for you. Otherwise you'll have to provide all the extra >scaffold code to make your new widget act like Tk coders expect widgets
to act.
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 916 |
Nodes: | 10 (1 / 9) |
Uptime: | 51:00:23 |
Calls: | 12,172 |
Calls today: | 2 |
Files: | 186,522 |
Messages: | 2,234,734 |