• A widget package project

    From Luc@luc@sep.invalid to comp.lang.tcl on Sun Mar 31 02:17:51 2024
    From Newsgroup: comp.lang.tcl

    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.

    However, I had to test it in a real example:

    -- test.tcl

    package require Tk
    package require tile
    package require testpkg

    wm withdraw .
    set ::w [toplevel .test -background #c0c0c0]
    wm title $::w "test"
    tk appname "test"

    bind $::w <Escape> {exit 0}
    wm protocol $::w WM_DELETE_WINDOW {exit 0}

    set of $::w.outerframe
    frame $of -height 100
    pack $of -fill both -expand 1

    set fb $of.testpkg
    puts "fb $fb"
    testpkg $fb
    pack $fb -fill both -expand 1
    ----------------

    And it works. Yes, no problem.

    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?
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Sun Mar 31 15:55:46 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:
    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]]

    Looks typical. If created by "pkg_mkIndex" then it is almost always
    correct.

    ----------------

    -- testpkg.tcl

    package provide testpkg 1.0

    namespace eval testpkg {}

    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.

    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 "."]

    Why three lines of ugly split and lrange code to get the parent window
    name, when [winfo parent] already gives the parent window name.

    if {[winfo parent $wparent] == 0} {error "Invalid parent path $wpath"}

    winfo parent returns a window path, or empty string, so it will never
    equal zero here, and this will never trigger. Perhaps you meant
    '== ""' here?

    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.


    # ----------------
    # 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?

    That depends upon one's definition of "correctly". You have a package
    that per your message, works. So it is "correct" given a definition of
    "does not error out with a syntax or logic error".

    But you also have some oddness, that could be deemed "incorrect".

    I.e., the global "testpkg" proc, defined outside of the namespace,
    which then defines some things inside the namespace, is not quite right
    for at least the reason that it is polluting the global namespace with
    extra procs that don't belong there.

    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.

    Is this really how it's supposed to be done?

    Well, no, not really. Your package should only produce one globally
    visible item, the namespace name that the rest of your code is inside.
    So the global proc is pollution that is exactly what namespaces were
    created to avoid.

    As well, you should be passing arguments into procs to build widgets,
    not calling a proc that plays games with namespace name paths of define variables and pass in arguments in a roundabout way.

    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.

    Here's a much simplified version that (to the extent I can tell)
    produces the identical results, and avoids the unusual usages above:

    namespace eval testpkg {

    # make sure our required dependencies are available
    package require Tk
    package require tile

    variable version 1.0

    proc make-widget {wpath} {
    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."
    }

    package provide testpkg 1.0
    }

    And to "create" a "testpkg" widget, you call it somewhere else as:

    testpkg::make-widget .abc

    Will it work as expected from a widget?

    You've not told us what you expect, so we can't really say.

    Am I handling the widget path correctly?

    You appear to be passing in the path where you want the widget created,
    so that's correct (the aspect of "supply the path" to the creation
    system, not the way you are passing that path in). As to what you
    intended by all the 'wparent' stuff, I honestly don't know what you
    were trying to do there.

    Is it going to play ball nicely when inserted into someone else's Tk application?

    Most likely, but only because someone else's Tk application is not
    likely to have a "testpkg" proc at the global level. But, if someone
    else does have a 'testpkg' proc at the global level, then it will not
    play nicely (as you'll be overwriting someone else's proc with yours
    from this package).
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Sun Mar 31 16:08:28 2024
    From Newsgroup: comp.lang.tcl

    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.


    Thank you for your remarks.
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Sun Mar 31 20:18:02 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:
    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.

    Ok, a bit of experimenting has led me to this sentence in the 'package' manpage:

    "If so, the script for the highest acceptable version number is
    evaluated in the global namespace;"

    So your "namespace eval testpkg {}" is converted to effectively be
    "namespace eval ::testpkg {}" by the operation of package require.

    Also, why try to 'smuggle' when you can simply pass them to a proc that
    lives in the namespace and then the args get there via the method most programmers would expect. 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.

    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?

    From my experimentation it seems it does not matter, 'package require' ultimately drops you into :: before your package code runs anyway.

    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.

    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.

    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.

    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 ...

    And, for your own uses, just import it where you want it and then you
    don't have to type out "testpkg::testpkg ..." everywhere.

    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 ...

    I want it to operate in the global scope like all the first class
    built-in widgets.

    Making the procs exportable and then importing them into the global
    scope after loading the package accomplishes that goal, with only one
    extra command (the namespace import).

    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.

    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. 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.

    You should be aware of the consequences. This is not ordinary user
    input. It's something a developer is going to do.

    Yes, and at the same time that developer expects your package to abide
    by the "rules" (convention) of packages placing their "stuff" inside
    their own "boundaries" instead of filling up the global namespace.

    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.

    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.

    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.

    So wrap it in a try/catch (see above) if you want to customize the
    message, or just go and do the frame create. If it errors out and you
    have not caught it then the error will propagate back out to the other
    user's code to do with as they see fit.

    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?

    See my "revised" example of your test package that I included in my
    prior reply. Just use the standard proc parameters.

    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.

    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.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Sun Mar 31 22:49:06 2024
    From Newsgroup: comp.lang.tcl

    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? :-)
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Mon Apr 1 03:54:02 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> 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.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ashok@apnmbx-public@yahoo.com to comp.lang.tcl on Mon Apr 1 10:57:24 2024
    From Newsgroup: comp.lang.tcl

    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 3/31/2024 10:47 AM, Luc wrote:
    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:


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Mon Apr 1 14:51:14 2024
    From Newsgroup: comp.lang.tcl

    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.
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Mon Apr 1 18:45:16 2024
    From Newsgroup: comp.lang.tcl

    Luc <luc@sep.invalid> wrote:
    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

    No, Ashok means what he said, "Snit" [1].

    Which provides (among other things) a framework for building megawidgets
    [2] [3],

    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.

    Which is fine, but you will also lose access to a lot of newer things
    that will make the process a lot easier.


    [1] https://core.tcl-lang.org/tcllib/doc/tcllib-1-18/embedded/www/tcllib/files/modules/snit/snit.html
    https://wiki.tcl-lang.org/page/Snit%27s+Not+Incr+Tcl

    [2] https://wiki.tcl-lang.org/page/megawidget

    [3] https://wiki.tcl-lang.org/page/What+is+a+Megawidget
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Mon Apr 1 19:11:38 2024
    From Newsgroup: comp.lang.tcl

    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


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Mon Apr 1 22:31:28 2024
    From Newsgroup: comp.lang.tcl

    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.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From et99@et99@rocketship1.me to comp.lang.tcl on Mon Apr 1 18:09:04 2024
    From Newsgroup: comp.lang.tcl

    On 4/1/2024 3:31 PM, Rich wrote:
    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.



    I solve the different versions by creating starpaks where I build a single file tcl/tk executable, often starting with one of Ashok's tclkits.

    I include all the library packages I need as well as any other commands I want available w/o having to load them separately. Then I use the starpack as if it were a wish executable giving it the name of a script file to execute as arg one.

    These all can run side by side on systems with older tcl's as the default w/o affecting them.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Luc@luc@sep.invalid to comp.lang.tcl on Mon Apr 1 22:59:29 2024
    From Newsgroup: comp.lang.tcl

    On Mon, 1 Apr 2024 22:31:28 -0000 (UTC), Rich wrote:

    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.

    OK. I've made these decisions:

    1. Everything will be contained in its own namespace, including the
    one proc.

    2. I will maintain the rest of my original design. I can maybe get
    it all done in two weeks because I've been busy with something else.

    Learning Snit is definitely not easier for me. I've been avoiding it
    for 20 years. I always thought it was way too complicated for little
    to no benefit, at least within the scope of the things I normally do.
    Learning Snit now (or TclOO) would set me back at least two or three
    months. That's too long. I will look into it this year, but later and
    taking my sweet, sweet time. And I am not really convinced I really
    need it this time. I mean, do ALL third-party packages or megawidgets
    out there use Snit? I doubt it.

    3. I may rewrite my megawidget with Snit later. The code will be free
    (BSD) as usual so anyone is welcome to change it if they like. I am
    just afraid my code may not work well with geometry management, but
    let's see.

    Thank you for all the input.
    --
    Luc


    --- Synchronet 3.20a-Linux NewsLink 1.114