Code Monkey home page Code Monkey logo

qtpyconvert's Introduction

QtPyConvert

An automatic Python Qt binding transpiler to the Qt.py abstraction layer. It aims to help in your modernization of your Python Qt code. QtPyConvert supports the following bindings out of the box:

It also has experimental support for defining your own bindings.

See customization for more information

Project Goals

Convert any code using any of the four major Qt for Python bindings into the standardized Qt.py abstraction layer.

Warn users about incompatibilities or unsupported code. (WIP)

Standardize Qt imports to maintain sanity in code comprehension.

Removing start imports and deep class/module imports

Getting Started

When using QtPyConvert, developers should be aware of any shortcomings of Qt.py or its subset of supported features.

Basically read the README in the Qt.py project and be aware of what it does and does not do.

Prerequisites

QtPyConvert reads the private values of the Qt.py project to build it's internal conversion processes. To install this run

pip install Qt.py

QtPyConvert also uses RedBaron as an alternate abstract syntax tree. Redbaron allows us to modify the source code and write it back out again, preserving all comments and formatting.

pip install redbaron

We also provide a requirements.txt file which you could install instead by...

pip install -r requirements.txt

You should also have access to any of your source bindings so that Qy.py can import them freely.

A full list of dependencies is as follows:

  • rebaron
  • baron
  • rply
  • appdirs
  • qt_py
  • argparse

Usage

$ qt_py_convert [-h] [-r] [--stdout] [--write-path WRITE_PATH] [--backup]
                [--show-lines] [--to-method-support] [--explicit-signals-flag]
                files_or_directories [files_or_directories ...]
Argument Description
-h,--help Show the help message and exit.
files_or_directories Pass explicit files or directories to run. NOTE: If "-" is passed instead of files_or_directories, QtPyConvert will attempt to read from stdin instead. Useful for pipelining proesses together
-r,--recursive Recursively search for python files to convert. Only applicable when passing a directory.
--stdout Boolean flag which will write the resulting file to stdout instead of on disk.
--write-path If provided, QtPyConvert will treat "--write-path" as a relative root and write modified files from there.
--backup Create a hidden backup of the original source code beside the newly converted file.
--show-lines Turn on printing of line numbers while replacing statements. Ends up being much slower.
--to-method-support EXPERIMENTAL: An attempt to replace all api1.0 style "toString", "toInt", "toBool", "toPyObject", "toAscii" methods that are unavailable in api2.0.
--explicit-signals-flag EXPERIMENTAL: Modification on the api1.0 style signal conversion logic. It will explicitly slice into the QtCore.Signal object to find the signal with the matching signature.
This is a fairly unknown feature of PySide/PyQt and is usually worked around by the developer. However, this should be safe to turn on whichever the case.

Customization

QtPyConvert supports some custom bindings if you are willing to do a little bit of work.

This is done through environment variables:

Key Value Description
QT_CUSTOM_BINDINGS_SUPPORT The names of custom abstraction layers or bindings separated by os.pathsep This can be used if you have code that was already doing it's own abstraction and you want to move to the Qt.py layer.
QT_CUSTOM_MISPLACED_MEMBERS This is a json dictionary that you have saved into your environment variables. This json dictionary should look similar to the Qt.py _misplaced_members dictionary but instead of mapping to Qt.py it maps the source bindings to your abstraction layer.

Note This feature is experimental and has only been used internally a few times. Support for this feature will probably be slower than support for the core functionality of QyPyConvert.

Troubleshooting

QtPyConvert is still a bit of a work in progress, there are things that it cannot yet convert with 100% certainty.
The following is a guide of common problems that you might have pop up.

During Conversion

baron.parser.ParsingError

The most common thing that you will probably see is a Baron parsing error.

Traceback (most recent call last):
  File "/usr/bin/qt_py_convert", line 47, in <module>
    main(args.file_or_directory, args.recursive, args.write)
  File "/usr/bin/qt_py_convert", line 40, in main
    process_folder(path, recursive=recursive, write=write)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 310, in process_folder
    process_folder(os.path.join(folder, fn), recursive, write, fast_exit)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 310, in process_folder
    process_folder(os.path.join(folder, fn), recursive, write, fast_exit)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 310, in process_folder
    process_folder(os.path.join(folder, fn), recursive, write, fast_exit)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 310, in process_folder
    process_folder(os.path.join(folder, fn), recursive, write, fast_exit)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 302, in process_folder
    os.path.join(folder, fn), write=write, fast_exit=fast_exit
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 282, in process_file
    aliases, mappings, modified_code = run(source, fast_exit=fast_exit)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/run.py", line 264, in run
    psep0101.process(red)
  File "/usr/lib/python2.7/site-packages/qt_py_convert/_modules/psep0101/process.py", line 215, in process
    getattr(Processes, issue)(red, psep_issues[issue]) if psep_issues[issue] else None
  File "/usr/lib/python2.7/site-packages/qt_py_convert/_modules/psep0101/process.py", line 34, in _process_qvariant
    node.parent.replace(changed)
  File "/usr/lib/python2.7/site-packages/redbaron/base_nodes.py", line 1016, in replace
    new_node = self._convert_input_to_node_object(new_node, parent=None, on_attribute=None, generic=True)
  File "/usr/lib/python2.7/site-packages/redbaron/base_nodes.py", line 156, in _convert_input_to_node_object
    return Node.from_fst(baron.parse(value)[0], parent=parent, on_attribute=on_attribute)
  File "/usr/lib/python2.7/site-packages/baron/baron.py", line 57, in parse
    to_return = _parse(tokens, print_function)
  File "/usr/lib/python2.7/site-packages/baron/baron.py", line 26, in _parse
    raise e
