Updates to support Python version 3 #7

Merged
SlavekB merged 1 commits from python3-support into master 2 years ago
Collaborator

Amendments to the sip source and configuration/build scripts to allow
for support under Python version 3. The examples have been updated
using "2to3" along with some manual changes to sort out intentation
and casting to integer from float.

Signed-off-by: aneejit1 aneejit1@gmail.com

Amendments to the sip source and configuration/build scripts to allow for support under Python version 3. The examples have been updated using "2to3" along with some manual changes to sort out intentation and casting to integer from float. Signed-off-by: aneejit1 <aneejit1@gmail.com>
aneejit1 added 1 commit 2 years ago
6677b94427
Updates to support Python version 3
Poster
Collaborator

Well, this is fun. I appear to have been through Unicode hell!

Most of the examples are now running OK. The API hasn't been kept in line with what is going on with TQt so there are methods and classes that have gone missing causing the examples to fail. I suspect there's a substantial bit of work going to be needed to get them to fall in line again.

WRT the API, there's a lot of old crud hanging around relating to previous releases of Qt/TQt. I've stripped this out of qapplication.sip and qstring.sip along with Python v1 checks. It seemed rather pointless keeping this stuff hanging around given that python-tqt's versioning is advancing at the same rate as for TQt so it's highly unlikely that anyone would be building R14.0.11 python-tqt against R14.0.8 TQt, for instance.

There's an issue with integers and floats between Python 2 and 3. Python 2 seems to resolve calculations down to integers, however, Python 3 tends to store them as float. This causes issues with the bits of the TQt API that have ints as parameters. I've had to mod several of the scripts to put in "int()" calls around things to get them to work.

The missing API issues (occurring under 2.7 and 3.10) are:

  • examples2/addressbook.py: Iteration error on TQListView in the search tab
  • examples2/desktop.py: TQDesktopWidget object has no attribute setBackgroundPixmap
  • examples2/dragdrop.py: SecretSource object has no attribute setBackgroundColor
  • examples2/semaphore.py: TQLabel object has no attribute setBackgroundColor when button is pushed
  • examples2/splitter.py: Test object has no attribute setBackgroundColor
  • examples2/table.py: TQTableView is not defined
  • examples2/themes.py: TQPaintDevice: Must construct a TQApplication before a TQPaintDevice
  • examples2/widgets.py: Type object TQApplication has no attribute setWinStyleHighlightColor - color list
  • examples3/addressbook.py: Iteration error on TQListView in the search
  • examples3/biff.py: Runs, but "Biff object has no attribute mailbox" if clicked.

The following are failing under Python 3 but appear to be working fine in Python 2:

  • examples2/application.py: Runtime Error: underlying C/C++ object has been deleted
  • examples3/application.py: Runtime Error: underlying C/C++ object has been deleted
  • examples3/fonts.py: str related errors
  • examples3/checklists.py: name "apply" is not defined
  • examples3/cursor.py: Casting issue
  • examples3/dragdrop.py: float + str problems

These are the ones with Python 2 and 3 troubles:

  • examples3/mdi.py: gives "list object has no attribute at" under Python 2 and "Runtime Error: underlying C/C++ object has been deleted" with Python 3
  • examples3/qmag.py: Runs under Python 2, but not sure if working and gives "Bytes expected not str" under Python 3
  • examples3/tablestatistics.py: gives "NoneType object has no attribute endswith" in Python 2 and "Fails on: expected str, bytes, or os.PathLike object not NoneType" for Python 3
  • examples3/tooltip.py: qt.TQToolTip represents a C++ abstract class and cannot be instantiated

I've not managed to make much headway with the "underlying C/C++ object has been deleted" messages. Something is going on internally that might be a difference between the two Python versions, but I've yet to determine which object it's actually referring to; the messages are not particularly helpful on the matter!

On the sip side of things, I suspect that the examples only really exercise a small proportion of the functionality and that there are other issues lying in wait. This is hard to tell without going through all 280 .sip files.

