Re: TKinter in Python - advanced notions

2023-06-21 Thread aapost via Python-list

On 6/21/23 09:47, Dan Kolis wrote:

I've write a huge biotech program ( an IDE for synthetic biology ), and am 
slowly outgrowing TKINTER.

Has anybody out there merged a little bit of TCL direct calls from Python 3.X 
to get more freedom then TKINTER for just some Windows ?



I wish it looked better, but its 'ok'. I believe X11 IO is considerably superior for 
serious work the HTML.  I mean 'serious' work. with lots of multi media windows. I am not 
talking about fb "Oh ! There is a window it opened inthe corner !"... trivial 
functionality.



I don't know if it would help, but you can extend/add tcl/tk packages

I don't remember the full instructions right off, but quickly reverse 
engineering my old stuff I think you just need to drop them in 
/usr/share/tcltk/ or equivalent.


(I needed to do that to replace the terrible looking default file dialog 
for unix/linux with fsdialog.)


then running something like the following from your Tk object

self.eval('package require fsdialog')

(reverse engineering the python tkinter source you can likely find other 
ways of doing more tcl direct stuff)


I have not researched if there are some better, more featured 
(non-buggy) Text widgets implemented in tcl that can be dropped in, (I 
know several of the tcl drop in widgets I tried were lacking in refinement).


From what I can tell, once upon a time there were better, more 
interesting projects and tutorials on extending tkinter, such as WCK 
(tkinter3000), but the only remnants of those remain publicly available 
are outdated unmaintained archives.


You might also consider looking at the Grail browser source for research 
purposes, as it does some interesting things with some of the widgets, 
(parsing html and such), even though it is 20 years old now (and written 
in python 1).
The update attempts from 10+ years ago have disappeared. (it's license 
is considered questionable from what I understand, so not sure if that 
is an aspect of it, the other being one of it's main features, python 
applets, is unsafe and was not easily fixable)


You might already be beyond some of these things though.

I know what you mean as far is feeling like the little bit extra you 
need pushes beyond what tkinter can do / makes you feel like you have 
outgrown the module.


(I had to take a break from one of my projects and send it to 
development hell until my UI knowledge/skills improve after I found 
myself considering using xml schema appinfo annotations to store json 
formatted widget specific information, lol.)


I have felt that sense of lack with most of the UI modules I have tried 
though.


I don't know of a clear better python-only solution though that fits my 
personal needs.