baron.parser.ParsingError: Error, got an unexpected token RIGHT_SQUARE_BRACKET here:
   1 self.user_data[index.row(]<---- here

The token RIGHT_SQUARE_BRACKET should be one of those: BACKQUOTE, BINARY, BINARY_RAW_STRING, BINARY_STRING, COMMA, COMPLEX, DOUBLE_STAR, FLOAT, FLOAT_EXPONANT, FLOAT_EXPONANT_COMPLEX, HEXA, INT, LAMBDA, LEFT_BRACKET, LEFT_PARENTHESIS, LEFT_SQUARE_BRACKET, LONG, MINUS, NAME, NOT, OCTA, PLUS, RAW_STRING, RIGHT_PARENTHESIS, STAR, STRING, TILDE, UNICODE_RAW_STRING, UNICODE_STRING

It is not normal that you see this error, it means that Baron has failed to parse valid Python code. It would be kind if you can extract the snippet of your code that makes Baron fail and open a bug here: https://github.com/Psycojoker/baron/issues

Sorry for the inconvenience.

Because we are using an alternate Python AST, there are sometimes issues on edge cases of code. Unfortunately, Baron get's confused sometimes when it tries to send us a helpful traceback and the error is usually earlier than it thinks.
There are two usual suspects when getting a Baron ParsingError.

Incorrectly indented comments

This is a problem that will popup with Baron because it actually pays attention to the comments, whereas Python just throws them out. If you are getting an error similar to the above, you will want to look higher in the script for incorrectly indented comments, multiline and single line ones.

Bare print statements

This is less common than the indented comments issue but has still shown up in a few cases for us internally.
There are times where Baron cannot parse a print statement and turning it into a print function by enclosing it in parenthesis seems to fix it.

Qt.py does not support uic.loadUiType

The Qt.py module does not support uic.loadUiType.
Please see mottosso/Qt.py#237

During Runtime

QLayout.setMargin

As of Qt 4.7 QLayout.setMargin has been set to obsolete which means that it will be removed.
As it turns out, they removed it in Qt5. The obsoleted page is here

Obsoleted code

layout = QLayout()
layout.setMargin(10)

Replacement code

layout = QLayout()
layout.setContentsMargins(10, 10, 10, 10)

Future Features

There are several things that we would love to have support for in QtPyConvert but we just haven't gotten around to yet for various reasons.

  • Warnings about deprecated/obsoleted method calls in Qt4 code.
    • There were many method calls that were removed in Qt5 and most people were unaware that they should be using something else when they wrote the Qt4 code.
      There are lists on the Qt5 docs about what was deprecated and what the replacements are (if any) in Qt5 code. It would be nice if we could warn the user about these when we detect them and potentially automatically fix some of them too.
    • In a perfect world we would be able to dynamically build the list of these by scraping the Qt website too.
  • Better support for api-v1.0 method removal. Currently we are basially looking for the method names because they are quite unique. Ideally it'd be great if we could somehow record the type of the object at least in locals and then if that object was a QString for example, see if it is using any methods that aren't on a builtin string type.
  • Better support for the QtCompat..method replacements. Much of this would require the previous feature to be somewhat figured out. We would need to at least minimally track variable types.

Project Information

Built With

  • Qt.py - The Qt abstraction library that we port code to.
  • RedBaron - The alternate Python AST which allows us to modify and preserve comments + formatting.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use semantic versioning for versioning. For the versions available, see the tags on this repository.

Authors

See also the list of contributors who participated in this project.

License

This project is licensed under a modified Apache 2.0 license - see the LICENSE file for details

Acknowledgments

qtpyconvert's People

Contributors

ahuge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qtpyconvert's Issues

Conversion appends a new line which is always unix style

When converting a file the tool seems to append a new line to the end of the file under certain conditions - Not sure what conditions.

When it appends a new line it seems to always append a "\n" unix style new line. Even when the rest of the file may have different line endings.

  1. Does the tool have to append a newline? If so.
  2. Can the output file have consistent line endings that ideally match the sources line endings?

Before source:

from PySide import QtGui
if __name__ == '__main__':
    QtGui.QApplication([])

Before source as Hex:

00000010: 7274 2051 7447 7569 0d0a 6966 205f 5f6e  rt QtGui..if __n
00000020: 616d 655f 5f20 3d3d 2027 5f5f 6d61 696e  ame__ == '__main
00000030: 5f5f 273a 0d0a 2020 2020 5174 4775 692e  __':..    QtGui.
00000040: 5141 7070 6c69 6361 7469 6f6e 285b 5d29  QApplication([])

Source after:

from Qt import QtWidgets
if __name__ == '__main__':
    QtWidgets.QApplication([])

Source after as Hex:

00000010: 7457 6964 6765 7473 0d0a 6966 205f 5f6e  tWidgets..if __n
00000020: 616d 655f 5f20 3d3d 2027 5f5f 6d61 696e  ame__ == '__main
00000030: 5f5f 273a 0d0a 2020 2020 5174 5769 6467  __':..    QtWidg
00000040: 6574 732e 5141 7070 6c69 6361 7469 6f6e  ets.QApplication
00000050: 285b 5d29 0a                             ([]).

Note the 0a \n newline at the end of the file even though the file previously contained 0d0a \r\n windows style newlines.

Branding

Hey @Ahuge,

About the name, considering there is another project with a very similar name, the difference being letter case and a missing dot - Qt.py versus qtpy - do you think it would make sense to call this project Qt.py Convert, or the like? Granted Qt.py is a tricky name to add suffixes too..

Some ideas.

  • Qt.conv
  • Qt.all
  • Qt.pyconvert
  • ConvertQt.py
  • convert.Qt.py
  • QtConvert.py
  • convert-to-Qt.py

Not confident either of these are better, just thought I'd bring it up for discussion before any official announcements of the project (at which point it'd be a shame to change it!)