Well, this is fun. I appear to have been through Unicode hell! Most of the examples are now running OK. The API hasn't been kept in line with what is going on with TQt so there are methods and classes that have gone missing causing the examples to fail. I suspect there's a substantial bit of work going to be needed to get them to fall in line again. WRT the API, there's a lot of old crud hanging around relating to previous releases of Qt/TQt. I've stripped this out of qapplication.sip and qstring.sip along with Python v1 checks. It seemed rather pointless keeping this stuff hanging around given that python-tqt's versioning is advancing at the same rate as for TQt so it's highly unlikely that anyone would be building R14.0.11 python-tqt against R14.0.8 TQt, for instance. There's an issue with integers and floats between Python 2 and 3. Python 2 seems to resolve calculations down to integers, however, Python 3 tends to store them as float. This causes issues with the bits of the TQt API that have ints as parameters. I've had to mod several of the scripts to put in "int()" calls around things to get them to work. The missing API issues (occurring under 2.7 and 3.10) are: - examples2/addressbook.py: Iteration error on TQListView in the search tab - ~~examples2/desktop.py: TQDesktopWidget object has no attribute setBackgroundPixmap~~ - ~~examples2/dragdrop.py: SecretSource object has no attribute setBackgroundColor~~ - ~~examples2/semaphore.py: TQLabel object has no attribute setBackgroundColor when button is pushed~~ - ~~examples2/splitter.py: Test object has no attribute setBackgroundColor~~ - examples2/table.py: TQTableView is not defined - examples2/themes.py: TQPaintDevice: Must construct a TQApplication before a TQPaintDevice - examples2/widgets.py: Type object TQApplication has no attribute setWinStyleHighlightColor - color list - examples3/addressbook.py: Iteration error on TQListView in the search - examples3/biff.py: Runs, but "Biff object has no attribute mailbox" if clicked. The following are failing under Python 3 but appear to be working fine in Python 2: - ~~examples2/application.py: Runtime Error: underlying C/C++ object has been deleted~~ - ~~examples3/application.py: Runtime Error: underlying C/C++ object has been deleted~~ - ~~examples3/fonts.py: str related errors~~ - ~~examples3/checklists.py: name "apply" is not defined~~ - ~~examples3/cursor.py: Casting issue~~ - ~~examples3/dragdrop.py: float + str problems~~ These are the ones with Python 2 and 3 troubles: - ~~examples3/mdi.py: gives "list object has no attribute at" under Python 2 and "Runtime Error: underlying C/C++ object has been deleted" with Python 3~~ - ~~examples3/qmag.py: Runs under Python 2, but not sure if working and gives "Bytes expected not str" under Python 3~~ - ~~examples3/tablestatistics.py: gives "NoneType object has no attribute endswith" in Python 2 and "Fails on: expected str, bytes, or os.PathLike object not NoneType" for Python 3~~ - examples3/tooltip.py: qt.TQToolTip represents a C++ abstract class and cannot be instantiated I've not managed to make much headway with the "underlying C/C++ object has been deleted" messages. Something is going on internally that might be a difference between the two Python versions, but I've yet to determine which object it's actually referring to; the messages are not particularly helpful on the matter! On the sip side of things, I suspect that the examples only really exercise a small proportion of the functionality and that there are other issues lying in wait. This is hard to tell without going through all 280 .sip files.
Owner

The API hasn't been kept in line with what is going on with TQt so there are methods and classes that have gone missing causing the examples to fail.

That is quite possible. In fact one of the things that would be good to do is to find a way to regenerate all .sip files automatically, to be in line with the api all the time.

> The API hasn't been kept in line with what is going on with TQt so there are methods and classes that have gone missing causing the examples to fail. That is quite possible. In fact one of the things that would be good to do is to find a way to regenerate all .sip files automatically, to be in line with the api all the time.
aneejit1 force-pushed python3-support from 6677b94427 to 57f069ed97 2 years ago
aneejit1 force-pushed python3-support from 57f069ed97 to 4aa051b882 2 years ago
Poster
Collaborator

I hate Debian.

I SO hate Debian.

It's taken me ages to do this, and there has been much swearing, but I've finally gotten to the "non-trivial designated initializers" error. I changed the initialisation of "pyqtWrapper_Type" but it turns out that GCC 6 does not support it. I'm going to have to put it back the way it was and work around the problem that caused me to change it in the first place.

I hate Debian. I SO hate Debian. It's taken me ages to do this, and there has been much swearing, but I've finally gotten to the "non-trivial designated initializers" error. I changed the initialisation of "pyqtWrapper_Type" but it turns out that GCC 6 does not support it. I'm going to have to put it back the way it was and work around the problem that caused me to change it in the first place.
aneejit1 force-pushed python3-support from 4aa051b882 to 74e7331475 2 years ago
aneejit1 force-pushed python3-support from 74e7331475 to b9f5dadf75 2 years ago
Owner