So I have to lean toward improving my tcl / C in hopes that it might 
help steer me toward that extra (which seems to be in the spirit of what 
tcl/tk's intent is to begin with). That will be a while for me though if 
I get there.





--
https://mail.python.org/mailman/listinfo/python-list


Re: What to use instead of nntplib?

2023-05-30 Thread aapost





I used to run my own mail server.

Now I don't.

Practicality beats purity.

To be quite frank, the moralistic approach of complaining about the
way other people are too happy to give control to big companies is
NEVER going to achieve anything. You're welcome to be a little island,
that one Gaulish village that still holds out against the invaders,
but all you'll actually be doing is sidelining yourself.

I'm not saying that this is a GOOD situation, but facts are facts, and
I use Chrome and GitHub and a wide variety of other tools that aren't
free.

ChrisA



Don't get me wrong, I know I have lost the battle in this age, lol. The 
opening statement probably came off way less pragmatic than I intended 
before writing it. The attempt to steer the ol' "bitching just to bitch" 
toward more if a statement as to the fundamentals of why there is a 
bitch and frustration with how things are didn't quite make it there.


But regardless my positions are correct, lol, you know the situation is 
"NOT" GOOD, and you justify your compromises, as we tend to do to 
whatever extent we decide that we can live with, and time tells how 
those things play out.


For there to have been an elicited reaction to my statements sort of 
goes against the statement "NEVER going to achieve anything".


I am fine in my cave, a lot of things seen as "facts are facts" tend to 
change over time, and I don't see anyone else going anywhere that I need 
to go.


--
https://mail.python.org/mailman/listinfo/python-list


Re: What to use instead of nntplib?

2023-05-30 Thread aapost

On 5/22/23 12:10, Grant Edwards wrote:

On 2023-05-21, Retrograde  wrote:


Who ever came up with "Removing dead batteries" as a slogan, when
some of those batteries still work perfectly well, needs to rethink
it. Go ahead and remove code that no longer works, OK.  But removing
unpopular modules?  That undercuts the entire philosophy of the
platform, in my opinion.


And one of the metrics of "popularity" seems to be "activity"
(e.g. changes committed).  For things that have been around for 20+
years and have all the features they need and all of the bugs fixed
(and are now very stable) that lack of "activity" is interpreted as
"unpopular" regardless of how many people are using the module.

--
Grant




To add an additional bitching, I don't really ever see anyone discussing 
the dynamics and downsides of github (and things like it). Or how things 
like mozilla killing off usenet and mailing lists changes the entire 
dynamic of who manages and gets a say in how technology gets to move 
forward.


As someone who sees software independence and using free software as 
moral imperatives, signing up for github and agreeing to yet another 
terms of service is a no go for me, so moving to these platforms locks 
me out from contributing. (and a lot of python packages have code that 
also works with proprietary operating systems, so non-gnu/gnu hosting 
isn't a solution either)


And as someone who uses usenet to post to this list (I object to the 
google captcha on the mailing list sign up, and captchas in general), I 
imagine eventually a discussion will take place in a place like github 
that will do away with this avenue as well.


As far as modern commit dynamics,
Even if I did partake in the modern github style of code distribution, 
how many packages have issues where the "maintainers" inherited the 
package and really haven't dug deep enough in to the code to see how it 
really works. They have issues that sit around for YEARS, and when 
someone says "this sucks, this is broken and could be better", and the 
githubian response is typically a dismissive "Nothing is stopping you 
from making a PR".
Then when you get down to dedicating a month to polishing a PR to extend 
or redesign something with the features you need, it just sits there, 
for YEARS, because again, the authority that went in to the package in 
the first place is often gone, and there is no one with that knowledge 
to give the PR the review it deserves.
You end up with your fork, but it is lost, indistinguished from all the 
other forks of nothing.


There are now probably dozens of nntplib preservation implementations 
floating around, but how do you even consider which one to use? Without 
some energy behind it, to be certain in what you are doing, each person 
will practically have to download Python3.11 and extract it themselves,
and then either add it in to the latest version themselves, or 
comparitively study it vs a collection of new implementations to see 
which one feels most like a correct updated standard.
You also have to consider, is this a one off update? At 3.13 will I have 
to do it all over again? (at that point, doing it yourself really does 
become the only solution).


At the end of the day, what is there boils down to the influence of who 
is offering the resources.. And I would say most of that today comes 
from the microsofts and googles of the world that have no interest in 
preserving the independent ethos of the early web..


I personally am partial to autonomous website distribution, and 
mailmanv2 dev collaborations, so you can independently share modified 
versions of packages or tutorials you've written for your own purposes, 
and if they help others, great.. But I personally haven't found a place 
that accepts small cash payments and feels neutral enough to fit my 
needs and limited resources.


--
https://mail.python.org/mailman/listinfo/python-list


Re: Tkinter docs?

2023-05-23 Thread aapost

On 5/23/23 21:18, Rob Cliffe wrote:


Comments, anyone?
Better yet (holds breath ...) can anyone point me towards some decent 
tkinter documentation?


The variables are slightly more integrated when using tcl/tk directly, 
python has to have the object so you can track/use them easier. And the 
variables are more of what is intended to track a widgets purpose, vs 
state stuff that is more the state of the displaying of the widget.


occasionally giving tcl/tk a try directly helps, and using the official 
tcl/tk docs in addition to a couple other decent docs below.


https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/index.html
https://tkdocs.com/tutorial/styles.html

https://www.tcl.tk/man/tcl8.6/
https://www.tcl.tk/man/tcl8.6/TkCmd/contents.html
https://www.tcl.tk/man/tcl8.6/TkLib/contents.html

(this used to be some documentation that I only discovered existed 
recently, but most of it doesn't load and I don't know that anyone 
backed it up for rehosting..)

https://web.archive.org/web/20200227170827/http://effbot.org/tkinterbook

tk is decent for what it can do, (and it could do potentially more if 
you had a lifetime to dedicate to it, lol) and it's reasonably stable 
and not being perpetually hacked about and broken like gtk.


A couple additional pointers in case these haven't been figured out yet:
Running it interactively and using tab complete helps.
If you are running an app you can comment out the mytk.mainloop() 
statement then run python -i yourtk.py to give you access to all widgets 
live.
For navigating them, just come up with a widget naming convention that 
works for you in a similar vein to how tk does (like .frame.frame.label 
to start),

i.e.
main = tk.Tk()
main.frame = tk.Frame()
That way if you have anchored your widgets to something at the root 
level you can navigate down to whatever you are looking for easily. (you 
can also use winfo_children() iteratively to travel the way tcl 
structures them, but that is tedious).

--
https://mail.python.org/mailman/listinfo/python-list


Re: [pygettext] --package-name and --package-version unknown

2023-05-08 Thread aapost

On 5/5/23 04:39, c.bu...@posteo.jp wrote:


That being said, the git repo linked earlier has accepted commits to 
that file earlier this year. So read in to that what you will *shrugs*

--
https://mail.python.org/mailman/listinfo/python-list


Re: [pygettext] --package-name and --package-version unknown

2023-05-08 Thread aapost

On 5/5/23 04:39, c.bu...@posteo.jp wrote:

Thanks for the answer.

Am 05.05.2023 03:24 schrieb aapost:

pygettext is deprecated since xgettext supports python now, so using
xgettext is recommended.


If this is the official case then it should be mentioned in the python 
docs. The 3.11 docs still tell about pygettext and xgettext and don't 
recommend one of it.


Yep, no disagreement. A lot of things 'should' be though, and 
technically it is (which docs being the key, lol):


$man pygettext

PYGETTEXT(1)   General Commands 
Manual  PYGETTEXT(1)


NAME
   pygettext - Python equivalent of xgettext(1)

SYNOPSIS
   pygettext [OPTIONS] INPUTFILE ...

DESCRIPTION
   pygettext is deprecated. The current version of xgettext 
supports many languages, including Python.


   pygettext  uses Python's standard tokenize module to scan Python 
source code, generating .pot files identical to what
   GNU xgettext generates for C and C++ code.  From there, the 
standard GNU tools can be used.


   pygettext searches only for _() by default, even though GNU 
xgettext  recognizes  the  following  keywords:  gettext,
   dgettext, dcgettext, and gettext_noop. See the -k/--keyword flag 
below for how to augment this.



(I have never used either, I just spent a few minutes trying to be 
helpful =P)

--
https://mail.python.org/mailman/listinfo/python-list


Re: [pygettext] --package-name and --package-version unknown

2023-05-04 Thread aapost

On 5/4/23 17:38, c.bu...@posteo.jp wrote:
am I right to assume that "pygettext" is part of the official Python3 
"package"? So it is OK to aks here?


How can I set the "Project-Id-Version"? With "xgettext" I would use the 
arguments "--package-name" and "--package-version" for this but they are 
unknown for "pygettext".


pygettext is deprecated since xgettext supports python now, so using 
xgettext is recommended.


That being said, pygettext does not support the options, but it could be 
modified pretty easily.
Untested but if you wanted to add that functionality in just create a 
modified pygettext.py with something like:



link PACKAGE and VERSION to variables:

pot_header = _('''\
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR , YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: %(packagename)s %(packageversion)s\\n"
"POT-Creation-Date: %(time)s\\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
"Last-Translator: FULL NAME \\n"
"Language-Team: LANGUAGE \\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=%(charset)s\\n"
"Content-Transfer-Encoding: %(encoding)s\\n"
"Generated-By: pygettext.py %(version)s\\n"
''')

add attributes to Options class:

class Options:
# constants
GNU = 1
SOLARIS = 2
# defaults
extractall = 0 # FIXME: currently this option has no effect at all.
escape = 0
keywords = []
outpath = ''
outfile = 'messages.pot'
writelocations = 1
locationstyle = GNU
verbose = 0
width = 78
excludefilename = ''
docstrings = 0
nodocstrings = {}
packagename = "PACKAGE"
packageversion = "VERSION"

modify option parsing for loop to look for new options:
for opt, arg in opts:

elif opt in ('--package-name',):
options.packagename = arg
elif opt in ('--package-version',):
options.packageversion = arg

grab those options when generating file output:

def write(self, fp):
options = self.__options
packagename = options.packagename
packageversion = options.packageversion
timestamp = time.strftime('%Y-%m-%d %H:%M%z')
encoding = fp.encoding if fp.encoding else 'UTF-8'
print(pot_header % {'packagename': packagename,
'packageversion': packageversion,
'time': timestamp, 'version': __version__,
'charset': encoding,
'encoding': '8bit'}, file=fp)

(did not test, so might be a bug or two)

--
https://mail.python.org/mailman/listinfo/python-list


Note when migrating '#' formats for PyArgs_ParseTuple

2023-05-02 Thread aapost

Just in case anyone else runs in to it.

If you have code such as:

char* a;
char* b;
char* d;
int size;

if (!PyArg_ParseTuple(args, "sss#:f", &a, &b, &d, &size))
return NULL;

and it tells you to:

#define PY_SSIZE_T_CLEAN
#include "Python.h"

Be sure to change int size; to:

Py_ssize_t size;

If you forget, your code will compile fine and still work perfectly fine 
on all py2 and py3 i386 variants, but will break 64bit variants in very 
non-obvious ways.


In the above example the mismatched pointer type of size messes up the 
seemingly unrelated pointer a


padr a 0x7f1f
padr b 0x7f1fa656b960
padr d 0x7f1fa65579a0

So while accessing the s# argument works fine, you get a segfault when 
you access a


I spent quite a while chasing my tail researching variadics, picking 
apart the code trying to understand how and where 'a' gets stomped on 
before I realized the size type mismatch.


--
On that note, I will say that the removal of distutils/setup.py-ability 
from 3.12 without a similarly simple out of the box replacement is 
unfortunate. (if setup.py usage had design flaws that lead to 
deprecation fine, but python should have some form of stand alone 
equivalency included out of the box by default)


For researching issues like the above, and for people who want to use 
python for off-internet projects, it is nice to be able to spin up a 
virtual machine from any said era, and do a

./configure
make
./python setup.py install --user

As things diverge in the future, things start to break, and you don't 
always have all the pieces to put it back together, and pip kind of 
sucks and is not simple when it doesn't work. It's pushy, it wants to 
force everyone to connect to the hive, it demands ssl, etc, when I just 
want it to shut up and make

./python -m pip install .
do what
./python setup.py install --user
does

I will figure out what works best situationally moving forward as far as 
work arounds (probably modifying pip), but just saying, the modern way 
of jenkinizing everything sucks, and takes the fun out of tinkering in a 
cave.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Question regarding unexpected behavior in using __enter__ method

2023-04-21 Thread aapost

On 4/20/23 18:44, Lorenzo Catoni wrote:

Here, the TypeError occurred because "self" was passed as an input


Instantiate X and observe it there

x2 = X()

>>> X.__enter__

>>> X.__exit__
 at 0x...>
>>> x2.__enter__

>>> x2.__exit__
 of <__main__.X object at 0x...>>


To receive self the method must be bound. __enter__ = int doesn't bind 
the int type/class on instantiation, so it never gets self. Your custom 
function binds and receives self.


I am not sure if there is documentation to explain better specifically 
what makes makes type different and not bindable.


--
https://mail.python.org/mailman/listinfo/python-list


Re: Pycharm IDE

2023-04-18 Thread aapost

On 4/18/23 19:18, Kevin M. Wilson wrote:

Why complain about a 'comma', or a ')'???
 print (f'"I am thinking of a number between 1 to {LIMIT}\n")


my version says it expects ' first (to close the fstring)
then on a new line below it, it mentions the comma and )
I believe that is just showing you after ' it expects you to end the 
print with ) as you have

or , to add additional arguments to print
--
https://mail.python.org/mailman/listinfo/python-list


Re: Cannot install pkg_resources using pip

2023-04-17 Thread aapost

On 4/17/23 08:45, Rich Shepard wrote:

On Sun, 16 Apr 2023, Thomas Passin wrote:


Slackware isn't as straight forward in it's management as other distros 
(not standardized anyway). If this is someone elses install I would be 
cautious in using any advice I am providing, as it would be better for 
someone with familiarity with how they want the device set up to address 
the issues (such as if they prefer using some sort of package scheme or 
if doing everything from source is ok).


That being said, if you start from python source (as in overwriting what 
is currently there and doing a fresh reinstall), you can download 
Python3.9, then run

./configure
make
make install

after doing that, verify (python may still link to something old, that 
may be ok in this case, but good to note)

python --version
python3 --version
python3.9 --version

pkg_resources is inside of setuptools

so you would run
python3.9 -m pip install setuptools
(might already be there with the reinstall)

after that run
python3.9

then run
import pkg_resources
import setuptools
to see if you get errors or successful imports

if they import successfully, attempt to reinstall meson and ninja 
against your fresh python install

python3.9 -m pip install meson
python3.9 -m pip install ninja

verify versions with
meson -v
ninja --version

Then follow the instructions of the meson package from there (you 
mentioned some sort of pulseaudio-equalizer, depending on your desktop 
environment, you may run in to additional issues depending on how your 
slackware is setup).


--
https://mail.python.org/mailman/listinfo/python-list


Re: tksheet - Copy and Paste with headers

2023-04-16 Thread aapost

On 4/14/23 14:33, angela vales wrote:

I have recently created a tkinter app and need the ability to copy and paste 
data from tksheet table into an Excel file. I do have a button for export, but 
it will be beneficial to also allow the user to simply copy,paste.

I have enabled the appropriate bindings but cannot find a solution to also
copy the header information during the copy and paste.


the csv export code runs through a different path than the ctrl_c code, 
one operating on the sheet level, one on the main table level (I didn't 
dig in to the depths but my assumptions would be that main table doesn't 
mathematically consider the headers in the same way).


def yield_sheet_rows in _tksheet.py vs def ctrl_c in _tksheet_main_table.py

Comparing how the each path functions, without a larger redesign of 
tksheet, you could create a custom button press combo binding to 
something other than ctrl-c utilizing the yield_sheet_rows (or - 
disallow all other ctrl-c functionality in favor of ONLY a csv style 
everything dump when using ctrl-c):


Import these:
import csv as csv_module
import io

This would be your custom binding functionality:
rows = self.sheet.yield_sheet_rows(get_header = True, get_index = False)

s = io.StringIO()
writer = csv_module.writer(s, dialect = csv_module.excel_tab, 
lineterminator = "\n")

for row in rows:
writer.writerow(row)
self.clipboard_clear()
self.clipboard_append(s.getvalue())

It would need something deeper if you wanted to integrate it to ctrl-c 
and keep the existing ctrl-c functionality



--
https://mail.python.org/mailman/listinfo/python-list


Re: Embedded python is not 100% stable

2023-04-13 Thread aapost

On 4/13/23 03:40, Guenther Sohler wrote:




Attachments are stripped, so they weren't included.

Glancing at the branch and the 2 lines you mentioned.

You have a comment with a link for python 2.3 documentation.
Yet you have python 3.10 code included elsewhere (and openscad itself 
requires the modern spec C++17, so I assume your aim is at current versions)


https://docs.python.org/3.10/extending/newtypes.html
is the equivalent doc for that.

I am not experienced in defining PyType objects in C, but notice 
syntactical discrepancies, missing "static" on dealloc, use of 
typecasted malloc rather than PyType_GenericAlloc()

https://docs.python.org/3.10/c-api/typeobj.html#c.PyTypeObject.tp_alloc

Without digging in deeper and learning more I can't say for certain 
whether or not those are issues, but intuitively I assume they would be 
and that the implementation needs to more closely mirror the syntactical 
flow implementations of modern working examples..


--
https://mail.python.org/mailman/listinfo/python-list


Re: Weak Type Ability for Python

2023-04-13 Thread aapost

On 4/12/23 04:03, Ali Mohseni Roodbari wrote:
>
On 4/13/23 07:50, Stefan Ram wrote:
>If tomorrow Python would allow "string+int" and "int+string"
>in the sense of "string+str(int)" and "str(int)+string",
>what harm would be there?
>
>But for now, I think a typical approach would be to just use "str",
>i.e., "string+str(int)" and "str(int)+string".


I agree with Py Zen rule 2 in this case:
Explicit is better than implicit.

I hate when things try to guess what I am doing... It is why I can't use 
lxml.

--
https://mail.python.org/mailman/listinfo/python-list


Re: How does a method of a subclass become a method of the base class?

2023-03-27 Thread aapost

On 3/26/23 13:43, Jen Kris wrote:


My question is:  what makes "choose_method" a method of the base class, called 
as self.choose_method instead of UrnaryConstraint.choose_method?  Is it 
super(UrnaryConstraint, self).__init__(strength) or just the fact that Constraint is its 
base class?




When referring to "self" you are referring to an "instance" of the class 
or classes, think in terms of objects (the instance, usually self) vs a 
blueprint (the class, usually cls).


saying self.choose_method(mark) checks the "instance" of self to see if 
it has something called choose_method at run time (instances being 
created at run time). If you have an instance of just Constraint, and 
never had a choose_method defined for it, you will get an error because 
it can't find it (which in this case is presumed as designed). If the 
"instance" is of a subclass of Constraint that does have a 
choose_method, that also inherits the stuff from Constraint, it will 
successfully call it against the "instance" that has been constructed 
with attributes of both classes.


if the instance of a subclass has a definition for something that is in 
the base class, for instance IF UrnaryConstraint had it's own def 
satisfy() method, the instance would call the subclass version. In the 
case given, it does not, so it looks to the parent to see if it has 
inherited satisfy() from higher up.


Hopefully that helps a little bit.. just have to get a feel for OO 
instantiated object vs class..

--
https://mail.python.org/mailman/listinfo/python-list


Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app

2023-03-16 Thread aapost

On 3/15/23 07:37, John O'Hagan wrote:

On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:

On 3/14/23 06:54, John O'Hagan wrote:


[...]





Read an alternative description of the waitKey behavior

>For example, waitKey(0) will display the window infinitely until any 
keypress (it is suitable for image display). waitKey(25) will display a 
frame and wait approximately 25 ms for a key press (suitable for 
displaying a video frame-by-frame). To remove the window, use 
cv::destroyWindow.


I went back to double check and I stand corrected on the "any keypress" 
part. Any keypress on the 'keyboard' does break the wait (as I 
incorrectly concluded and assumed only ESC did).


It is still slightly ambiguous in explaining that when using 25ms, 
keypress or not, the wait breaks at 25ms (or before that if you press a 
keyboard key). For my setup the window is stale at that point, no 
controls, just a stale frame behind the tkinter window that needs to be 
destroyed or reused.


Whether that is the exact behavior on all set-ups isn't clear, the note 
further uses the ambiguous phrasing "might".


>Note: This function should be followed by a call to cv::waitKey or 
cv::pollKey to perform GUI housekeeping tasks that are necessary to 
actually show the given image and make the window respond to mouse and 
keyboard events. Otherwise, it won’t display the image and the window 
might lock up.


It seems with the several variations, behavior varies between them, like 
one comment saying startWindowThread when using c++ and gtk allows you 
to not use waitKey (not the case here.. it seems -- changing my language 
to not misspeak, lol).


I haven't come across any examples beyond imshow running stand-alone as 
in my solution suggestion 2. But waitKey does return a keyvalue for the 
keypess to allow extending the functionality, so you can grab it, do 
something, and go right back to waiting, I haven't seen any use of this 
though.


You can also leave out reference to tkinter all together when using 
startWindowThread:


import sys
import cv2

cv2.startWindowThread()
cv2.namedWindow("W", cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty("W", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.imshow("W", cv2.imread(sys.argv[1]))
while(1):
a = cv2.waitKey(0)
if a == 27:#ESC
break
#elif a == something else, do something
cv2.destroyAllWindows()
exit()

But it still blocks if integrated in to the main tkinter thread, and 
appears to use tkinter under the hood. And as tkinter is considered 
thread-unsafe, the startWindowThread would only be ok when spawned as a 
separate process like the subprocess example.


Anyway, apologies for the mistake on the any key part.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app

2023-03-15 Thread aapost

On 3/15/23 07:37, John O'Hagan wrote:

On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:

On 3/14/23 06:54, John O'Hagan wrote:




Doing a quick read, tkinter is not threadsafe, so diving in to a 
threading solution is probably not the best approach.


But just to throw out another possible solution to see if you can find a 
"good enough" work around that fits your desired behavior.


You could spawn the imshow as it's own program:

file2: sp.py

import sys
import cv2
import tkinter as tk
def show(img, root):
   cv2.namedWindow(str(root), cv2.WND_PROP_FULLSCREEN)
   cv2.setWindowProperty(str(root), cv2.WND_PROP_FULLSCREEN, 
cv2.WINDOW_FULLSCREEN)

   cv2.imshow(str(root), cv2.imread(img))
   cv2.waitKey(0)
   cv2.destroyAllWindows()
   exit()
root=tk.Tk()
show(sys.argv[1], root)
tk.mainloop()


file1:main.py

import cv2
import tkinter as tk
import subprocess
images=['c.jpg', 'b.jpg', 'c.jpg']
control = {
"counter" : 0,
"pid": None
}
def show(control):
   if control["pid"]:
 control["pid"].kill()
   control["pid"] = subprocess.Popen(["python", "sp.py", 
images[control["counter"] % len(images)]])

   control["counter"] += 1
root=tk.Tk()
root.wm_attributes("-topmost", 1)
tk.Button(root, text=' Show ', command=lambda: show(control)).pack()
tk.mainloop()


refactor/design as needed, you track the pid, kill the existing on each 
subsequent show press.


caveats would be that since it is an entirely separate program, if you 
close the main window, the other window will still linger until it is 
also closed. You could try to catch the close to run a .kill() on the 
subprocess if you want right before the exit, etc.


But this gives control back to the main GUI since they are now 
independent of each other and not within the same thread.


If they need to talk to each other in some way more than that, I am sure 
deeper design solutions could be thought up.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app

2023-03-15 Thread aapost

On 3/15/23 07:37, John O'Hagan wrote:

On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:

On 3/14/23 06:54, John O'Hagan wrote:

It works up to a point - I can cycle through the images by clicking
the
button - but if I mouse-click on the displayed image (e.g. to use
the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying
"'Unknown'
is not responding" and giving the option of waiting or forcing
close
(but sometimes these options are greyed out too). Clicking "wait",
if
available, closes the popup but it comes back a few seconds later.
If I
then click on the tkinter window titlebar, the popup changes
to "'Tk'
is not responding". Clicking on the button still works and after a
few
clicks the popup closes.


[...]


I think this particular popup is a Gnome thing, but AIUI most DEs
have
something similar to detect stuck apps. But the app is not stuck.

I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and
the
tkinter event loop, but it's beyond my knowledge.

Any suggestions about causes or workarounds?

Thanks

--

John





I don't get any of the zoom/panning behavior with waitKey(1). But a
couple notes from the web:


https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html

Note

This function should be followed by waitKey function which displays
the
image for specified milliseconds. Otherwise, it won’t display the
image.
For example, waitKey(0) will display the window infinitely until any
keypress (it is suitable for image display). waitKey(25) will display
a
frame for 25 ms, after which display will be automatically closed.
(If
you put it in a loop to read videos, it will display the video
frame-by-frame)

https://pythonexamples.org/python-opencv-imshow/

cv2.waitKey(0) is important for holding the execution of the python
program at this statement, so that the image window stays visible. If
you do not provide this statement, cv2.imshow() executes in fraction
of
a second and the program closes all the windows it opened, which
makes
it almost impossible to see the image on the window.

if I change waitKey to 0, I get the ability to zoom/pan, but it
places
the tk window in a blocked state because it is waiting on cv2 to
return.
If I hit the ESC key, it releases the wait and gives control back to
the
tk window, allowing me to press show again to continue to the next
image.

I can try to change waitKey to a high ms like 1000 and have
zoom/pan
for that amount of time before it gives control back to tk (not a
sensical approach).

The fact that you can still see the image after tk takes back control
is
I believe just a matter of design, some examples show
cv2.destroyAllWindows() after waitKey(0) to clean that up, but of
course
if you are reusing the same window, that destroys the target.

You can resolve that if you move
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)

to inside the show, and destroy it after the wait, and make waitKey
0,
this allows creation/cleanup of that window per image

Hitting ESC when done zooming/panning on each image to get back to
tk.


Thanks for your reply.

I'm afraid in the real app it won't be practical to have no gui control
while viewing images.

But your suggestions have made me realise that my issue has nothing to
do with tkinter, it seems to be the way imshow is supposed to work.
This code gives the same behaviour described above:

cv2.imshow('W', array)
cv2.waitKey(100)
time.sleep(20)

I've seen the docs you mention that say the window will close if no
keypress happens within the waitKey time, but that doesn't seem to be
what happens as you say. After waitKey returns, the image remains and
everything works, but for some reason the desktop complains about an
unresponsive app.

You can use cv2 to display video with:

while 1:
  frame = camera.read() #pseudocode
  cv2.imshow('W', frame)
  cv2.waitKey(1)

(In fact this used to work without waitKey if you called
cv2.startWindowThread() after creating the window, but that stopped
working at some point.)

The fact that this works, presumably because the image is being
replaced often enough, suggested the following workaround to my
problem, using a call to after() to re-display the current image before
the desktop starts complaining:

images=[cv2.imread(i) for i in ('a.jpg', 'b.jpg', 'c.jpg')]

cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)

counter=[0]

image = [None]

def show():
im = images[counter[0] % 3]
cv2.imshow('W', im)
cv2.waitKey(1)
image[:] = [im]
counter[0] += 1

root=Tk()
root.wm_attributes(&q

Re: =- and -= snag

2023-03-14 Thread aapost




On 3/14/23 18:50, Rob Cliffe wrote:
On 14/03/2023 21:28, avi.e.gr...@gmail.com wrote:



Type hints are actually situationally quite useful (though yes, kind of 
hard to understand when you first come across their use, largely because 
of things like Union). And I wouldn't recommend their use in all 
situations because maintaining them is frustrating.


xmlschema uses them extensively though and I appreciate them. The module 
does what I need where all the other offerings failed, and parsing the 
source because of the subject matter would have been harder to quickly 
grok without them.


Alternatively, I spent quite a while on the subject of pep 232 (before 
finding the pep) trying to understand it because intuitively I felt like 
there was something I must be missing. Turns out there isn't. And what 
my thoughts were assuming was the intent, are discussed under "Future 
Directions", but when seeing it laid out like that, subjectively the 
dissenting conclusion of "It opens the way to mind abuse." is more 
correct. "What if I launch a virtual machine inside a virtual machine 
inside a virtual machine inside a virtual machine, what happens?"...


If I want to have a grouping of minor functionality or attributes on a 
callable, I can get that with creating a class that defines __call__.


I would say 232 is fine for what it is (I don't have to use it, and in 
many cases it probably is), but the only wide use I know of that I have 
come across is in the standard library ElementTree:

# For tests and troubleshooting
register_namespace._namespace_map = _namespace_map

Which I hate that variable and it's design with a passion. lol.

And exactly, the more a language starts policing my ability to be 
flexible in my trying to find a creative solution to a problem, the less 
I want to use it. Sometimes to have something useably beautiful to 
interact with requires something painfully complex under the surface. 
Hiding and avoiding all complexity away so you never have to see it or 
make mistakes prevents you from growing.


Anyway, the 20th rule to The Zen of Python is:
Don't believe your own bullshit *shrug*


--
https://mail.python.org/mailman/listinfo/python-list


Re: Tkinter and cv2: "not responding" popup when imshow launched from tk app

2023-03-14 Thread aapost

On 3/14/23 06:54, John O'Hagan wrote:

Hi list

I'm trying to use cv2 to display images created as numpy arrays, from
within a tkinter app (which does other things with the arrays before
they are displayed as images). The arrays are colour-coded
visualisations of genomes and can be over a billion elements in size,
and I've found the PIL methods to display images in tkinter are too
slow and memory-heavy.

Here is minimal code that demonstrates the problem in the subject line:

import cv2
from tkinter import *

images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths

cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
cv2.waitKey(1)
counter[0] += 1

root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()

It works up to a point - I can cycle through the images by clicking the
button - but if I mouse-click on the displayed image (e.g. to use the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying "'Unknown'
is not responding" and giving the option of waiting or forcing close
(but sometimes these options are greyed out too). Clicking "wait", if
available, closes the popup but it comes back a few seconds later. If I
then click on the tkinter window titlebar, the popup changes to "'Tk'
is not responding". Clicking on the button still works and after a few
clicks the popup closes.

This happens under both x11 and wayland, but under wayland, I  also get
this error:

"QSocketNotifier: Can only be used with threads started with QThread
qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"

and only every second button press displays a new image ,with only
every second image displayed.

I think this particular popup is a Gnome thing, but AIUI most DEs have
something similar to detect stuck apps. But the app is not stuck.

I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and the
tkinter event loop, but it's beyond my knowledge.

Any suggestions about causes or workarounds?

Thanks

--

John





I don't get any of the zoom/panning behavior with waitKey(1). But a 
couple notes from the web:



https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html

Note

This function should be followed by waitKey function which displays the 
image for specified milliseconds. Otherwise, it won’t display the image. 
For example, waitKey(0) will display the window infinitely until any 
keypress (it is suitable for image display). waitKey(25) will display a 
frame for 25 ms, after which display will be automatically closed. (If 
you put it in a loop to read videos, it will display the video 
frame-by-frame)


https://pythonexamples.org/python-opencv-imshow/

cv2.waitKey(0) is important for holding the execution of the python 
program at this statement, so that the image window stays visible. If 
you do not provide this statement, cv2.imshow() executes in fraction of 
a second and the program closes all the windows it opened, which makes 
it almost impossible to see the image on the window.





if I change waitKey to 0, I get the ability to zoom/pan, but it places 
the tk window in a blocked state because it is waiting on cv2 to return. 
If I hit the ESC key, it releases the wait and gives control back to the 
tk window, allowing me to press show again to continue to the next image.


I can try to change waitKey to a high ms like 1000 and have zoom/pan 
for that amount of time before it gives control back to tk (not a 
sensical approach).


The fact that you can still see the image after tk takes back control is 
I believe just a matter of design, some examples show 
cv2.destroyAllWindows() after waitKey(0) to clean that up, but of course 
if you are reusing the same window, that destroys the target.


You can resolve that if you move
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

to inside the show, and destroy it after the wait, and make waitKey 0, 
this allows creation/cleanup of that window per image


Hitting ESC when done zooming/panning on each image to get back to tk.
--
https://mail.python.org/mailman/listinfo/python-list


Re: =- and -= snag

2023-03-14 Thread aapost

On 3/13/23 17:26, Morten W. Petersen wrote:


It went into negative numbers,
when that shouldn't have been possible.

Turns out I had a very small typo, I had =- instead of -=.

Isn't it unpythonic to be able to make a mistake like that?



That is why I tell Alice it is always best to stay positive and use 
x-=-1 to avoid situations like that. *shrugs*


--
https://mail.python.org/mailman/listinfo/python-list


Re: Problem with wxPython form

2023-03-12 Thread aapost

On 3/10/23 15:15, Chris wrote:
Hi everyone. I'm new to Python and wxPython. I've got a form I use to 
calculate the Sq In of a leather project.


I'm using python 3.9.13 and wxPython 4.20

I'm having the following issues:
1) When I come into the form, no grid cell has the focus set - I start 
typing and nothing happens. I have to click the cell.
If I hit Tab or Enter, the OnKeyDown fires, but does not move to the 
appropriate cell - it does nothing but run the update and move off of 
the current cell.

The action I'm trying to make is this
ENTER KEY: Always go down 1 row and to col 0
TAB, if Col 0 Move to Col 1 on same row, if Col 1 go to Row +1, Col 0
I also need to have what3ever cell it is supposed to land on to get the 
focus so I can just type.

Currently I have to click in each cell I want/need to add.
There could be up to 20 pieces of leather with differing sizes, so in 
order to accurately calculate the Sq In, I need to get all the 
measurements in.
The form, one of several tabs, comes up and does everything else I've 
coded for great. Just no navigation.

Can anyone assist? Here is the module with the form


Your source was badly mangled by whatever you submitted it in, so it was 
very difficult to parse. Additionally, it is usually best to strip your 
code down to a minimal example of the issue, and remove any references 
to modules that are likely not publicly available (i.e. alwlogic) 
(putting basic dummy data in place if need be).


That being said:
2 things will likely address your issue, remove 
self.grid.SetCellHighlightPenWidth(0) since this is making the highlight 
box of the selected cell invisible, this is preventing you from seeing 
that your tabs and enters are working as you are expecting.


Next, the wx.EVT_KEY_DOWN binding is grabbing all key presses, and your 
function is only addressing 2 keys, you need to add an else: to the end 
with an event.Skip() to allow the default behaviors of the grid cells to 
pass through for other keys (i.e. typing).


That should get you the behavior you need.


--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-10 Thread aapost

On 3/10/23 22:16, Thomas Passin wrote:

On 3/10/2023 7:07 PM, aapost wrote:
which does start to break down readability due to line length, as 
there isn't really an indention rule set for something uncommonly used.


but some renaming makes the pattern clearer

pids.update({"messages" :subprocess.Popen(["cmd1"])}) if not 
pids["messages"] else None,
pids.update({"syslog" :subprocess.Popen(["cmd2"])}) if not 
pids["syslog"] else None,
pids.update({"kern" :subprocess.Popen(["cmd3"])}) if not pids["kern"] 
else None,
pids.update({"user" :subprocess.Popen(["cmd4"])}) if not pids["user"] 
else None,


I'd make the pattern in this example even more understandable and less 
error-prone:


def update_pids(target):
     cmd = ["tail", "-n", "1", "-f", f"/var/log/{target}"]
     pids.update({target: subprocess.Popen(cmd)}) if not \
     pids[target] else None

lambda x: ( # The Tk callback includes an event arg, doesn't it?
     update_pids('messages'),
     update_pids('syslog'),
     # etc
   )



So yeah, that's along the same lines. cmd could be in a function, or 
just outside the lambdas. Could also do from subprocess import Popen to 
shorten that, etc.


The .trace( on the tkinter Vars use events, so you have to do something 
like

lambda _a, _b, _c: stuff

But not in the above case. My focus these last couple week hasn't used 
any of those so the specifics aren't as fresh (still heading back in 
that direction), when required python will definitely let you know, lol.


The additional note in the above is, when taking the def route above, 
the thing you would have to consider is what scope is the dictionary pids?


Do you need to submit it to the lambda and subsequently the function 
such as

lambda pids=pids: (
update_pids("messages", pids),
update_pids("syslog", pids),

So that update_pids can access it? Or in your design do you have a 
separate management of it that def update_pids already has access to? 
(either is valid depending on design intent).


In the direction I am going, in trying to build some compound widgets, 
and I need to grab stuff from one widget and send it to another within 
the same widget group, so in cases where it goes to an external 
function, it has to be sent as an arg through the lambda 
whatever=whatever: ( function(whatever), )



--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-10 Thread aapost

On 3/9/23 15:25, Thomas Passin wrote:

 >>> # this is a code snippet from a Tkinter gui app
 >>> # in this case lambda is quite convenient
 >>> self.btn_cancel = Button(self.progress_container, text='Cancel',
 >>> command=lambda: subprocess.call('taskkill /f /im uberzip.exe',
 >>> shell=True))



def kill_uberzip():
     """Kill an external running program named uberzip.exe."""
     subprocess.call('taskkill /f /im uberzip.exe', shell=True))

self.btn_cancel = Button(self.progress_container, text='Cancel',
  command = kill_uberzip())

This way, it's easy to understand what btn_cancel() will do each time 
you scan that line of code.  Using the lambda makes you reparse the line 
and spend mental effort each time you scan it.  And this way, you know 
directly that the button is going to cause a side effect outside your 
program, which you have to infer (an indirect mental operation) when you 
scan the lambda.


For this particular example, it might turn out that there could be more 
than one instance of uberzip.exe running at the same time.  Which one 
should be killed, and how do you kill the right one?  With the function, 
you can get those details under control, but I hate to think what might 
happen to the lambda expression.


Yes, of course, there can be times when the lambda expression is 
somewhat easy to understand and the side effects are harmless.  In that 
case, it may be easy enough to grasp quickly that the anonymous function 
would not benefit from having a name.  So OK, it's not a hard-and-fast 
rule.


The not knowing which uberzip to kill is a design choice, the bad design 
of the the example is not really the fault of the lambda.


And in general with naming anything, much has to do with context. 
Buttons perform actions, it should be implicitly understood at the 
button level and in context of where it is placed what it is a button is 
for. A button without a command has no use, so it is understood it needs 
to do something, with good design it would be better to know what the 
buttons purpose is, rather than having to then parse an additional 
function to figure it out.



Case in point, to solve the subprocess problem, (ignoring the "whys" and 
the lack of usefulness of this example, just grok the pattern, maybe you 
are designing a game of whack-a-mole and are watching 4 things and need 
to do something when you see a change from 1 of them)



import tkinter as tk
import subprocess

main = tk.Tk()
main.pids = {
"messages": None,
"syslog": None,
"kern": None,
"user": None,
}

tk.Button(
master=main,
text="Start tailing logs",
command=lambda: (
		main.pids.update({"messages" :subprocess.Popen(["tail", "-n", "1", 
"-f", "/var/log/messages"])}),
		main.pids.update({"syslog" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/syslog"])}),
		main.pids.update({"kern" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/kern.log"])}),
		main.pids.update({"user" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/user.log"])}),

),
).pack()

tk.Button(
master=main,
text="Kill messages tail",
command=lambda: (
main.pids["messages"].kill() if main.pids["messages"] else None,
),
).pack()

tk.Button(
master=main,
text="Kill syslog tail",
command=lambda: (
main.pids["syslog"].kill() if main.pids["syslog"] else None,
),
).pack()

tk.Button(
master=main,
text="Kill kern tail",
command=lambda: (
main.pids["kern"].kill() if main.pids["kern"] else None,
),
).pack()

tk.Button(
master=main,
text="Kill user tail",
command=lambda: (
main.pids["user"].kill() if main.pids["user"] else None,
),
).pack()

tk.Button(
master=main,
text="Kill all tails",
command=lambda: (
main.pids["messages"].kill() if main.pids["messages"] else None,
main.pids["syslog"].kill() if main.pids["syslog"] else None,
main.pids["kern"].kill() if main.pids["kern"] else None,
main.pids["user"].kill() if main.pids["user"] else None,
),
).pack()

main.mainloop()


--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-10 Thread aapost

On 3/10/23 18:46, aapost wrote:

     main.pids.update({"messages" :subprocess.Popen(["tail", "-n", 
"1", "-f", "/var/log/messages"])}),
     main.pids.update({"syslog" :subprocess.Popen(["tail", "-n", 
"1", "-f", "/var/log/syslog"])}),
     main.pids.update({"kern" :subprocess.Popen(["tail", "-n", "1", 
"-f", "/var/log/kern.log"])}),
     main.pids.update({"user" :subprocess.Popen(["tail", "-n", "1", 
"-f", "/var/log/user.log"])}),

 ),


To pre-emptively address the bug there it would need to be:

main.pids.update({"messages" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/messages"])}) if not main.pids["messages"] else None,
main.pids.update({"syslog" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/syslog"])}) if not main.pids["syslog"] else None,
main.pids.update({"kern" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/kern.log"])}) if not main.pids["kern"] else None,
main.pids.update({"user" :subprocess.Popen(["tail", "-n", "1", "-f", 
"/var/log/user.log"])}) if not main.pids["user"] else None,


which does start to break down readability due to line length, as there 
isn't really an indention rule set for something uncommonly used.


but some renaming makes the pattern clearer

pids.update({"messages" :subprocess.Popen(["cmd1"])}) if not 
pids["messages"] else None,
pids.update({"syslog" :subprocess.Popen(["cmd2"])}) if not 
pids["syslog"] else None,
pids.update({"kern" :subprocess.Popen(["cmd3"])}) if not pids["kern"] 
else None,
pids.update({"user" :subprocess.Popen(["cmd4"])}) if not pids["user"] 
else None,


and adding a comment to say something like
# starting a series of processes via lambda tuple sequence if process 
not running

#pattern: p.update({"name":sp.Popen(cmd)}) if not p["name"] else None,

--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-09 Thread aapost

On 3/9/23 16:37, Cameron Simpson wrote:

On 09Mar2023 09:06, Alan Gauld  wrote:


Just a note that some code formatters use a trailing comma on the last 
element to make the commas fold points. Both yapf (my preference) and 
black let you write a line like (and, indeed, flatten if short enough):


     ( a, b, c )

but if you write:

     ( a, b, c, )

they'll fold the lines like:

     ( a,
   b,
   c,
     )
Cameron Simpson 



Thanks for the info, good to know, I actually do like the idea of 
trailing commas for tuples (helps prevent things like the difference 
between ("abc") and ("abc",) and makes swapping things around nicer.


I've just been using a lot of json lately and it has been subconsciously 
training me different, lol.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-09 Thread aapost

On 3/9/23 04:06, Alan Gauld wrote:




Thank you for the feedback, I appreciate the comments.

To add a little extra, there is actually a reason I lean toward overuse 
of .config() for a lot of things even though they could be sent to the 
constructor (other than that w["attribute"]= doesn't work in a lambda).


It comes from a use case I needed to solve in the shortcoming of tk with 
frames not being scrollable, requiring a canvas.


When dynamically generating a series of widgets (i.e. loading a custom 
json/xml layout config file), getting the scrollable area right is a bit 
of a chore with a catch-22 situation.


With an inner frame in a canvas, pack_propogate(tk.True) is the default 
on the frame, so that the children dictate the size, but in doing so it 
prevents the frame from expanding to the full width of the area it's 
given within the UI layout.


If you turn pack_propogate to tk.False, it breaks the ability for the 
canvas to return bbox(tk.ALL) (which is kind of rough anyway as the tcl 
docs state "The return value may overestimate the actual bounding box by 
a few pixels."). So you end up with 1,1 and no way of knowing what size 
scrollable area to set. Trying to obtain this value from a source 
outside of the canvas requires knowing what you are placing the canvas 
in each time, and was creating a similar catch-22 as the outer widget 
doesn't know what it wants it's size to be without knowing what the 
inner widget wants.. Switching to False, grabbing bbox, then back to 
True of course causes an unsightly flicker and disrupts the smoothness 
of the user experience.


So my solution is to create a widget instantiator which does a few 
things, mainly adding something I call a "ghost".


it's something like this:

instantiator(qlass, master, config_args=None, pack_args=None, 
init_args=None, ghost=False):

  if not init_args:
init_args = {}
  object = qlass(master=master, **init_args)
  if hasattr(master, "ghost"):
object.ghost = qlass(master=master.ghost, **init_args)
  elif ghost:
object.ghost = qlass(master=tk.Frame(), **init_args)

When i pass it a canvas and say ghost=True, the canvas gets a .ghost 
duplicate, which is attached to an arbitrary frame that I never pack and 
stays invisible.


Subsequent widgets created to the canvas then see that their parent, 
starting with the canvas, have a ghost, and in return get a ghost of 
themselves attached to their parents ghost.


This allows you to get an accurate bbox size from the unseen ghost 
canvas that mirrors the visible version.


Keeping the init_args down to only what is necessary helps in 
consistency, and the subsequent config_args and pack_args I have in 
their respective dicts.


This also allows me to create a quality of life pack function I call ppack()

def ppack(self):
  self.pack(**self.pack_args)
  if hasattr(self, "ghost"):
self.ghost.pack(**self.ghost.pack_args)
  return self

That allows each primary widget to manage and track their own set of 
configurations.


Of course I could strip a lot of that crap out if I find a better and 
smooth way of obtaining those bbox numbers, but I didn't see any quick 
solutions in glancing through the tcl/tk source so I went with what 
works for now.








--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-09 Thread aapost

On 3/9/23 00:13, Thomas Passin wrote:




lol.. 'us'..

So.. to give an example from your own code:

but_play = Tk.Button(_frame, text='Play', width = BUTTONWIDTH + 1, pady 
= PADY, command=lambda x=plotmgr:play_macro(x), bg = BUTTON_BG, font = 
NEWFONT)


Can be written as:

b = Tk.Button(master=_frame)
b.config(text='Play', width = BUTTONWIDTH + 1, pady = PADY, 
command=lambda x=plotmgr:play_macro(x), bg = BUTTON_BG, font = NEWFONT)


.config() is just a way of adding/modifying most of the same 
initialization arguments after the instantiation of the object.


tkinter objects are flexible by design, You can also do
b["text"] = "adsfa"
b["command"] = lambda: (a,b,c,d)

I could also rewrite the original example with the exact same result:
b = tk.Button(
master=main,
text="Enable",
command=lambda: (
e1.config(state="normal"),
e2.config(state="normal"),
e3.config(state="normal")
)
)
b.pack()

b is not changing states at any point. Nothing "happens" to state when 
.config() is called. b does nothing without a command= configured. 
.config() is binding the button press event via command= to a call so 
than an action can occur on button press. There is no 'instead' of 
b.config() (unless of course using one of the equivalent examples above 
to do the exact same thing)


The disconnect is not in the not understanding of my code, it's in the 
not understanding of .config(). (which is no big deal, I forgot what a 
file buffer was just this week, lol)


So, as far as the examples, they are simplified abstract 
psuedo-like-code illustrations to accompany the ask of and open ended 
question regarding a programming practice.


(I did forgot to include main.mainloop() at the end of each one, as I 
was using python -i when I quickly wrote them)


example 1 described:
A button press does sequence of actions against widgets, i.e. I have 3 
disabled widgets, I press a button, now in sequence all 3 are enabled so 
I can type in them. Without a lambda tuple sequence this requires a def 
and an another lambda.


example 2 described:
dict with an attribute assigned to a label (you can't assign directly to 
external variables within a lambda, but you can call something with a 
method like .update(), which why would that be considered any different 
than any other state changing call?)
Then lambda assigned to a label and called to start it up, recursively 
performs an update to said widget every 1 second. There are several ways 
to do this without a lambda tuple sequence, none as concise.


The 'what I am trying to do' is ask a question regarding opinions and 
practices on issuing a sequence of actions within a lambda via a tuple 
(since the common practice approaches against it - mainly with tkinter - 
feel more convoluted), and in doing so leaving it open ended to get a 
feel on what opinions are, and to see if any opinions influence mine.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Lambda returning tuple question, multi-expression

2023-03-08 Thread aapost

On 3/8/23 16:56, aapost wrote:

Thomas > Cameron



def set_entries_enabled_state(enabled = True):
 state = 'normal' if enabled else 'disabled'
 for e in (e1, e2, e3):
 e.config(state=state)

def config_b_and_entries(enabled = True):
 state = 'normal' if enabled else 'disabled'
 b.config(state = state)
 set_entries_enabled_state(enabled)


I admit adding commenting might be useful to clearly express intent for 
patterns I don't commonly see. But when you are managing a bunch of 
widgets and experimenting with the design, the variable name word salad 
approach 'in the name of readability' starts to melt your brain a bit 
and it all starts too look like a wall of..:


the_thing.the_things_thing.do_this_thing_to_that_other_thing_but_only_if_this_one_time()

So the button b in the example only needs a reference to a callable 
configured, set with the command= parameter in .config()


The code:

def func():
  pass
b.config(command=func)

Is equivalent. So that if the button is clicked, code at func (or the 
lambda) gets called.


In both cases (as per my intent), care about the return values of the 
expressions does not matter and I am fairly certain regardless of what 
the callable returns, it does not get evaluated/assigned/considered 
anywhere. (at least that is how I have been considering it, a void 
foo(void) if you will..). Now as far as the tuple, yes, left to right 
evaluation is what I expect, (hope anyway, lol),


meaning (3, False, 1, x := 5+9, print("hello{}".format(x)))
would return a tuple of (3, False, 1, 14, None) which gets assigned to 
nothing and the string "hello14" printed to console.


Now.. When you want to assign a callable that requires args, the main 
examples people give are a combo of them both,


def func(x,y,z):
  pass
x = y = x = "something"
b.config(command=lambda x,y,z: func(x,y,z))

So as far as the examples given above (which I can't really parse), if 
you meant for passing in a bool value, to do so would require something 
like:


b.config(command=lambda enabled: config_b_and_entries(enabled))

Which that type of thing to me gets even harder to grok after a while, 
and I guess for me I find having to go to a different scope or a 
separate file to parse a bunch of definitions like these:


def set_entries_enabled_state(enabled = True):

def config_b_and_entries(enabled = True):

ends up taking me out of an object oriented focus / headspace of the 
layout at hand.



And it is sort of like.. Ok I can either

b.config(command=lambda: (
a.expr,
b.expr.update({something: "morecomplicated"}),
c.expr
)
)

OR..

b.config(command=lambda a=a, b=b, c=c, s=something: foo(a, b, c, s))

somewhere else:
def foo(a, b, c, something):
a.expr
b.expr.update({something: "morecomplicated"})
c.expr


When you are trying to add a bit of dynamic feel to the tediousness of 
the widget management, keeping things kind of contained to their object 
just feels more natural to me (at the moment anyway).. (all the packing, 
unpacking, making collections of widgets within frames appear or go away 
based on states, colour changes based on input, dynamic widget 
generation and where their relative attachment should go, etc)


I read a lot of sentiment against complicated lambdas suggesting one 
should go for more functions, but I guess I feel pushing a more 
complicated lambda to contain behavior more closely to an instance feels 
more intuitive at the moment (and the sentiment against it doesn't 
really resonate with me), so long as it isn't introducing some inherent 
behavioral flaw or bug I am blind to..


Of course I might change my mind at some point during a refactor and 
think "what the hell is that, why didn't I just..".. Which will probably 
come in a few weeks. lol


One additional note on the Thread comment, I haven't really needed to 
dig in to that too deeply, but Threading is amazing for tkinter UI 
troubleshooting,


if you add something like:
t = threading.Thread(target=maintk.mainloop)
and run it with python -i
so long has you have attached every widget to some relative position on 
to the root (maintk in this case), you can interact with any 
object/widget directly live to see what is going on or what a change does.


--
https://mail.python.org/mailman/listinfo/python-list


Lambda returning tuple question, multi-expression

2023-03-08 Thread aapost
When making a UI there are a lot of binding/trace operations that need 
to occur that lead to a lot of annoying 1 use function definitions. I 
don't really see lambda use like below.


Giving 2 working lambda examples using a returned tuple to accomplish 
multiple expressions - what sort of gotchas, if any, might make the 
following bad practice if I am missing something?



Example 1:

import tkinter as tk

main = tk.Tk()
e1 = tk.Entry(master=main)
e1["state"] = "disabled"
e1.pack()
e2 = tk.Entry(master=main)
e2["state"] = "disabled"
e2.pack()
e3 = tk.Entry(master=main)
e3["state"] = "disabled"
e3.pack()

b = tk.Button(master=main, text="Enable")
b.config(
command=lambda: (
e1.config(state="normal"),
e2.config(state="normal"),
e3.config(state="normal")
)
)
b.pack()


Example 2:

import tkinter as tk

main = tk.Tk()
l = tk.Label(master=main)
l.a = {"seconds":0}
l._updater = lambda: (
l.a.update({"seconds": 1 + l.a["seconds"]}),
l.config(text=l.a["seconds"]),
l.after(ms=1000, func=l._updater)
)
l._updater()
l.pack()

--
https://mail.python.org/mailman/listinfo/python-list


Re: Bug 3.11.x behavioral, open file buffers not flushed til file closed.

2023-03-06 Thread aapost

On 3/5/23 19:02, Cameron Simpson wrote:

On 05Mar2023 10:38, aapost  wrote:

Additionally (not sure if this still applies):
flush() does not necessarily write the file’s data to disk. Use 
flush() followed by os.fsync() to ensure this behavior.


Yes. You almost _never_ need or want this behaviour. A database tends to 
fsync at the end of a transaction and at other critical points.


However, once you've `flush()`ed the file the data are then in the hands 
of the OS, to get to disc in a timely but efficient fashion. Calling 
fsync(), like calling flush(), affects writing _efficiency_ by depriving 
the OS (or for flush(), the Python I/O buffering system) the opportunity 
to bundle further data efficiency. It will degrade the overall performance.


Also, fsync() need not expedite the data getting to disc. It is equally 
valid that it just blocks your programme _until_ the data have gone to 
disc. I practice it probably does expedite things slightly, but the real 
world effect is that your pogramme will gratuitously block anyway, when 
it could just get on with its work, secure in the knowledge that the OS 
has its back.


flush() is for causality - ensuring the data are on their way so that 
some external party _will_ see them rather than waiting forever for data 
with are lurking in the buffer.  If that external party, for you, is an 
end user tailing a log file, then you might want to flush(0 at the end 
of every line.  Note that there is a presupplied line-buffering mode you 
can choose which will cause a file to flush like that for you 
automatically.


So when you flush is a policy decision which you can make either during 
the programme flow or to a less flexible degree when you open the file.


As an example of choosing-to-flush, here's a little bit of code in a 
module I use for writing packet data to a stream (eg a TCP connection):

https://github.com/cameron-simpson/css/blob/00ab1a8a64453dc8a39578b901cfa8d1c75c3de2/lib/python/cs/packetstream.py#L624

Starting at line 640: `if Q.empty():` it optionally pauses briefly to 
see if more packets are coming on the source queue. If another arrives, 
the flush() is _skipped_, and the decision to flush made again after the 
next packet is transcribed. In this way a busy source of packets can 
write maximally efficient data (full buffers) as long as there's new 
data coming from the queue, but if the queue is empty and stays empty 
for more that `grace` seconds we flush anyway so that the receiver 
_will_ still see the latest packet.


Cheers,
Cameron Simpson 


Thanks for the details. And yes, that above quote was from a 
non-official doc without a version reference that several forum posts 
were referencing, with no further reasoning as to why they make the 
suggestion or to what importance it was (for the uninformed trying to 
parse it, the suggestion could be because of anything, like python 
lacking something that maybe was fixed, or who knows.) Thanks.



--
https://mail.python.org/mailman/listinfo/python-list


Re: Bug 3.11.x behavioral, open file buffers not flushed til file closed.

2023-03-06 Thread aapost

On 3/5/23 09:35, aapost wrote:
I have run in to this a few times and finally reproduced it. Whether it 
is as expected I am not sure since it is slightly on the user, but I can 
think of scenarios where this would be undesirable behavior.. This 
occurs on 3.11.1 and 3.11.2 using debian 12 testing, in case the 
reasoning lingers somewhere else.


If a file is still open, even if all the operations on the file have 
ceased for a time, the tail of the written operation data does not get 
flushed to the file until close is issued and the file closes cleanly.


2 methods to recreate - 1st run from interpreter directly:

f = open("abc", "w")
for i in range(5):
   f.write(str(i) + "\n")

you can cat the file and see it stops at 49626 until you issue an f.close()

a script to recreate:

f = open("abc", "w")
for i in range(5):
   f.write(str(i) + "\n")
while(1):
   pass

cat out the file and same thing, stops at 49626. a ctrl-c exit closes 
the files cleanly, but if the file exits uncleanly, i.e. a kill command 
or something else catastrophic. the remaining buffer is lost.


Of course one SHOULD manage the closing of their files and this is 
partially on the user, but if by design something is hanging on to a 
file while it is waiting for something, then a crash occurs, they lose a 
portion of what was assumed already complete...


>Cameron
>Eryk

Yeah, I later noticed open() has the buffering option in the docs, and 
the warning on a subsequent page:


Warning
Calling f.write() without using the with keyword or calling f.close() 
might result in the arguments of f.write() not being completely written 
to the disk, even if the program exits successfully.


I will have to set the buffer arg to 1. I just hadn't thought about 
buffering in quite a while since python just handles most of the things 
lower level languages don't. I guess my (of course incorrect) 
assumptions would have leaned toward some sort of auto handling of the 
flush, or a non-buffer default (not saying it should).


And I understand why it is the way it is from a developer standpoint, 
it's sort of a mental thing in the moment, I was in a sysadmin way of 
thinking, switching around from doing things in bash with multiple 
terminals, forgetting the fundamentals of what the python interpreter is 
vs a sequence of terminal commands.


That being said, while "with" is great for many use cases, I think its 
overuse causes concepts like flush and the underlying "whys" to atrophy 
(especially since it is obviously a concept that is still important). It 
also doesn't work well when doing quick and dirty work in the 
interpreter to build a file on the fly with a sequence of commands you 
haven't completely thought through yet, in addition to the not wanting 
to close yet, the subsequent indention requirement is annoying. f = 
open("fn", "w", 1) will be the go to for that type of work since now I 
know. Again, just nitpicking, lol.


--
https://mail.python.org/mailman/listinfo/python-list


Re: Cutting slices

2023-03-05 Thread aapost

On 3/5/23 17:43, Stefan Ram wrote:

   The following behaviour of Python strikes me as being a bit
   "irregular". A user tries to chop of sections from a string,
   but does not use "split" because the separator might become
   more complicated so that a regular expression will be required
   to find it. But for now, let's use a simple "find":
   
|>>> s = 'alpha.beta.gamma'

|>>> s[ 0: s.find( '.', 0 )]
|'alpha'
|>>> s[ 6: s.find( '.', 6 )]
|'beta'
|>>> s[ 11: s.find( '.', 11 )]
|'gamm'
|>>>

   . The user always inserted the position of the previous find plus
   one to start the next "find", so he uses "0", "6", and "11".
   But the "a" is missing from the final "gamma"!
   
   And it seems that there is no numerical value at all that

   one can use for "n" in "string[ 0: n ]" to get the whole
   string, isn't it?




I would agree with 1st part of the comment.

Just noting that string[11:], string[11:None], as well as string[11:16] 
work ... as well as string[11:324242]... lol..

--
https://mail.python.org/mailman/listinfo/python-list


Re: Bug 3.11.x behavioral, open file buffers not flushed til file closed.

2023-03-05 Thread aapost

On 3/5/23 09:35, aapost wrote:




Guess it could just be an annoying gotcha thing on me.

calling at least

f.flush()

in any cases where an explicit close is delayed would be the solution.

Additionally (not sure if this still applies):
flush() does not necessarily write the file’s data to disk. Use flush() 
followed by os.fsync() to ensure this behavior.

--
https://mail.python.org/mailman/listinfo/python-list


Bug 3.11.x behavioral, open file buffers not flushed til file closed.

2023-03-05 Thread aapost
I have run in to this a few times and finally reproduced it. Whether it 
is as expected I am not sure since it is slightly on the user, but I can 
think of scenarios where this would be undesirable behavior.. This 
occurs on 3.11.1 and 3.11.2 using debian 12 testing, in case the 
reasoning lingers somewhere else.


If a file is still open, even if all the operations on the file have 
ceased for a time, the tail of the written operation data does not get 
flushed to the file until close is issued and the file closes cleanly.


2 methods to recreate - 1st run from interpreter directly:

f = open("abc", "w")
for i in range(5):
  f.write(str(i) + "\n")

you can cat the file and see it stops at 49626 until you issue an f.close()

a script to recreate:

f = open("abc", "w")
for i in range(5):
  f.write(str(i) + "\n")
while(1):
  pass

cat out the file and same thing, stops at 49626. a ctrl-c exit closes 
the files cleanly, but if the file exits uncleanly, i.e. a kill command 
or something else catastrophic. the remaining buffer is lost.


Of course one SHOULD manage the closing of their files and this is 
partially on the user, but if by design something is hanging on to a 
file while it is waiting for something, then a crash occurs, they lose a 
portion of what was assumed already complete...

--
https://mail.python.org/mailman/listinfo/python-list


Re: Python - working with xml/lxml/objectify/schemas, datatypes, and assignments

2023-01-15 Thread aapost

On 1/11/23 13:21, Dieter Maurer wrote:

aapost wrote at 2023-1-10 22:15 -0500:

On 1/4/23 12:13, aapost wrote:

On 1/4/23 09:42, Dieter Maurer wrote:
...

You might have a look at `PyXB`, too.
It tries hard to enforce schema restrictions in Python code.

...

Unfortunately picking it apart for a while and diving deeper in to a
rabbit hole, PyXB looks to be a no-go.

PyXB while interesting, and I respect it's complexity and depth, is
lacking in design consistency in how it operates if you are trying to
modify and work with the resulting structure intuitively.
... problem with simple types ...


I use `PyXB` in `dm.saml2` and `dm.zope.saml2`, i.e. with
the SAML2 schema definitions (which include those
of XML signature and XML encryption).
I had no problems with simple types. I just assign them to attributes
of the Python objects representing the XML elements.
`PyXB` does the right thing when it serializes those objects into XML.


It does do a lot of good things, and I am sad to see all the good work 
in it not get used, but for me it really boils down to what it can sum 
up itself in a couple comments from the author in it's first file (which 
I appreciate them and their honesty, because those are comments I could 
see myself writing in a similar situation)...


##
class cscRoot (object):
"""This little bundle of joy exists because in Python 2.6 it
became an error to invoke C{object.__init__} with parameters (unless
you also override C{__new__}, in which case it's only a warning.
Whatever.).  Since I'm bloody not going to check in every class
whether C{super(Myclass,self)} refers to C{object} (even if I could
figure out how to do that, 'cuz the obvious solutions don't work),
we'll just make this thing the root of all U{cooperative super

calling<http://www.geocities.com/foetsch/python/new_style_classes.htm#super>}
hierarchies.
##

##
def __init__ (self, *args, **kw):
# Oh gross.  If this class descends from list (and probably 
dict), we
# get here when object is *not* our direct superclass.  In that 
case,

# we have to pass the arguments on up, or the strings don't get
# created right.  Below is the only way I've figured out to 
detect the

# situation.
#
# Note that we might also get here if you mix-in a class that used
# object as a parent instead of cscRoot.  Don't do that. 
Printing the

# mro() is a decent way of identifying the problem.
##

using that suggestion you can see that on simple types
>>> pyxbxmlroot.SomeString._mro()
[, 'pyxb.binding.basis.simpleTypeDefinition'>, 'pyxb.binding.basis._TypeBinding_mixin'>, 'pyxb.utils.utility.Locatable_mixin'>, 'pyxb.utils.utility._DeconflictSymbols_mixin'>, 'pyxb.binding.basis._DynamicCreate_mixin'>, , 
, ]


it has a python type that it sends all the way up right next to object, 
when that doesn't actually occur until after simpleType in class string 
(basis.simpleTypeDefinition, str):


This makes the object dependent on it's parent, since it itself IS the 
value, I can't assign to or do anything to it by itself, or it and all 
the other stuff goes away. As designed it is very hard to change 
anything in it without breaking something.


After working with xmlschema, it pretty much confirmed my assumptions 
that it doesn't need to be that way. I was able to follow what was going 
on and tweak xmlschema fairly easily.


That and the fact that PyXB was abandoned 5-6 years ago make it a strong 
no-go to use in a project. It would need to be adopted with fresh 
development, stripped of the python2 stuff, and the object structure 
redesigned in a more uniform way with functionality properly 
containerized instead of all stuffed together...

--
https://mail.python.org/mailman/listinfo/python-list


Re: Python - working with xml/lxml/objectify/schemas, datatypes, and assignments

2023-01-15 Thread aapost

On 1/3/23 22:57, aapost wrote:
I am trying to wrap my head around how one goes about working with and 
editing xml elements ... Back to 
contemplating and tinkering..


For anyone in a similar situation, xmlschema is actually quite nice.

It didn't have the features I was looking for out of the box, but it 
does have a to_objects function and I have learned quite a bit while 
picking it apart. I am able to patch it to be good enough for my 
requirements.


Below is the patch for anyone interested:

#
# Contribution for the xmlschema & elementpath python modules which are
# Copyright (c), 2016-2020, SISSA (International School for Advanced 
Studies).

# All rights reserved.
#
# This file is distributed under the terms of the MIT License.
# See the file 'LICENSE' in the root directory of the present
# distribution, or http://opensource.org/licenses/MIT.
#

# Patching and expansion of the xmlschema.dataobjects.DataElement object 
features
# to get the best demonstration, change schema variable to your .xsd, 
and xmlobj to your .xml files

# then run this as $ python -i filename.py

from typing import Any, Optional, Union, Tuple
#from types import MethodType

class ValueLockedError(Exception):
  def __init__(self, obj, variable_name):
self.message = "Can't set ." + variable_name + \
   "\nThe object:\n" + str(obj) + \
   "\nis Locked (._locked is set to True)"
super().__init__(self.message)

# importing in order necessary for intended monkey patch
import elementpath.etree as ep_etree

# Monkey patching additional static functions to the import of 
elementpath.etree


# for namespace management of xml.etree.ElementTree code paths (which use
# the global variable register_namespace._namespace_map for namespace 
registering)

def etree_remove_registered_namespace(elem: ep_etree.ElementProtocol,
  uri: str = '') -> None:
  etree_module: Any
  if not ep_etree.is_etree_element(elem):
raise TypeError(f"{elem!r} is not an Element")
  elif isinstance(elem, ep_etree.PyElementTree.Element):
etree_module = ep_etree.PyElementTree
  elif not hasattr(elem, 'nsmap'):
etree_module = ep_etree.ElementTree
  else:
import lxml.etree as etree_module  # type: ignore[no-redef]

  if not hasattr(elem, 'nsmap'):
if uri in etree_module.register_namespace._namespace_map:
  del etree_module.register_namespace._namespace_map[uri]
  else:
# TODO research this for better understanding
# _namespace_map is uri->prefix
# DataElement.nsmap prefix->uri
# lxml etree .nsmap ?->?
# not using lxml anyway so not really an issue as
# this condition shouldn't be met
for key, value in elem.nsmap.items():
  # research - can there be multiple instances of uri to prefix?..
  # or are they intended to be 1:1?..
  if value == uri:
if key in elem.nsmap:
  del elem.nsmap[key]

#patching
setattr(ep_etree, "etree_remove_registered_namespace",
staticmethod(etree_remove_registered_namespace))

# for namespace management of xml.etree.ElementTree code paths (which use
# the global variable register_namespace._namespace_map for namespace 
registering)

def etree_get_registered_namespaces(elem: ep_etree.ElementProtocol) -> dict:
  etree_module: Any
  if not ep_etree.is_etree_element(elem):
raise TypeError(f"{elem!r} is not an Element")
  elif isinstance(elem, ep_etree.PyElementTree.Element):
etree_module = ep_etree.PyElementTree
  elif not hasattr(elem, 'nsmap'):
etree_module = ep_etree.ElementTree
  else:
import lxml.etree as etree_module  # type: ignore[no-redef]

  if not hasattr(elem, 'nsmap'):
return etree_module.register_namespace._namespace_map
  else:
return elem.nsmap # shouldn't be met

#patching
setattr(ep_etree, "etree_get_registered_namespaces",
staticmethod(etree_get_registered_namespaces))

# for namespace management of xml.etree.ElementTree code paths (which use
# the global variable register_namespace._namespace_map for namespace 
registering)

def etree_register_namespace(elem: ep_etree.ElementProtocol,
 prefix: str = None,
 uri: str = None) -> None:
  etree_module: Any
  if not ep_etree.is_etree_element(elem):
raise TypeError(f"{elem!r} is not an Element")
  elif isinstance(elem, ep_etree.PyElementTree.Element):
etree_module = ep_etree.PyElementTree
  elif not hasattr(elem, 'nsmap'):
etree_module = ep_etree.ElementTree
  else:
import lxml.etree as etree_module  # type: ignore[no-redef]

  if prefix != None and uri != None:
if not hasattr(elem, 'nsmap'):
  etree_module.register_namespace(prefix, uri)
else:
  # TODO research this for better understanding
  # _namespac

Re: Python - working with xml/lxml/objectify/schemas, datatypes, and assignments

2023-01-10 Thread aapost

On 1/4/23 12:13, aapost wrote:

On 1/4/23 09:42, Dieter Maurer wrote:

aapost wrote at 2023-1-3 22:57 -0500:

...
Consider the following:

from lxml import objectify, etree
schema = etree.XMLSchema(file="path_to_my_xsd_schema_file")
parser = objectify.makeparser(schema=schema, encoding="UTF-8")
xml_obj = objectify.parse("path_to_my_xml_file", parser=parser)
xml_root = xml_obj.getroot()

let's say I have a Version element, that is defined simply as a string
in a 3rd party provided xsd schema




Does your schema include the third party schema?

You might have a look at `PyXB`, too.
It tries hard to enforce schema restrictions in Python code.



Yes, to clarify, they provide the schema, which is what we use, 
downloaded locally. Basically just trying to remain compliant with their 
structures that they already define without reinventing the wheel for 
numerous calls and custom types, and in a way that feels more live 
rather than just checking validity at the end of the edits as if I were 
modifying the XML manually.


Thank you for the suggestion, PyXB works much more like how I envisioned 
working with xml in my head:


 >>> xml_root.Version = 1231.32000
pyxb.exceptions_.SimpleTypeValueError: Type 
{http://www.w3.org/2001/XMLSchema}string cannot be created from: 1231.32

 >>> xml_root.Version = "1231.32000"

I will have to do some more testing to see how smooth the transition 
back to a formatted document goes, since it creates a variable for all 
possible fields defined in the type, even if they are optional and not 
there in the situational template.


Thanks


Unfortunately picking it apart for a while and diving deeper in to a 
rabbit hole, PyXB looks to be a no-go.


PyXB while interesting, and I respect it's complexity and depth, is 
lacking in design consistency in how it operates if you are trying to 
modify and work with the resulting structure intuitively. It was 
developed on Python2 14 years ago, made compatible with python3 late, 
seems like it was trying to maintain vast version compatibility rather 
than getting a needed overhaul, before being abandoned in 2017 after the 
author moved on to more interesting work... I don't blame him, lol.. The 
community forks are just minor bug fixes currently.


There are no setValue()/_setValue() functions for SimpleTypes (the bulk 
of your objects) so you can't change their values directly. Assigning to 
them appears to work if they are nested inside a parent that has 
__setattr__ overloaded (as a default resulting structure does when you 
first load a document), but it is a rats nest as far as what happens 
from there. Sometimes it calls .Factory(), sometimes it goes through a 
series of __init__s, but nothing is really clear on what is or is not a 
kosher approach to managing value changes, and my attempts have failed 
so far to see if I could figure out how to encompass those paths in to a 
single _setValue() call.


Then there are ComplexTypes, with a value called _IsSimpleContent, which 
indicates whether it is a wrapper for a custom SimpleType, or something 
that does not contain SimpleType data. These DO have _setValue() 
functions IF it contains SimpleType data, where the SimpleType is stored 
in a __content member variable. Assignment on these also appears to work 
but the results aren't good, you need to use _setValue(), or you lose 
things like attributes. It would have been nicer if the structure of the 
ComplexType was called something else and wrapped all objects with a 
common set of functions.


The validate functions do not work how one would assume, like they do 
for other libraries, where they go back and verify the data. I believe 
they only function on the way in, because if the data becomes invalid 
through some manual messing with it after the fact, they indicate that 
the data is still valid even when it's not.


It seems like what I am probably looking for may reside in java with 
JAXB, but nothing really beyond that.


generateDS, doesn't really offer anything I need from what I could tell 
in messing with it, and by the looks of it, I might have to bite the 
bullet and use the xmlschema library, work with dicts, handle many more 
corner cases on my side, and just let the result be a lot clunkier than 
I was hoping. Unless I find 8 years to redesign the wheel myself, not 
sure I am granted that ability though. Oh well. lol

--
https://mail.python.org/mailman/listinfo/python-list


Re: Python - working with xml/lxml/objectify/schemas, datatypes, and assignments

2023-01-04 Thread aapost

On 1/4/23 09:42, Dieter Maurer wrote:

aapost wrote at 2023-1-3 22:57 -0500:

...
Consider the following:

from lxml import objectify, etree
schema = etree.XMLSchema(file="path_to_my_xsd_schema_file")
parser = objectify.makeparser(schema=schema, encoding="UTF-8")
xml_obj = objectify.parse("path_to_my_xml_file", parser=parser)
xml_root = xml_obj.getroot()

let's say I have a Version element, that is defined simply as a string
in a 3rd party provided xsd schema




Does your schema include the third party schema?

You might have a look at `PyXB`, too.
It tries hard to enforce schema restrictions in Python code.



Yes, to clarify, they provide the schema, which is what we use, 
downloaded locally. Basically just trying to remain compliant with their 
structures that they already define without reinventing the wheel for 
numerous calls and custom types, and in a way that feels more live 
rather than just checking validity at the end of the edits as if I were 
modifying the XML manually.


Thank you for the suggestion, PyXB works much more like how I envisioned 
working with xml in my head:


>>> xml_root.Version = 1231.32000
pyxb.exceptions_.SimpleTypeValueError: Type 
{http://www.w3.org/2001/XMLSchema}string cannot be created from: 1231.32

>>> xml_root.Version = "1231.32000"

I will have to do some more testing to see how smooth the transition 
back to a formatted document goes, since it creates a variable for all 
possible fields defined in the type, even if they are optional and not 
there in the situational template.


Thanks
--
https://mail.python.org/mailman/listinfo/python-list


Python - working with xml/lxml/objectify/schemas, datatypes, and assignments

2023-01-03 Thread aapost
I am trying to wrap my head around how one goes about working with and 
editing xml elements since it feels more complicated than it seems it 
should be.. Just to get some feedback on how others might approach it 
and see if I am missing anything obvious that I haven't discovered yet, 
since maybe I am wandering off in a wrong way of thinking..


I am looking to interact with elements directly, loaded from a template, 
editing them, then ultimately submitting them to an API as a modified 
xml document.


Consider the following:

from lxml import objectify, etree
schema = etree.XMLSchema(file="path_to_my_xsd_schema_file")
parser = objectify.makeparser(schema=schema, encoding="UTF-8")
xml_obj = objectify.parse("path_to_my_xml_file", parser=parser)
xml_root = xml_obj.getroot()

let's say I have a Version element, that is defined simply as a string 
in a 3rd party provided xsd schema




and is set to a number 2342 in my document

The xml file loads with the above code successfully against the schema

But lxml objectify decides the element type is Int, and the pytype is int..

Version 
Version.pyval 

Let's say I want this loaded into a UI with a variety of dynamically 
loaded entry widgets so I can edit a large number of values like this 
and of many other different types.


I can assign in one of two ways (both resulting the same)
xml_root.Version =
xml_root['Version'] =
(if there is some other more kosher way of assignment, let me know)

I can assign "2342" and the element suddenly becomes a 'lxml.objectify.StringElement'>


I can assign 1.4 and the element suddenly becomes a 'lxml.objectify.FloatElement'>


The schema does not check during this assignment, it could be invalid, 
like assigning "abc" to a xs:dateTime and it does so any way.
The original value is lost. The only way I see to verify against the 
schema again is to do so explicitly against the whole root.


schema.validate(xml_root)

This returns False because of the added xmlns:py, py:pytype stuff, I can 
strip those with:
objectify.deannotate(xml_root[etree.QName(xml_root.Version.tag).localname], 
cleanup_namespaces=True)


and get back to schema.validate(xml_root) validating True. BUT, it 
validates True whether the element is a String, Int, Float, etc (so long 
as it 'could' potentially be a string or something..).. So let's say a 
Version is 322.1121000, should be a string, validates against the schema 
as string, but is now 322.1121 (much more relevant for something like a 
product identification number)


If it is a case where the validate remains False, I then have to 
manually look at the error log via schema.error_log for something like this:


api_files/Basic:0:0:ERROR:SCHEMASV:SCHEMAV_CVC_DATATYPE_VALID_1_2_1: 
Element '{nsstuff}StartTime': 'asdfasdfa' is not a valid value of the 
atomic type 'xs:dateTime'.


Then I have to consider how I should reject the users input.. From a UI 
design standpoint it just seems like a lot of added steps, and redundant 
work on top of a object layer that doesn't really do anything other than 
give me a thumbs up on the way in and a thumbs up on a way out. Rather 
than interacting with an object that can say your change is schema 
approved or not from the get-go, I instead seem to have to parse 10+ 
lines of xsd and design UI interaction much more situationally and 
granularly to assert types and corner cases and preserve original values 
in duplicate structures, etc..


My original assumptions when hearing about xml features doesn't seem to 
exist from what I have found so far. Where schema should be the law, if 
my schema says something should be loaded as a string, it should be a 
string (or something close enough, definitely not an int or float), then 
attempting to assign something to it that doesn't match schema should be 
denied or throw an error. I am sure under the hood it would probably 
have performance draw backs or something.. Oh well.. Back to 
contemplating and tinkering..

--
https://mail.python.org/mailman/listinfo/python-list