Any Binding -> PySide2

I had a thought about how to increase the use, adoption and value of QtPyConvert, which in turn can help improve contribution and increase the number of eyeballs on the project.

For background, I think conversion from one binding to another is an interesting concept to many people, VFX artists and app developer alike. But the fact that they get to the README of this project and read about how it takes their precious code and converts it to a third-party, relatively unknown library such as Qt.py I think can be a little discouraging. I think most people, especially those unfamiliar with Qt.py, would be able to appreciate it further had it converted to something more "official" and known, like PySide2.

PySide2 is a stone's throw from Qt.py, so once it's there it would be a matter of search-and-replace of a few keywords (in fact, just PySide2 -> Qt). That part is actually already built into Qt.py,

python -m Qt --convert my_pyside2_project.py

I think the end result could be:

  1. People who aren't interested in Qt.py could still be interested in QtPyConvert
  2. Both those who are and aren't would benefit from a conversion to a binding they could later Google about and find answers for and potentially have pre-existing knowledge about as they fix any kinks coming out of the conversion.
  3. Finally, as a third-step of the process, if they wanted cross-compatibility, they could convert to Qt.py

You could call it PySide2Convert. :)

Just a thought!

Conversion fails when there's trailing white space in an indentation block

The conversion fails with the following error when you have trailing whitespace inside of an indentation block.

2018-05-08 12:24:04 - ERROR | [QtPyConvert.run]
Line 1
from PySide import QtGui
Traceback (most recent call last):
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\qt_py_convert\run.py", line 489, in run
    red = redbaron.RedBaron(text)
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\redbaron\redbaron.py", line 36, in __init__
    self.node_list = base_nodes.NodeList.from_fst(baron.parse(source_code), parent=self, on_attribute="root")
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\baron\baron.py", line 49, in parse
    tokens = tokenize(source_code, False)
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\baron\baron.py", line 70, in tokenize
    return mark_indentation(inner_group(space_group(_tokenize(group(split(pouet)), print_function))))
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\baron\indentation_marker.py", line 24, in mark_indentation
    return list(mark_indentation_generator(sequence))
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\baron\indentation_marker.py", line 85, in mark_indentation_generator
    the_indentation_level_changed = get_space(current) is None or get_space(current) != indentations[-1]
  File "Q:\Tools\eng\bjarrett\qtpyconvert\venv\lib\site-packages\baron\indentation_marker.py", line 38, in get_space
    if len(node) < 3 or len(node[3]) == 0:
IndexError: tuple index out of range

The source that causes the error:

from PySide import QtGui
if True:
    app = QtGui.QApplication([]) 

Note there is a trailing space after the QApplication constructor. Here's the hex for clarity

00000000: 6672 6f6d 2050 7953 6964 6520 696d 706f  from PySide impo
00000010: 7274 2051 7447 7569 0a69 6620 5472 7565  rt QtGui.if True
00000020: 3a0a 2020 2020 6170 7020 3d20 5174 4775  :.    app = QtGu
00000030: 692e 5141 7070 6c69 6361 7469 6f6e 285b  i.QApplication([
00000040: 5d29 200a                                ]) .

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.