I hate Debian.
I SO hate Debian.

You clearly need to use Debian a bit longer. You will end up loving it :-)

> I hate Debian. > I SO hate Debian. You clearly need to use Debian a bit longer. You will end up loving it :-)
Collaborator

I hate Debian.
I SO hate Debian.

Or come to the dark side and install Slackware ;-)

> I hate Debian. > I SO hate Debian. Or come to the dark side and install Slackware ;-)
Owner

I hate Debian.
I SO hate Debian.

Or come to the dark side and install Slackware ;-)

Then you will really LOVE Debian and how well and easy it manages dependencies 😉

> > I hate Debian. > > I SO hate Debian. > > Or come to the dark side and install Slackware ;-) Then you will really LOVE Debian and how well and easy it manages dependencies :wink:
Poster
Collaborator

Then you will really LOVE Debian and how well and easy it manages dependencies 😉

The dependencies were the trouble. So little of what I needed was actually installed by default and I had a hell of a job finding some of the bits that I needed in the way of headers.

...and my lovely build system wouldn't work with it meaning I had to do everything manually! :-(

> Then you will really LOVE Debian and how well and easy it manages dependencies :wink: The dependencies were the trouble. So little of what I needed was actually installed by default and I had a hell of a job finding some of the bits that I needed in the way of headers. ...and my lovely build system wouldn't work with it meaning I had to do everything manually! :-(
Poster
Collaborator

Or come to the dark side and install Slackware ;-)

That's where I started back in around 1993!

> Or come to the dark side and install Slackware ;-) That's where I started back in around 1993!
Poster
Collaborator

So, it looks like I've fixed the issue with the "non-trivial designated initializers" error. I've set it back to using positional initialisers which means that there are a number of preprocessor conditions as this struct has changed in transition from Python 2 to 3, and has been amended a few times since.

What has how been thrown up is an issue with "PyInit_qt" or "initqt" not being defined. There's a version script that is supposed to localise everything except these declarations, but it's not working (at least not unless you override the compiler parameters... oops!). Everything is currently defined as visibility "hidden". Although the "qt.exp" file says make it global, hidden forces it back to local again. I can get around this by removing the "-fvisibility=hidden" option, but that makes everything global and I'd rather not do that.

I've got a solution, but it means going back to sip4-tqt as the code in question is generated. There's also a question of what to support. gcc 4 and above supports visibility, so does clang. The question is, which other compilers that might be used should I check for in order to enable changing the visibility?

So, it looks like I've fixed the issue with the "non-trivial designated initializers" error. I've set it back to using positional initialisers which means that there are a number of preprocessor conditions as this struct has changed in transition from Python 2 to 3, and has been amended a few times since. What has how been thrown up is an issue with "PyInit_qt" or "initqt" not being defined. There's a version script that is supposed to localise everything except these declarations, but it's not working (at least not unless you override the compiler parameters... oops!). Everything is currently defined as visibility "hidden". Although the "qt.exp" file says make it global, hidden forces it back to local again. I can get around this by removing the "-fvisibility=hidden" option, but that makes everything global and I'd rather not do that. I've got a solution, but it means going back to sip4-tqt as the code in question is generated. There's also a question of what to support. gcc 4 and above supports visibility, so does clang. The question is, which other compilers that might be used should I check for in order to enable changing the visibility?
Owner

The dependencies were the trouble. So little of what I needed was actually installed by default and I had a hell of a job finding some of the bits that I needed in the way of headers.

Well, that's very surprising. Debian usually resolves the required dependencies for you when installing packages. And if you are developing, the debian/control file will tell you all the required dependencies for a particular package.

> The dependencies were the trouble. So little of what I needed was actually installed by default and I had a hell of a job finding some of the bits that I needed in the way of headers. Well, that's very surprising. Debian usually resolves the required dependencies for you when installing packages. And if you are developing, the debian/control file will tell you all the required dependencies for a particular package.
aneejit1 force-pushed python3-support from b9f5dadf75 to 19e732d3ff 2 years ago
aneejit1 force-pushed python3-support from 19e732d3ff to 11dfe910d4 2 years ago
aneejit1 force-pushed python3-support from 11dfe910d4 to 0541671ecd 2 years ago
Poster
Collaborator

Finally managed to work out the cause of the "underlying C/C++ object has been deleted" exception that was being raised.

Where a slot function was being specified, a call to "sipGetCppPtr" was being invoked which was then going off and calling "checkPointer". This raises an exception if the CppPtr is NULL. The problem is, the exception only seems to matter if running Python 3. Yes, Python 2 calls the same function to raise the exception, but it then goes and ignores it resulting in the example running where Python 3 would accept the exception and fail. I've not been able to work out why Python 2 ignores it, but given that the scripts work under v2 and not v3 it seems that the exception is not important.

The solution is a mod to "sipTQtCreateUniversalSlot" in "qobject.sip" which goes and checks if the CppPtr is NULL and skips the call to "sipGetCppPtr" if it is. This prevents the exception being raised in the first place allowing the code to run with both versions of Python.

The check clearly does not seem to matter for a slot-type parameter as the affected Python scripts are running fine and don't seem to care that the CppPtr is NULL. It may be that it's a stupid check for that particular type and it shouldn't have been doing it in that situation in the first place.

Finally managed to work out the cause of the "underlying C/C++ object has been deleted" exception that was being raised. Where a slot function was being specified, a call to "sipGetCppPtr" was being invoked which was then going off and calling "checkPointer". This raises an exception if the CppPtr is NULL. The problem is, the exception only seems to matter if running Python 3. Yes, Python 2 calls the same function to raise the exception, but it then goes and ignores it resulting in the example running where Python 3 would accept the exception and fail. I've not been able to work out why Python 2 ignores it, but given that the scripts work under v2 and not v3 it seems that the exception is not important. The solution is a mod to "sipTQtCreateUniversalSlot" in "qobject.sip" which goes and checks if the CppPtr is NULL and skips the call to "sipGetCppPtr" if it is. This prevents the exception being raised in the first place allowing the code to run with both versions of Python. The check clearly does not seem to matter for a slot-type parameter as the affected Python scripts are running fine and don't seem to care that the CppPtr is NULL. It may be that it's a stupid check for that particular type and it shouldn't have been doing it in that situation in the first place.
Owner

@aneejit1 is this PR ready for review or more worked is planned on it?

@aneejit1 is this PR ready for review or more worked is planned on it?
Poster
Collaborator

I've got most of the non-API change failures sorted and decided to have a go at the API-related ones. Unfortunately, I managed to trigger another problem connected to "TQByteArray" which I've still got to sort out. I'll go through the other API-related ones and (where possible) see if there are any other issues that might be present. I've also got to properly integrate some new functions added as they're currently only tacked on in not quite the right way.

Basically, there's still a few things to be done.

I've got most of the non-API change failures sorted and decided to have a go at the API-related ones. Unfortunately, I managed to trigger another problem connected to "TQByteArray" which I've still got to sort out. I'll go through the other API-related ones and (where possible) see if there are any other issues that might be present. I've also got to properly integrate some new functions added as they're currently only tacked on in not quite the right way. Basically, there's still a few things to be done.
Owner

Ok, thanks for the update. We will wait for you to tell us when this is ready.

Ok, thanks for the update. We will wait for you to tell us when this is ready.
aneejit1 force-pushed python3-support from 0541671ecd to 41715746be 2 years ago
aneejit1 force-pushed python3-support from 41715746be to fb8cdb8a3b 2 years ago
aneejit1 force-pushed python3-support from fb8cdb8a3b to 581fc5090d 2 years ago
Poster
Collaborator

OK, I think I'm there. The examples that still don't run are:

  • examples2/addressbook.py: Iteration error on TQListView in the search tab
  • examples2/table.py: TQTableView is not defined
  • examples2/themes.py: TQPaintDevice: Must construct a TQApplication before a TQPaintDevice
  • examples2/widgets.py: Type object TQApplication has no attribute setWinStyleHighlightColor - color list
  • examples3/addressbook.py: Iteration error on TQListView in the search
  • examples3/biff.py: Runs, but "Biff object has no attribute mailbox" if clicked.
  • examples3/tooltip.py: qt.TQToolTip represents a C++ abstract class and cannot be instantiated

These seem to be API-related issues where the API has advanced but the examples haven't.

I did have a bash at trying to fix the "Iteration error on TQListView" problems that occur in the "search" tab on both of the "application.py" examples, but could not come up with a solution that worked either through altering the example or the module's code. I'm not entirely sure how the iterator class should be tied to Python and unfortunately PyQt4 has moved on sufficiently that I couldn't get any helpful hints from it.

All the code changes have been uploaded and I finally got around to doing the rebase, so if you'd care to have a look and let me know if there are any problems.

OK, I think I'm there. The examples that still don't run are: - examples2/addressbook.py: Iteration error on TQListView in the search tab - examples2/table.py: TQTableView is not defined - examples2/themes.py: TQPaintDevice: Must construct a TQApplication before a TQPaintDevice - examples2/widgets.py: Type object TQApplication has no attribute setWinStyleHighlightColor - color list - examples3/addressbook.py: Iteration error on TQListView in the search - examples3/biff.py: Runs, but "Biff object has no attribute mailbox" if clicked. - examples3/tooltip.py: qt.TQToolTip represents a C++ abstract class and cannot be instantiated These seem to be API-related issues where the API has advanced but the examples haven't. I did have a bash at trying to fix the "Iteration error on TQListView" problems that occur in the "search" tab on both of the "application.py" examples, but could not come up with a solution that worked either through altering the example or the module's code. I'm not entirely sure how the iterator class should be tied to Python and unfortunately PyQt4 has moved on sufficiently that I couldn't get any helpful hints from it. All the code changes have been uploaded and I finally got around to doing the rebase, so if you'd care to have a look and let me know if there are any problems.
Owner

Does it make sense to keep the files in the "examples2" folder? They are for Qt2, and most of them are duplicated into "examples3" anyway. I think we could simply remove that folder as a whole.
What do you think?

Does it make sense to keep the files in the "examples2" folder? They are for Qt2, and most of them are duplicated into "examples3" anyway. I think we could simply remove that folder as a whole. What do you think?
Owner

@SlavekB this PR required python3 to build, so we will need to adjust debian files too for it.

@SlavekB this PR required python3 to build, so we will need to adjust debian files too for it.
Owner

@SlavekB this PR required python3 to build, so we will need to adjust debian files too for it.

Yes, I already have a locally initial state of modifications debian packaging for building with python3. Once it can be built successfully, I will create a PR for it.

> @SlavekB this PR required python3 to build, so we will need to adjust debian files too for it. Yes, I already have a locally initial state of modifications debian packaging for building with python3. Once it can be built successfully, I will create a PR for it.
Owner

Yes, I already have a locally initial state of modifications debian packaging for building with python3. Once it can be built successfully, I will create a PR for it.

Ok, perfect.

> Yes, I already have a locally initial state of modifications debian packaging for building with python3. Once it can be built successfully, I will create a PR for it. Ok, perfect.
Poster
Collaborator

Does it make sense to keep the files in the "examples2" folder? They are for Qt2, and most of them are duplicated into "examples3" anyway. I think we could simply remove that folder as a whole.
What do you think?

Oh, so that's the reason there are two of them! It's certainly something that can be done, although it might be best to do it as part of a general cleanup of the Qt/TQt version-conditional bits of the .sip files.

> Does it make sense to keep the files in the "examples2" folder? They are for Qt2, and most of them are duplicated into "examples3" anyway. I think we could simply remove that folder as a whole. > What do you think? Oh, so that's the reason there are two of them! It's certainly something that can be done, although it might be best to do it as part of a general cleanup of the Qt/TQt version-conditional bits of the .sip files.
Owner

Oh, so that's the reason there are two of them!

Well, that's what they say in the README is each folder 😉
Same goes for pyuic2 and pyuic3.

as part of a general cleanup of the Qt/TQt version-conditional bits of the .sip files.

The cleanup is good to do. Dropping examples and files for Qt2 could be done anytime IMO, since TQt3 is only based on Qt3 and try to run code for Qt2 would possibly not work...

> Oh, so that's the reason there are two of them! Well, that's what they say in the README is each folder :wink: Same goes for pyuic2 and pyuic3. > as part of a general cleanup of the Qt/TQt version-conditional bits of the .sip files. The cleanup is good to do. Dropping examples and files for Qt2 could be done anytime IMO, since TQt3 is only based on Qt3 and try to run code for Qt2 would possibly not work...
Poster
Collaborator

Dropping examples and files for Qt2 could be done anytime IMO

This is quite true, I'd just rather not do it here; keep this change just for the Python 3 port. Given that the next thing I was going to do is clean up the .sip files, you'd not have to wait long!

> Dropping examples and files for Qt2 could be done anytime IMO This is quite true, I'd just rather not do it here; keep this change just for the Python 3 port. Given that the next thing I was going to do is clean up the .sip files, you'd not have to wait long!
Owner

Dropping examples and files for Qt2 could be done anytime IMO

This is quite true, I'd just rather not do it here; keep this change just for the Python 3 port. Given that the next thing I was going to do is clean up the .sip files, you'd not have to wait long!

sure, not a problem. Just need to wait for @SlavekB to review and test on old distros I guess :-)

> > Dropping examples and files for Qt2 could be done anytime IMO > > This is quite true, I'd just rather not do it here; keep this change just for the Python 3 port. Given that the next thing I was going to do is clean up the .sip files, you'd not have to wait long! sure, not a problem. Just need to wait for @SlavekB to review and test on old distros I guess :-)
SlavekB reviewed 2 years ago
SlavekB left a comment
Owner

I did a test build on several distributions and it was successful.
There is only one minor note below.

I did a test build on several distributions and it was successful. There is only one minor note below.
configure.py Outdated
while 1:
try:
resp = raw_input("Do you accept the terms of the license? ")
resp = input("Do you accept the terms of the license? ")
Owner

Since input(...) didn't work well for me, in my earlier patch I had ready:

    while 1:
        sys.stdout.write("Do you accept the terms of the license? ")
        sys.stdout.flush()

        try:
            resp = sys.stdin.readline()
        except KeyboardInterrupt:
            raise SystemExit
        except:
            resp = ""

        resp = resp.strip().lower()

        if resp == "yes":
Since `input(...)` didn't work well for me, in my earlier patch I had ready: ``` while 1: sys.stdout.write("Do you accept the terms of the license? ") sys.stdout.flush() try: resp = sys.stdin.readline() except KeyboardInterrupt: raise SystemExit except: resp = "" resp = resp.strip().lower() if resp == "yes": ```
SlavekB reviewed 2 years ago
configure.py Outdated
# Check the licenses are compatible.
check_license()
if opt_accept_license == 1:
Owner

Second note: You need to add global opt_accept_license inside the main function (for example next do global opt_libpython) so that it works even if the -z argument is not used.

Second note: You need to add `global opt_accept_license` inside the `main` function (for example next do `global opt_libpython`) so that it works even if the `-z` argument is not used.
SlavekB added 1 commit 2 years ago
ab0c1c2bcf
Fix a query for the acceptance of the license
Owner

I've added a commit that fixes the issues with the license acceptance query as mentioned in the comments above. If you agree with the solution, I'll make squash commits because there's no important reason to keep it as a separate commit.

I've added a commit that fixes the issues with the license acceptance query as mentioned in the comments above. If you agree with the solution, I'll make squash commits because there's no important reason to keep it as a separate commit.
Poster
Collaborator

Hi.

Yes, that looks fine.

The "input" problem was a product of running "2to3". I was going to fix it by doing this:

try: input = raw_input except NameError: pass

but both ways will work!

Hi. Yes, that looks fine. The "input" problem was a product of running "2to3". I was going to fix it by doing this: `` try: input = raw_input except NameError: pass `` but both ways will work!
aneejit1 changed title from WIP: Updates to support Python version 3 to Updates to support Python version 3 2 years ago
SlavekB force-pushed python3-support from ab0c1c2bcf to 6be0466422 2 years ago
Owner

Thank you. I left the prepared solution and squashed commits together.
Thanks for your excellent effort!

Thank you. I left the prepared solution and squashed commits together. Thanks for your excellent effort!
SlavekB approved these changes 2 years ago
SlavekB left a comment
Owner

It looks good – probably nothing prevents merge.

It looks good – probably nothing prevents merge.
SlavekB merged commit 6be0466422 into master 2 years ago
SlavekB deleted branch python3-support 2 years ago
SlavekB added this to the R14.0.13 release milestone 2 years ago

Reviewers

SlavekB approved these changes 2 years ago
The pull request has been merged as 6be0466422.
Sign in to join this conversation.
No reviewers
No Milestone
No Assignees
4 Participants
Notifications
Due Date

No due date set.

Dependencies

No dependencies set.

Reference: TDE/pytqt#7
Loading…
There is no content yet.