├── src
└── rez_manager
│ ├── __init__.py
│ ├── __main__.py
│ ├── utils.py
│ ├── textedithandler.py
│ ├── window.py
│ ├── models.py
│ └── views.py
├── images
├── GUI.png
├── open.gif
├── spreadsheet.gif
├── localization.gif
└── delete-package.gif
├── tests
├── data
│ ├── local
│ │ ├── pkg_a
│ │ │ └── 0.1.0
│ │ │ │ └── package.py
│ │ └── pkg_c
│ │ │ ├── 0.1.0
│ │ │ └── package.py
│ │ │ └── 0.2.0
│ │ │ └── package.py
│ └── remote
│ │ ├── pkg_a
│ │ └── 0.2.0
│ │ │ └── package.py
│ │ └── pkg_b
│ │ └── 0.1.0
│ │ └── package.py
└── test_main.py
├── resources
└── icon.png
├── package.py
├── README.md
├── LICENSE
└── vendors
└── Qt.py
/src/rez_manager/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/GUI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuckon/rez-manager/HEAD/images/GUI.png
--------------------------------------------------------------------------------
/tests/data/local/pkg_a/0.1.0/package.py:
--------------------------------------------------------------------------------
1 | name = 'pkg_a'
2 | version = '0.1.0'
3 |
--------------------------------------------------------------------------------
/tests/data/local/pkg_c/0.1.0/package.py:
--------------------------------------------------------------------------------
1 | name = 'pkg_c'
2 | version = '0.1.0'
3 |
--------------------------------------------------------------------------------
/tests/data/local/pkg_c/0.2.0/package.py:
--------------------------------------------------------------------------------
1 | name = 'pkg_c'
2 | version = '0.2.0'
3 |
--------------------------------------------------------------------------------
/tests/data/remote/pkg_a/0.2.0/package.py:
--------------------------------------------------------------------------------
1 | name = 'pkg_a'
2 | version = '0.2.0'
3 |
--------------------------------------------------------------------------------
/tests/data/remote/pkg_b/0.1.0/package.py:
--------------------------------------------------------------------------------
1 | name = 'pkg_b'
2 | version = '0.1.0'
3 |
--------------------------------------------------------------------------------
/images/open.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuckon/rez-manager/HEAD/images/open.gif
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuckon/rez-manager/HEAD/resources/icon.png
--------------------------------------------------------------------------------
/images/spreadsheet.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuckon/rez-manager/HEAD/images/spreadsheet.gif
--------------------------------------------------------------------------------
/images/localization.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuckon/rez-manager/HEAD/images/localization.gif
--------------------------------------------------------------------------------
/images/delete-package.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cuckon/rez-manager/HEAD/images/delete-package.gif
--------------------------------------------------------------------------------
/src/rez_manager/__main__.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from Qt import QtWidgets
4 |
5 | from .window import ManagerWin
6 |
7 |
8 | if __name__ == '__main__':
9 | app = QtWidgets.QApplication(sys.argv)
10 | win = ManagerWin()
11 | win.resize(1200, 600)
12 | win.show()
13 | sys.exit(app.exec_())
14 |
--------------------------------------------------------------------------------
/src/rez_manager/utils.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 |
4 | def catch_exception(fn):
5 | """A decorator that catches exceptions and logs them.
6 |
7 | Args:
8 | fn (function): The function to catch exceptions.
9 | """
10 | def _wrapper(*args, **kwargs):
11 | try:
12 | fn(*args, **kwargs)
13 | except Exception:
14 | logger = logging.getLogger('rez_manager')
15 | logger.exception('Exception encountered.')
16 |
17 | return _wrapper
18 |
--------------------------------------------------------------------------------
/package.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | name = 'rez_manager'
4 |
5 | authors = ['John Su']
6 |
7 | version = '2.0.0'
8 |
9 | uuid = 'johnsu.rez-manager'
10 |
11 | requires = [
12 | 'rez',
13 | 'PyQt5',
14 | 'python-3',
15 | 'QtAwesome-0.7.3',
16 | ]
17 |
18 | tools = [
19 | 'manager',
20 | ]
21 |
22 | tests = {
23 | 'test': {
24 | 'command': 'pytest',
25 | 'requires': ['python-3', 'pytest', 'pytest_qt'],
26 | },
27 | }
28 |
29 |
30 | def commands():
31 |
32 | env.PYTHONPATH.append('{root}/src')
33 | env.PYTHONPATH.append('{root}/vendors')
34 | env.PATH.append('{root}/path')
35 | env.MANAGER_RESOURCES_FOLDER = '{root}/resources'
36 |
37 | alias('rez-manager', 'python -m rez_manager')
38 |
--------------------------------------------------------------------------------
/tests/test_main.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import os
3 |
4 | import pytest
5 | import rez.config
6 |
7 | from rez_manager import views, models
8 |
9 |
10 | ROOT = os.path.dirname(os.path.dirname(__file__))
11 |
12 |
13 | @pytest.fixture
14 | def rez_tmp_folder(tmp_path):
15 | tmp_folder = f'{tmp_path}/rez_manager'
16 | if os.path.exists(tmp_folder):
17 | shutil.rmtree(tmp_folder)
18 | os.mkdir(tmp_folder)
19 |
20 | yield tmp_folder
21 |
22 | shutil.rmtree(tmp_folder)
23 |
24 |
25 | @pytest.fixture
26 | def packages(rez_tmp_folder):
27 | local_repo = f'{rez_tmp_folder}/rez_manager/local'
28 | remote_repo = f'{rez_tmp_folder}/rez_manager/remote'
29 |
30 | config_content = dict(
31 | packages_path=[local_repo, remote_repo],
32 | local_packages_path=local_repo,
33 | )
34 | config = rez.config._create_locked_config(config_content)
35 | rez.config.config._swap(config)
36 |
37 | src_folder = os.path.join(ROOT, 'tests/data')
38 | for repo_folder in os.listdir(src_folder):
39 | shutil.copytree(
40 | os.path.join(src_folder, repo_folder),
41 | os.path.join(rez_tmp_folder, repo_folder),
42 | )
43 |
44 | yield
45 |
46 | rez.config.config._swap(config)
47 |
48 |
49 | def test_views_get_local_repo_index(packages):
50 | assert views.get_local_repo_index() == 0
51 |
52 |
53 | def test_models_update(qtbot, packages):
54 | model = models.RezPackagesModel()
55 | assert model.columnCount() == 3
56 |
--------------------------------------------------------------------------------
/src/rez_manager/textedithandler.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import re
3 | import html
4 | import bisect
5 |
6 | from Qt import QtCore
7 |
8 |
9 | def log_color(level, dark_text=True):
10 | COLORS = ['gray', 'black', 'magenta', 'red', 'red'] if dark_text \
11 | else ['gray', 'white', 'magenta', 'red', 'red']
12 | index = bisect.bisect_left(
13 | [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR],
14 | level
15 | )
16 | return COLORS[index]
17 |
18 |
19 | class TextEditHandler(logging.Handler):
20 | """Messages are guaranteed to be appended in main GUI thread."""
21 |
22 | # TODO: avoid dead textedit?
23 | HTML_RE = re.compile('<.+>')
24 |
25 | class Sender(QtCore.QObject):
26 | """Transition class."""
27 | _append = QtCore.Signal(str)
28 |
29 | def __init__(self, textedit, level=logging.DEBUG, dark_text=True):
30 | super(TextEditHandler, self).__init__(level)
31 | self._textedit = textedit
32 | self._sender = TextEditHandler.Sender()
33 | self._sender._append.connect(self._textedit.append)
34 | self.dark_text = dark_text
35 |
36 | def emit(self, record):
37 | msg = self.format(record)
38 | is_html = self.HTML_RE.search(msg)
39 | if not is_html:
40 | msg = html.escape(msg)
41 | formatted = u'{}'.format(
42 | log_color(record.levelno, dark_text=self.dark_text),
43 | msg.replace('\n', '
')
44 | )
45 | self._sender._append.emit(formatted)
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rez Managaer
2 | 
3 |
4 | 
5 |
6 | A rez util package that can:
7 | - [x] List all the status of rez packages.
8 | - [x] Localization.
9 | - [x] Delete local package.
10 | - [x] Support variants.
11 | - [ ] Filter & Sort the results.
12 | - [x] Open folder of selected package.
13 | - [ ] Work with rez in separate thread.
14 |
15 | # Features
16 |
17 | ## Informative Spreadsheet
18 |
19 | 
20 |
21 | - Gray cells package are shadowed by the black ones.
22 | - Description, tools, variants are shown in tooltips.
23 | - Package repository are represented by columns.
24 |
25 | # Highly contextual RMB menu.
26 |
27 | 
28 |
29 | Copy selected packages from remote repositories to local repositories at one click.
30 |
31 | 
32 |
33 | Reveal the package folder by menu.
34 |
35 | 
36 |
37 | You can delete the local package(s) of latest version or all versions.
38 |
39 |
40 | # Deployment
41 | Please note that the deploy scripts is not contained in this repository. I
42 | suppose it varies from place to place. The simplest way to deploy it is just
43 | copying the contents to one of your rez repository folder.
--------------------------------------------------------------------------------
/src/rez_manager/window.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | from functools import partial
4 |
5 | from Qt import QtWidgets, QtGui
6 |
7 | from .models import RezPackagesModel
8 | from .views import SpreadsheetView
9 | from .textedithandler import TextEditHandler
10 |
11 |
12 | def _setup_logger(textedit):
13 | logger = logging.getLogger('rez_manager')
14 | log_handler = TextEditHandler(textedit)
15 | log_handler.setLevel(logging.DEBUG)
16 | logger.addHandler(log_handler)
17 | logger.setLevel(logging.DEBUG)
18 | return logger
19 |
20 |
21 | class ManagerWin(QtWidgets.QMainWindow):
22 | """Main window class."""
23 | def __init__(self):
24 | super(ManagerWin, self).__init__()
25 |
26 | self.setup_window()
27 |
28 | self.splitter = QtWidgets.QSplitter(self.centralWidget())
29 | self.centralWidget().layout().addWidget(self.splitter)
30 |
31 | self.spreadsheet = self.setup_spreadsheet()
32 | self.splitter.addWidget(self.spreadsheet)
33 |
34 | self.log_widget = QtWidgets.QTextEdit()
35 | self.splitter.addWidget(self.log_widget)
36 | self.splitter.setSizes([800, 400])
37 |
38 | self.logger = _setup_logger(self.log_widget)
39 |
40 | self._connect()
41 | self.spreadsheet.model().reload()
42 |
43 | def _connect(self):
44 | self.spreadsheet.packagesChanged.connect(
45 | self.spreadsheet.model().reload
46 | )
47 |
48 | def setup_window(self):
49 | """Do the general ui setup work."""
50 | # Layout
51 | central_widget = QtWidgets.QWidget()
52 | central_widget.setLayout(QtWidgets.QVBoxLayout())
53 | self.setCentralWidget(central_widget)
54 |
55 | # Statusbar
56 | statusbar = QtWidgets.QStatusBar()
57 | self.setStatusBar(statusbar)
58 |
59 | # Appearance
60 | version = os.environ['REZ_REZ_MANAGER_VERSION']
61 | self.setWindowTitle('Rez Packages Manager - ' + version)
62 | icon_path = os.path.join(
63 | os.environ['MANAGER_RESOURCES_FOLDER'], 'icon.png'
64 | )
65 | self.setWindowIcon(QtGui.QIcon(icon_path))
66 |
67 | def show_status_message(self, message):
68 | self.statusBar().showMessage(message, 4000)
69 |
70 | def setup_spreadsheet(self):
71 | view = SpreadsheetView()
72 | model = RezPackagesModel()
73 | # proxy_model = RezPackagesProxyModel()
74 | # proxy_model.setSourceModel(model)
75 | # view.setModel(proxy_model)
76 | view.setModel(model)
77 | return view
78 |
--------------------------------------------------------------------------------
/src/rez_manager/models.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 |
4 | from Qt import QtGui, QtCore
5 | from rez import packages
6 | from rez.config import config
7 | from rez.package_repository import package_repository_manager
8 |
9 | from .utils import catch_exception
10 |
11 |
12 | def generate_item_tooltip(item):
13 | """Generate a proper tooltip for item"""
14 | if item.empty_folder:
15 | return '[Empty folder]'
16 |
17 | latest = item.latest
18 | if not latest:
19 | return ''
20 |
21 | tooltip = []
22 |
23 | if latest.description:
24 | tooltip.append(latest.format('Description: {description}'))
25 | if latest.variants:
26 | variants_string = ['Variants: ']
27 |
28 | # `latest.variants` Example:
29 | # [
30 | # [PackageRequest('python-2.7')],
31 | # [PackageRequest('python-3.7')],
32 | # ]
33 | for variant in latest.variants:
34 | variant_str = ' * '
35 | variant_str += ' | '.join(p.safe_str() for p in variant)
36 | variants_string.append(variant_str)
37 | tooltip.append('\n'.join(variants_string))
38 | if latest.tools:
39 | tooltip.append('Tools: ' + ', '.join(latest.tools))
40 |
41 | return '\n'.join(tooltip)
42 |
43 |
44 | def generate_item_text(item):
45 | if item.latest:
46 | return str(item.latest.version)
47 |
48 | if item.empty_folder:
49 | return '-'
50 |
51 | return ''
52 |
53 |
54 | class RezPackagesModel(QtGui.QStandardItemModel):
55 | cookingStarted = QtCore.Signal()
56 | cookingEnded = QtCore.Signal()
57 |
58 | def __init__(self, parent=None):
59 | self.repos = config.get('packages_path')
60 | super(RezPackagesModel, self).__init__(0, len(self.repos) + 1)
61 | self.setHorizontalHeaderLabels(['Package'] + self.repos)
62 |
63 | self.logger = logging.getLogger(__name__)
64 |
65 | @catch_exception
66 | def _make_row(self, row, family_name):
67 | self.setItem(row, 0, QtGui.QStandardItem(family_name))
68 | versions = [None]
69 | version_max = None
70 |
71 | # Fill the spreadsheet
72 | for irepo, repo in enumerate(self.repos):
73 | latest = packages.get_latest_package(family_name, paths=[repo])
74 | package_folder = os.path.join(repo, family_name)
75 |
76 | item = QtGui.QStandardItem()
77 | item.latest = None
78 | item.empty_folder = None
79 |
80 | if not latest:
81 | versions.append(None)
82 | if os.path.isdir(os.path.join(repo, family_name)):
83 | item.empty_folder = package_folder
84 | else:
85 | item.latest = latest
86 |
87 | version_max = max(latest.version, version_max) \
88 | if version_max else latest.version
89 | versions.append(latest.version or None)
90 |
91 | item.setText(generate_item_text(item))
92 |
93 | item.setToolTip(generate_item_tooltip(item))
94 | self.setItem(row, irepo + 1, item)
95 |
96 | # Find the winner
97 | winner_found = False
98 | for i in range(len(self.repos)):
99 | if (
100 | not winner_found and versions[i + 1] and
101 | versions[i + 1] == version_max
102 | ):
103 | winner_found = True
104 | else:
105 | self.item(row, i + 1).setForeground(QtGui.QColor('gray'))
106 |
107 | @catch_exception
108 | def reload(self):
109 | self.logger.info('Reloading..')
110 | package_repository_manager.clear_caches()
111 | family_names = list(set(
112 | f.name for f in packages.iter_package_families()
113 | ))
114 | self.setRowCount(len(family_names))
115 | family_names.sort(key=lambda x: x.lower())
116 |
117 | for row, family_name in enumerate(family_names):
118 | self._make_row(row, family_name)
119 |
120 | self.logger.info(f'{len(family_names)} packages collected.')
121 |
--------------------------------------------------------------------------------
/src/rez_manager/views.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import logging
4 | from functools import partial
5 |
6 | import qtawesome as qta
7 | from Qt import QtWidgets, QtCore
8 | from rez.config import config
9 | from rez.package_copy import copy_package
10 |
11 | from .utils import catch_exception
12 |
13 |
14 | def get_local_repo_index():
15 | packages_path = config.get('packages_path', [])
16 | local_packages_path = config.get('local_packages_path')
17 | if local_packages_path in packages_path:
18 | return packages_path.index(local_packages_path)
19 | return -1
20 |
21 |
22 | def format_list(list_of_str):
23 | return '\n'.join(' - ' + line for line in list_of_str)
24 |
25 |
26 | def delete_local(packages, all_version: bool):
27 | folders_deleted = []
28 | for package in packages:
29 | package_dir = os.path.join(package.resource.location, package.name)
30 |
31 | if all_version:
32 | to_delete = package_dir
33 | else:
34 | children = os.listdir(package_dir)
35 |
36 | # Remove package folder instead of version folder to avoid leaving
37 | # empty folder
38 | if len(children) == 1:
39 | assert children[0] == str(package.version)
40 | to_delete = package_dir
41 | else:
42 | to_delete = os.path.join(package_dir, str(package.version))
43 | shutil.rmtree(to_delete)
44 | folders_deleted.append(to_delete)
45 | return folders_deleted
46 |
47 |
48 | class SpreadsheetView(QtWidgets.QTreeView):
49 | packagesChanged = QtCore.Signal()
50 |
51 | def __init__(self, parent=None):
52 | super(SpreadsheetView, self).__init__(parent)
53 | self.setSelectionBehavior(self.SelectItems)
54 | self.setSelectionMode(self.ExtendedSelection)
55 | self.logger = logging.getLogger(__name__)
56 |
57 | def contextMenuEvent(self, event):
58 | indexes = self.selectionModel().selectedIndexes()
59 | if not indexes:
60 | return
61 |
62 | menu = QtWidgets.QMenu(self)
63 | model = self.model()
64 |
65 | self._add_multiple_packages_menu(menu, indexes)
66 | self._add_one_package_menu(menu, indexes)
67 |
68 | menu.addAction(qta.icon('fa.refresh'), 'Update', model.reload)
69 | menu.exec(event.globalPos())
70 |
71 | def _add_one_package_menu(self, menu, indexes):
72 | if len(indexes) == 1 and indexes[0].column() != 0 and \
73 | self.model().itemFromIndex(indexes[0]).text():
74 | menu.addAction(
75 | qta.icon('fa.folder'),
76 | 'Open Folder',
77 | partial(self.open_folder, indexes[0])
78 | )
79 |
80 | def _add_delete_packages_menu(
81 | self, indexes, model, local_repo_table_index, menu
82 | ):
83 | to_delete = []
84 | empty_package_folders = []
85 |
86 | for index in indexes:
87 | item = model.itemFromIndex(index)
88 | if index.column() == local_repo_table_index:
89 | if item.latest:
90 | to_delete.append(item.latest)
91 | elif item.empty_folder:
92 | empty_package_folders.append(item.empty_folder)
93 |
94 | actions = []
95 | if to_delete:
96 | actions.append(menu.addAction(
97 | 'Delete Local Packages (All Versions)',
98 | partial(self.on_delete_local, to_delete, True)
99 | ))
100 | actions.append(menu.addAction(
101 | 'Delete Local Packages',
102 | partial(self.on_delete_local, to_delete, False)
103 | ))
104 |
105 | if empty_package_folders:
106 | actions.append(menu.addAction(
107 | 'Delete Empty Package Folder',
108 | partial(self.delete_empty_folder, empty_package_folders)
109 | ))
110 |
111 | return actions
112 |
113 | def _add_localise_package_menu(
114 | self, indexes, model, local_repo_table_index, menu
115 | ):
116 | to_localise = []
117 |
118 | for index in indexes:
119 | item = model.itemFromIndex(index)
120 | version = item.text()
121 |
122 | if index.column() not in [0, local_repo_table_index]:
123 | if version:
124 | to_localise.append(item.latest)
125 |
126 | actions = []
127 | if to_localise:
128 | actions.append(menu.addAction(
129 | qta.icon('fa.cloud-download'),
130 | 'Localise',
131 | partial(self.localise, to_localise)
132 | ))
133 |
134 | return actions
135 |
136 | def _add_multiple_packages_menu(self, menu, indexes):
137 | local_repo_table_index = get_local_repo_index() + 1
138 | model = self.model()
139 |
140 | actions = [
141 | self._add_delete_packages_menu(
142 | indexes, model, local_repo_table_index, menu
143 | )
144 | ]
145 | actions.extend(
146 | self._add_localise_package_menu(
147 | indexes, model, local_repo_table_index, menu
148 | )
149 | )
150 |
151 | if actions:
152 | menu.addSeparator()
153 |
154 | @catch_exception
155 | def on_delete_local(self, packages, all_version):
156 | self.logger.info('Deleting..')
157 | folders_deleted = delete_local(packages, all_version)
158 | self.packagesChanged.emit()
159 | self.logger.info('Folder(s) deleted:\n' + format_list(folders_deleted))
160 |
161 | @catch_exception
162 | def delete_empty_folder(self, folders):
163 | self.logger.info('Deleting..')
164 | for folder in folders:
165 | shutil.rmtree(folder)
166 | self.logger.info('Folder(s) deleted:\n' + format_list(folders))
167 | self.packagesChanged.emit()
168 |
169 | @catch_exception
170 | def open_folder(self, index):
171 | item = self.model().itemFromIndex(index)
172 | if not item.latest:
173 | return
174 | folder = os.path.join(
175 | item.latest.resource.location,
176 | item.latest.name,
177 | str(item.latest.version),
178 | )
179 | os.startfile(folder)
180 |
181 | @catch_exception
182 | def localise(self, packages):
183 | local_repo = config.get('local_packages_path')
184 | self.logger.info('Localising..')
185 | for package in packages:
186 | copy_package(package, local_repo, keep_timestamp=True)
187 | self.logger.info(f'{len(packages)} packages localised.')
188 | self.packagesChanged.emit()
189 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
--------------------------------------------------------------------------------
/vendors/Qt.py:
--------------------------------------------------------------------------------
1 | """Minimal Python 2 & 3 shim around all Qt bindings
2 |
3 | DOCUMENTATION
4 | Qt.py was born in the film and visual effects industry to address
5 | the growing need for the development of software capable of running
6 | with more than one flavour of the Qt bindings for Python - PySide,
7 | PySide2, PyQt4 and PyQt5.
8 |
9 | 1. Build for one, run with all
10 | 2. Explicit is better than implicit
11 | 3. Support co-existence
12 |
13 | Default resolution order:
14 | - PySide2
15 | - PyQt5
16 | - PySide
17 | - PyQt4
18 |
19 | Usage:
20 | >> import sys
21 | >> from Qt import QtWidgets
22 | >> app = QtWidgets.QApplication(sys.argv)
23 | >> button = QtWidgets.QPushButton("Hello World")
24 | >> button.show()
25 | >> app.exec_()
26 |
27 | All members of PySide2 are mapped from other bindings, should they exist.
28 | If no equivalent member exist, it is excluded from Qt.py and inaccessible.
29 | The idea is to highlight members that exist across all supported binding,
30 | and guarantee that code that runs on one binding runs on all others.
31 |
32 | For more details, visit https://github.com/mottosso/Qt.py
33 |
34 | LICENSE
35 |
36 | See end of file for license (MIT, BSD) information.
37 |
38 | """
39 |
40 | import os
41 | import sys
42 | import types
43 | import shutil
44 | import importlib
45 | import json
46 |
47 |
48 | __version__ = "1.3.3"
49 |
50 | # Enable support for `from Qt import *`
51 | __all__ = []
52 |
53 | # Flags from environment variables
54 | QT_VERBOSE = bool(os.getenv("QT_VERBOSE"))
55 | QT_PREFERRED_BINDING_JSON = os.getenv("QT_PREFERRED_BINDING_JSON", "")
56 | QT_PREFERRED_BINDING = os.getenv("QT_PREFERRED_BINDING", "")
57 | QT_SIP_API_HINT = os.getenv("QT_SIP_API_HINT")
58 |
59 | # Reference to Qt.py
60 | Qt = sys.modules[__name__]
61 | Qt.QtCompat = types.ModuleType("QtCompat")
62 |
63 | try:
64 | long
65 | except NameError:
66 | # Python 3 compatibility
67 | long = int
68 |
69 |
70 | """Common members of all bindings
71 |
72 | This is where each member of Qt.py is explicitly defined.
73 | It is based on a "lowest common denominator" of all bindings;
74 | including members found in each of the 4 bindings.
75 |
76 | The "_common_members" dictionary is generated using the
77 | build_membership.sh script.
78 |
79 | """
80 |
81 | _common_members = {
82 | "QtCore": [
83 | "QAbstractAnimation",
84 | "QAbstractEventDispatcher",
85 | "QAbstractItemModel",
86 | "QAbstractListModel",
87 | "QAbstractState",
88 | "QAbstractTableModel",
89 | "QAbstractTransition",
90 | "QAnimationGroup",
91 | "QBasicTimer",
92 | "QBitArray",
93 | "QBuffer",
94 | "QByteArray",
95 | "QByteArrayMatcher",
96 | "QChildEvent",
97 | "QCoreApplication",
98 | "QCryptographicHash",
99 | "QDataStream",
100 | "QDate",
101 | "QDateTime",
102 | "QDir",
103 | "QDirIterator",
104 | "QDynamicPropertyChangeEvent",
105 | "QEasingCurve",
106 | "QElapsedTimer",
107 | "QEvent",
108 | "QEventLoop",
109 | "QEventTransition",
110 | "QFile",
111 | "QFileInfo",
112 | "QFileSystemWatcher",
113 | "QFinalState",
114 | "QGenericArgument",
115 | "QGenericReturnArgument",
116 | "QHistoryState",
117 | "QItemSelectionRange",
118 | "QIODevice",
119 | "QLibraryInfo",
120 | "QLine",
121 | "QLineF",
122 | "QLocale",
123 | "QMargins",
124 | "QMetaClassInfo",
125 | "QMetaEnum",
126 | "QMetaMethod",
127 | "QMetaObject",
128 | "QMetaProperty",
129 | "QMimeData",
130 | "QModelIndex",
131 | "QMutex",
132 | "QMutexLocker",
133 | "QObject",
134 | "QParallelAnimationGroup",
135 | "QPauseAnimation",
136 | "QPersistentModelIndex",
137 | "QPluginLoader",
138 | "QPoint",
139 | "QPointF",
140 | "QProcess",
141 | "QProcessEnvironment",
142 | "QPropertyAnimation",
143 | "QReadLocker",
144 | "QReadWriteLock",
145 | "QRect",
146 | "QRectF",
147 | "QRegExp",
148 | "QResource",
149 | "QRunnable",
150 | "QSemaphore",
151 | "QSequentialAnimationGroup",
152 | "QSettings",
153 | "QSignalMapper",
154 | "QSignalTransition",
155 | "QSize",
156 | "QSizeF",
157 | "QSocketNotifier",
158 | "QState",
159 | "QStateMachine",
160 | "QSysInfo",
161 | "QSystemSemaphore",
162 | "QT_TRANSLATE_NOOP",
163 | "QT_TR_NOOP",
164 | "QT_TR_NOOP_UTF8",
165 | "QTemporaryFile",
166 | "QTextBoundaryFinder",
167 | "QTextCodec",
168 | "QTextDecoder",
169 | "QTextEncoder",
170 | "QTextStream",
171 | "QTextStreamManipulator",
172 | "QThread",
173 | "QThreadPool",
174 | "QTime",
175 | "QTimeLine",
176 | "QTimer",
177 | "QTimerEvent",
178 | "QTranslator",
179 | "QUrl",
180 | "QVariantAnimation",
181 | "QWaitCondition",
182 | "QWriteLocker",
183 | "QXmlStreamAttribute",
184 | "QXmlStreamAttributes",
185 | "QXmlStreamEntityDeclaration",
186 | "QXmlStreamEntityResolver",
187 | "QXmlStreamNamespaceDeclaration",
188 | "QXmlStreamNotationDeclaration",
189 | "QXmlStreamReader",
190 | "QXmlStreamWriter",
191 | "Qt",
192 | "QtCriticalMsg",
193 | "QtDebugMsg",
194 | "QtFatalMsg",
195 | "QtMsgType",
196 | "QtSystemMsg",
197 | "QtWarningMsg",
198 | "qAbs",
199 | "qAddPostRoutine",
200 | "qChecksum",
201 | "qCritical",
202 | "qDebug",
203 | "qFatal",
204 | "qFuzzyCompare",
205 | "qIsFinite",
206 | "qIsInf",
207 | "qIsNaN",
208 | "qIsNull",
209 | "qRegisterResourceData",
210 | "qUnregisterResourceData",
211 | "qVersion",
212 | "qWarning",
213 | "qrand",
214 | "qsrand"
215 | ],
216 | "QtGui": [
217 | "QAbstractTextDocumentLayout",
218 | "QActionEvent",
219 | "QBitmap",
220 | "QBrush",
221 | "QClipboard",
222 | "QCloseEvent",
223 | "QColor",
224 | "QConicalGradient",
225 | "QContextMenuEvent",
226 | "QCursor",
227 | "QDesktopServices",
228 | "QDoubleValidator",
229 | "QDrag",
230 | "QDragEnterEvent",
231 | "QDragLeaveEvent",
232 | "QDragMoveEvent",
233 | "QDropEvent",
234 | "QFileOpenEvent",
235 | "QFocusEvent",
236 | "QFont",
237 | "QFontDatabase",
238 | "QFontInfo",
239 | "QFontMetrics",
240 | "QFontMetricsF",
241 | "QGradient",
242 | "QHelpEvent",
243 | "QHideEvent",
244 | "QHoverEvent",
245 | "QIcon",
246 | "QIconDragEvent",
247 | "QIconEngine",
248 | "QImage",
249 | "QImageIOHandler",
250 | "QImageReader",
251 | "QImageWriter",
252 | "QInputEvent",
253 | "QInputMethodEvent",
254 | "QIntValidator",
255 | "QKeyEvent",
256 | "QKeySequence",
257 | "QLinearGradient",
258 | "QMatrix2x2",
259 | "QMatrix2x3",
260 | "QMatrix2x4",
261 | "QMatrix3x2",
262 | "QMatrix3x3",
263 | "QMatrix3x4",
264 | "QMatrix4x2",
265 | "QMatrix4x3",
266 | "QMatrix4x4",
267 | "QMouseEvent",
268 | "QMoveEvent",
269 | "QMovie",
270 | "QPaintDevice",
271 | "QPaintEngine",
272 | "QPaintEngineState",
273 | "QPaintEvent",
274 | "QPainter",
275 | "QPainterPath",
276 | "QPainterPathStroker",
277 | "QPalette",
278 | "QPen",
279 | "QPicture",
280 | "QPictureIO",
281 | "QPixmap",
282 | "QPixmapCache",
283 | "QPolygon",
284 | "QPolygonF",
285 | "QQuaternion",
286 | "QRadialGradient",
287 | "QRegExpValidator",
288 | "QRegion",
289 | "QResizeEvent",
290 | "QSessionManager",
291 | "QShortcutEvent",
292 | "QShowEvent",
293 | "QStandardItem",
294 | "QStandardItemModel",
295 | "QStatusTipEvent",
296 | "QSyntaxHighlighter",
297 | "QTabletEvent",
298 | "QTextBlock",
299 | "QTextBlockFormat",
300 | "QTextBlockGroup",
301 | "QTextBlockUserData",
302 | "QTextCharFormat",
303 | "QTextCursor",
304 | "QTextDocument",
305 | "QTextDocumentFragment",
306 | "QTextFormat",
307 | "QTextFragment",
308 | "QTextFrame",
309 | "QTextFrameFormat",
310 | "QTextImageFormat",
311 | "QTextInlineObject",
312 | "QTextItem",
313 | "QTextLayout",
314 | "QTextLength",
315 | "QTextLine",
316 | "QTextList",
317 | "QTextListFormat",
318 | "QTextObject",
319 | "QTextObjectInterface",
320 | "QTextOption",
321 | "QTextTable",
322 | "QTextTableCell",
323 | "QTextTableCellFormat",
324 | "QTextTableFormat",
325 | "QTouchEvent",
326 | "QTransform",
327 | "QValidator",
328 | "QVector2D",
329 | "QVector3D",
330 | "QVector4D",
331 | "QWhatsThisClickedEvent",
332 | "QWheelEvent",
333 | "QWindowStateChangeEvent",
334 | "qAlpha",
335 | "qBlue",
336 | "qGray",
337 | "qGreen",
338 | "qIsGray",
339 | "qRed",
340 | "qRgb",
341 | "qRgba"
342 | ],
343 | "QtHelp": [
344 | "QHelpContentItem",
345 | "QHelpContentModel",
346 | "QHelpContentWidget",
347 | "QHelpEngine",
348 | "QHelpEngineCore",
349 | "QHelpIndexModel",
350 | "QHelpIndexWidget",
351 | "QHelpSearchEngine",
352 | "QHelpSearchQuery",
353 | "QHelpSearchQueryWidget",
354 | "QHelpSearchResultWidget"
355 | ],
356 | "QtMultimedia": [
357 | "QAbstractVideoBuffer",
358 | "QAbstractVideoSurface",
359 | "QAudio",
360 | "QAudioDeviceInfo",
361 | "QAudioFormat",
362 | "QAudioInput",
363 | "QAudioOutput",
364 | "QVideoFrame",
365 | "QVideoSurfaceFormat"
366 | ],
367 | "QtNetwork": [
368 | "QAbstractNetworkCache",
369 | "QAbstractSocket",
370 | "QAuthenticator",
371 | "QHostAddress",
372 | "QHostInfo",
373 | "QLocalServer",
374 | "QLocalSocket",
375 | "QNetworkAccessManager",
376 | "QNetworkAddressEntry",
377 | "QNetworkCacheMetaData",
378 | "QNetworkConfiguration",
379 | "QNetworkConfigurationManager",
380 | "QNetworkCookie",
381 | "QNetworkCookieJar",
382 | "QNetworkDiskCache",
383 | "QNetworkInterface",
384 | "QNetworkProxy",
385 | "QNetworkProxyFactory",
386 | "QNetworkProxyQuery",
387 | "QNetworkReply",
388 | "QNetworkRequest",
389 | "QNetworkSession",
390 | "QSsl",
391 | "QTcpServer",
392 | "QTcpSocket",
393 | "QUdpSocket"
394 | ],
395 | "QtOpenGL": [
396 | "QGL",
397 | "QGLContext",
398 | "QGLFormat",
399 | "QGLWidget"
400 | ],
401 | "QtPrintSupport": [
402 | "QAbstractPrintDialog",
403 | "QPageSetupDialog",
404 | "QPrintDialog",
405 | "QPrintEngine",
406 | "QPrintPreviewDialog",
407 | "QPrintPreviewWidget",
408 | "QPrinter",
409 | "QPrinterInfo"
410 | ],
411 | "QtSql": [
412 | "QSql",
413 | "QSqlDatabase",
414 | "QSqlDriver",
415 | "QSqlDriverCreatorBase",
416 | "QSqlError",
417 | "QSqlField",
418 | "QSqlIndex",
419 | "QSqlQuery",
420 | "QSqlQueryModel",
421 | "QSqlRecord",
422 | "QSqlRelation",
423 | "QSqlRelationalDelegate",
424 | "QSqlRelationalTableModel",
425 | "QSqlResult",
426 | "QSqlTableModel"
427 | ],
428 | "QtSvg": [
429 | "QGraphicsSvgItem",
430 | "QSvgGenerator",
431 | "QSvgRenderer",
432 | "QSvgWidget"
433 | ],
434 | "QtTest": [
435 | "QTest"
436 | ],
437 | "QtWidgets": [
438 | "QAbstractButton",
439 | "QAbstractGraphicsShapeItem",
440 | "QAbstractItemDelegate",
441 | "QAbstractItemView",
442 | "QAbstractScrollArea",
443 | "QAbstractSlider",
444 | "QAbstractSpinBox",
445 | "QAction",
446 | "QActionGroup",
447 | "QApplication",
448 | "QBoxLayout",
449 | "QButtonGroup",
450 | "QCalendarWidget",
451 | "QCheckBox",
452 | "QColorDialog",
453 | "QColumnView",
454 | "QComboBox",
455 | "QCommandLinkButton",
456 | "QCommonStyle",
457 | "QCompleter",
458 | "QDataWidgetMapper",
459 | "QDateEdit",
460 | "QDateTimeEdit",
461 | "QDesktopWidget",
462 | "QDial",
463 | "QDialog",
464 | "QDialogButtonBox",
465 | "QDirModel",
466 | "QDockWidget",
467 | "QDoubleSpinBox",
468 | "QErrorMessage",
469 | "QFileDialog",
470 | "QFileIconProvider",
471 | "QFileSystemModel",
472 | "QFocusFrame",
473 | "QFontComboBox",
474 | "QFontDialog",
475 | "QFormLayout",
476 | "QFrame",
477 | "QGesture",
478 | "QGestureEvent",
479 | "QGestureRecognizer",
480 | "QGraphicsAnchor",
481 | "QGraphicsAnchorLayout",
482 | "QGraphicsBlurEffect",
483 | "QGraphicsColorizeEffect",
484 | "QGraphicsDropShadowEffect",
485 | "QGraphicsEffect",
486 | "QGraphicsEllipseItem",
487 | "QGraphicsGridLayout",
488 | "QGraphicsItem",
489 | "QGraphicsItemGroup",
490 | "QGraphicsLayout",
491 | "QGraphicsLayoutItem",
492 | "QGraphicsLineItem",
493 | "QGraphicsLinearLayout",
494 | "QGraphicsObject",
495 | "QGraphicsOpacityEffect",
496 | "QGraphicsPathItem",
497 | "QGraphicsPixmapItem",
498 | "QGraphicsPolygonItem",
499 | "QGraphicsProxyWidget",
500 | "QGraphicsRectItem",
501 | "QGraphicsRotation",
502 | "QGraphicsScale",
503 | "QGraphicsScene",
504 | "QGraphicsSceneContextMenuEvent",
505 | "QGraphicsSceneDragDropEvent",
506 | "QGraphicsSceneEvent",
507 | "QGraphicsSceneHelpEvent",
508 | "QGraphicsSceneHoverEvent",
509 | "QGraphicsSceneMouseEvent",
510 | "QGraphicsSceneMoveEvent",
511 | "QGraphicsSceneResizeEvent",
512 | "QGraphicsSceneWheelEvent",
513 | "QGraphicsSimpleTextItem",
514 | "QGraphicsTextItem",
515 | "QGraphicsTransform",
516 | "QGraphicsView",
517 | "QGraphicsWidget",
518 | "QGridLayout",
519 | "QGroupBox",
520 | "QHBoxLayout",
521 | "QHeaderView",
522 | "QInputDialog",
523 | "QItemDelegate",
524 | "QItemEditorCreatorBase",
525 | "QItemEditorFactory",
526 | "QKeyEventTransition",
527 | "QLCDNumber",
528 | "QLabel",
529 | "QLayout",
530 | "QLayoutItem",
531 | "QLineEdit",
532 | "QListView",
533 | "QListWidget",
534 | "QListWidgetItem",
535 | "QMainWindow",
536 | "QMdiArea",
537 | "QMdiSubWindow",
538 | "QMenu",
539 | "QMenuBar",
540 | "QMessageBox",
541 | "QMouseEventTransition",
542 | "QPanGesture",
543 | "QPinchGesture",
544 | "QPlainTextDocumentLayout",
545 | "QPlainTextEdit",
546 | "QProgressBar",
547 | "QProgressDialog",
548 | "QPushButton",
549 | "QRadioButton",
550 | "QRubberBand",
551 | "QScrollArea",
552 | "QScrollBar",
553 | "QShortcut",
554 | "QSizeGrip",
555 | "QSizePolicy",
556 | "QSlider",
557 | "QSpacerItem",
558 | "QSpinBox",
559 | "QSplashScreen",
560 | "QSplitter",
561 | "QSplitterHandle",
562 | "QStackedLayout",
563 | "QStackedWidget",
564 | "QStatusBar",
565 | "QStyle",
566 | "QStyleFactory",
567 | "QStyleHintReturn",
568 | "QStyleHintReturnMask",
569 | "QStyleHintReturnVariant",
570 | "QStyleOption",
571 | "QStyleOptionButton",
572 | "QStyleOptionComboBox",
573 | "QStyleOptionComplex",
574 | "QStyleOptionDockWidget",
575 | "QStyleOptionFocusRect",
576 | "QStyleOptionFrame",
577 | "QStyleOptionGraphicsItem",
578 | "QStyleOptionGroupBox",
579 | "QStyleOptionHeader",
580 | "QStyleOptionMenuItem",
581 | "QStyleOptionProgressBar",
582 | "QStyleOptionRubberBand",
583 | "QStyleOptionSizeGrip",
584 | "QStyleOptionSlider",
585 | "QStyleOptionSpinBox",
586 | "QStyleOptionTab",
587 | "QStyleOptionTabBarBase",
588 | "QStyleOptionTabWidgetFrame",
589 | "QStyleOptionTitleBar",
590 | "QStyleOptionToolBar",
591 | "QStyleOptionToolBox",
592 | "QStyleOptionToolButton",
593 | "QStyleOptionViewItem",
594 | "QStylePainter",
595 | "QStyledItemDelegate",
596 | "QSwipeGesture",
597 | "QSystemTrayIcon",
598 | "QTabBar",
599 | "QTabWidget",
600 | "QTableView",
601 | "QTableWidget",
602 | "QTableWidgetItem",
603 | "QTableWidgetSelectionRange",
604 | "QTapAndHoldGesture",
605 | "QTapGesture",
606 | "QTextBrowser",
607 | "QTextEdit",
608 | "QTimeEdit",
609 | "QToolBar",
610 | "QToolBox",
611 | "QToolButton",
612 | "QToolTip",
613 | "QTreeView",
614 | "QTreeWidget",
615 | "QTreeWidgetItem",
616 | "QTreeWidgetItemIterator",
617 | "QUndoCommand",
618 | "QUndoGroup",
619 | "QUndoStack",
620 | "QUndoView",
621 | "QVBoxLayout",
622 | "QWhatsThis",
623 | "QWidget",
624 | "QWidgetAction",
625 | "QWidgetItem",
626 | "QWizard",
627 | "QWizardPage"
628 | ],
629 | "QtX11Extras": [
630 | "QX11Info"
631 | ],
632 | "QtXml": [
633 | "QDomAttr",
634 | "QDomCDATASection",
635 | "QDomCharacterData",
636 | "QDomComment",
637 | "QDomDocument",
638 | "QDomDocumentFragment",
639 | "QDomDocumentType",
640 | "QDomElement",
641 | "QDomEntity",
642 | "QDomEntityReference",
643 | "QDomImplementation",
644 | "QDomNamedNodeMap",
645 | "QDomNode",
646 | "QDomNodeList",
647 | "QDomNotation",
648 | "QDomProcessingInstruction",
649 | "QDomText",
650 | "QXmlAttributes",
651 | "QXmlContentHandler",
652 | "QXmlDTDHandler",
653 | "QXmlDeclHandler",
654 | "QXmlDefaultHandler",
655 | "QXmlEntityResolver",
656 | "QXmlErrorHandler",
657 | "QXmlInputSource",
658 | "QXmlLexicalHandler",
659 | "QXmlLocator",
660 | "QXmlNamespaceSupport",
661 | "QXmlParseException",
662 | "QXmlReader",
663 | "QXmlSimpleReader"
664 | ],
665 | "QtXmlPatterns": [
666 | "QAbstractMessageHandler",
667 | "QAbstractUriResolver",
668 | "QAbstractXmlNodeModel",
669 | "QAbstractXmlReceiver",
670 | "QSourceLocation",
671 | "QXmlFormatter",
672 | "QXmlItem",
673 | "QXmlName",
674 | "QXmlNamePool",
675 | "QXmlNodeModelIndex",
676 | "QXmlQuery",
677 | "QXmlResultItems",
678 | "QXmlSchema",
679 | "QXmlSchemaValidator",
680 | "QXmlSerializer"
681 | ]
682 | }
683 |
684 | """ Missing members
685 |
686 | This mapping describes members that have been deprecated
687 | in one or more bindings and have been left out of the
688 | _common_members mapping.
689 |
690 | The member can provide an extra details string to be
691 | included in exceptions and warnings.
692 | """
693 |
694 | _missing_members = {
695 | "QtGui": {
696 | "QMatrix": "Deprecated in PyQt5",
697 | },
698 | }
699 |
700 |
701 | def _qInstallMessageHandler(handler):
702 | """Install a message handler that works in all bindings
703 |
704 | Args:
705 | handler: A function that takes 3 arguments, or None
706 | """
707 | def messageOutputHandler(*args):
708 | # In Qt4 bindings, message handlers are passed 2 arguments
709 | # In Qt5 bindings, message handlers are passed 3 arguments
710 | # The first argument is a QtMsgType
711 | # The last argument is the message to be printed
712 | # The Middle argument (if passed) is a QMessageLogContext
713 | if len(args) == 3:
714 | msgType, logContext, msg = args
715 | elif len(args) == 2:
716 | msgType, msg = args
717 | logContext = None
718 | else:
719 | raise TypeError(
720 | "handler expected 2 or 3 arguments, got {0}".format(len(args)))
721 |
722 | if isinstance(msg, bytes):
723 | # In python 3, some bindings pass a bytestring, which cannot be
724 | # used elsewhere. Decoding a python 2 or 3 bytestring object will
725 | # consistently return a unicode object.
726 | msg = msg.decode()
727 |
728 | handler(msgType, logContext, msg)
729 |
730 | passObject = messageOutputHandler if handler else handler
731 | if Qt.IsPySide or Qt.IsPyQt4:
732 | return Qt._QtCore.qInstallMsgHandler(passObject)
733 | elif Qt.IsPySide2 or Qt.IsPyQt5:
734 | return Qt._QtCore.qInstallMessageHandler(passObject)
735 |
736 |
737 | def _getcpppointer(object):
738 | if hasattr(Qt, "_shiboken2"):
739 | return getattr(Qt, "_shiboken2").getCppPointer(object)[0]
740 | elif hasattr(Qt, "_shiboken"):
741 | return getattr(Qt, "_shiboken").getCppPointer(object)[0]
742 | elif hasattr(Qt, "_sip"):
743 | return getattr(Qt, "_sip").unwrapinstance(object)
744 | raise AttributeError("'module' has no attribute 'getCppPointer'")
745 |
746 |
747 | def _wrapinstance(ptr, base=None):
748 | """Enable implicit cast of pointer to most suitable class
749 |
750 | This behaviour is available in sip per default.
751 |
752 | Based on http://nathanhorne.com/pyqtpyside-wrap-instance
753 |
754 | Usage:
755 | This mechanism kicks in under these circumstances.
756 | 1. Qt.py is using PySide 1 or 2.
757 | 2. A `base` argument is not provided.
758 |
759 | See :func:`QtCompat.wrapInstance()`
760 |
761 | Arguments:
762 | ptr (long): Pointer to QObject in memory
763 | base (QObject, optional): Base class to wrap with. Defaults to QObject,
764 | which should handle anything.
765 |
766 | """
767 |
768 | assert isinstance(ptr, long), "Argument 'ptr' must be of type "
769 | assert (base is None) or issubclass(base, Qt.QtCore.QObject), (
770 | "Argument 'base' must be of type ")
771 |
772 | if Qt.IsPyQt4 or Qt.IsPyQt5:
773 | func = getattr(Qt, "_sip").wrapinstance
774 | elif Qt.IsPySide2:
775 | func = getattr(Qt, "_shiboken2").wrapInstance
776 | elif Qt.IsPySide:
777 | func = getattr(Qt, "_shiboken").wrapInstance
778 | else:
779 | raise AttributeError("'module' has no attribute 'wrapInstance'")
780 |
781 | if base is None:
782 | if Qt.IsPyQt4 or Qt.IsPyQt5:
783 | base = Qt.QtCore.QObject
784 | else:
785 | q_object = func(long(ptr), Qt.QtCore.QObject)
786 | meta_object = q_object.metaObject()
787 |
788 | while True:
789 | class_name = meta_object.className()
790 |
791 | try:
792 | base = getattr(Qt.QtWidgets, class_name)
793 | except AttributeError:
794 | try:
795 | base = getattr(Qt.QtCore, class_name)
796 | except AttributeError:
797 | meta_object = meta_object.superClass()
798 | continue
799 |
800 | break
801 |
802 | return func(long(ptr), base)
803 |
804 |
805 | def _isvalid(object):
806 | """Check if the object is valid to use in Python runtime.
807 |
808 | Usage:
809 | See :func:`QtCompat.isValid()`
810 |
811 | Arguments:
812 | object (QObject): QObject to check the validity of.
813 |
814 | """
815 |
816 | assert isinstance(object, Qt.QtCore.QObject)
817 |
818 | if hasattr(Qt, "_shiboken2"):
819 | return getattr(Qt, "_shiboken2").isValid(object)
820 |
821 | elif hasattr(Qt, "_shiboken"):
822 | return getattr(Qt, "_shiboken").isValid(object)
823 |
824 | elif hasattr(Qt, "_sip"):
825 | return not getattr(Qt, "_sip").isdeleted(object)
826 |
827 | else:
828 | raise AttributeError("'module' has no attribute isValid")
829 |
830 |
831 | def _translate(context, sourceText, *args):
832 | # In Qt4 bindings, translate can be passed 2 or 3 arguments
833 | # In Qt5 bindings, translate can be passed 2 arguments
834 | # The first argument is disambiguation[str]
835 | # The last argument is n[int]
836 | # The middle argument can be encoding[QtCore.QCoreApplication.Encoding]
837 | if len(args) == 3:
838 | disambiguation, encoding, n = args
839 | elif len(args) == 2:
840 | disambiguation, n = args
841 | encoding = None
842 | else:
843 | raise TypeError(
844 | "Expected 4 or 5 arguments, got {0}.".format(len(args) + 2))
845 |
846 | if hasattr(Qt.QtCore, "QCoreApplication"):
847 | app = getattr(Qt.QtCore, "QCoreApplication")
848 | else:
849 | raise NotImplementedError(
850 | "Missing QCoreApplication implementation for {binding}".format(
851 | binding=Qt.__binding__,
852 | )
853 | )
854 | if Qt.__binding__ in ("PySide2", "PyQt5"):
855 | sanitized_args = [context, sourceText, disambiguation, n]
856 | else:
857 | sanitized_args = [
858 | context,
859 | sourceText,
860 | disambiguation,
861 | encoding or app.CodecForTr,
862 | n
863 | ]
864 | return app.translate(*sanitized_args)
865 |
866 |
867 | def _loadUi(uifile, baseinstance=None):
868 | """Dynamically load a user interface from the given `uifile`
869 |
870 | This function calls `uic.loadUi` if using PyQt bindings,
871 | else it implements a comparable binding for PySide.
872 |
873 | Documentation:
874 | http://pyqt.sourceforge.net/Docs/PyQt5/designer.html#PyQt5.uic.loadUi
875 |
876 | Arguments:
877 | uifile (str): Absolute path to Qt Designer file.
878 | baseinstance (QWidget): Instantiated QWidget or subclass thereof
879 |
880 | Return:
881 | baseinstance if `baseinstance` is not `None`. Otherwise
882 | return the newly created instance of the user interface.
883 |
884 | """
885 | if hasattr(Qt, "_uic"):
886 | return Qt._uic.loadUi(uifile, baseinstance)
887 |
888 | elif hasattr(Qt, "_QtUiTools"):
889 | # Implement `PyQt5.uic.loadUi` for PySide(2)
890 |
891 | class _UiLoader(Qt._QtUiTools.QUiLoader):
892 | """Create the user interface in a base instance.
893 |
894 | Unlike `Qt._QtUiTools.QUiLoader` itself this class does not
895 | create a new instance of the top-level widget, but creates the user
896 | interface in an existing instance of the top-level class if needed.
897 |
898 | This mimics the behaviour of `PyQt5.uic.loadUi`.
899 |
900 | """
901 |
902 | def __init__(self, baseinstance):
903 | super(_UiLoader, self).__init__(baseinstance)
904 | self.baseinstance = baseinstance
905 | self.custom_widgets = {}
906 |
907 | def _loadCustomWidgets(self, etree):
908 | """
909 | Workaround to pyside-77 bug.
910 |
911 | From QUiLoader doc we should use registerCustomWidget method.
912 | But this causes a segfault on some platforms.
913 |
914 | Instead we fetch from customwidgets DOM node the python class
915 | objects. Then we can directly use them in createWidget method.
916 | """
917 |
918 | def headerToModule(header):
919 | """
920 | Translate a header file to python module path
921 | foo/bar.h => foo.bar
922 | """
923 | # Remove header extension
924 | module = os.path.splitext(header)[0]
925 |
926 | # Replace os separator by python module separator
927 | return module.replace("/", ".").replace("\\", ".")
928 |
929 | custom_widgets = etree.find("customwidgets")
930 |
931 | if custom_widgets is None:
932 | return
933 |
934 | for custom_widget in custom_widgets:
935 | class_name = custom_widget.find("class").text
936 | header = custom_widget.find("header").text
937 | module = importlib.import_module(headerToModule(header))
938 | self.custom_widgets[class_name] = getattr(module,
939 | class_name)
940 |
941 | def load(self, uifile, *args, **kwargs):
942 | from xml.etree.ElementTree import ElementTree
943 |
944 | # For whatever reason, if this doesn't happen then
945 | # reading an invalid or non-existing .ui file throws
946 | # a RuntimeError.
947 | etree = ElementTree()
948 | etree.parse(uifile)
949 | self._loadCustomWidgets(etree)
950 |
951 | widget = Qt._QtUiTools.QUiLoader.load(
952 | self, uifile, *args, **kwargs)
953 |
954 | # Workaround for PySide 1.0.9, see issue #208
955 | widget.parentWidget()
956 |
957 | return widget
958 |
959 | def createWidget(self, class_name, parent=None, name=""):
960 | """Called for each widget defined in ui file
961 |
962 | Overridden here to populate `baseinstance` instead.
963 |
964 | """
965 |
966 | if parent is None and self.baseinstance:
967 | # Supposed to create the top-level widget,
968 | # return the base instance instead
969 | return self.baseinstance
970 |
971 | # For some reason, Line is not in the list of available
972 | # widgets, but works fine, so we have to special case it here.
973 | if class_name in self.availableWidgets() + ["Line"]:
974 | # Create a new widget for child widgets
975 | widget = Qt._QtUiTools.QUiLoader.createWidget(self,
976 | class_name,
977 | parent,
978 | name)
979 | elif class_name in self.custom_widgets:
980 | widget = self.custom_widgets[class_name](parent=parent)
981 | else:
982 | raise Exception("Custom widget '%s' not supported"
983 | % class_name)
984 |
985 | if self.baseinstance:
986 | # Set an attribute for the new child widget on the base
987 | # instance, just like PyQt5.uic.loadUi does.
988 | setattr(self.baseinstance, name, widget)
989 |
990 | return widget
991 |
992 | widget = _UiLoader(baseinstance).load(uifile)
993 | Qt.QtCore.QMetaObject.connectSlotsByName(widget)
994 |
995 | return widget
996 |
997 | else:
998 | raise NotImplementedError("No implementation available for loadUi")
999 |
1000 |
1001 | """Misplaced members
1002 |
1003 | These members from the original submodule are misplaced relative PySide2
1004 |
1005 | """
1006 | _misplaced_members = {
1007 | "PySide2": {
1008 | "QtCore.QStringListModel": "QtCore.QStringListModel",
1009 | "QtGui.QStringListModel": "QtCore.QStringListModel",
1010 | "QtCore.Property": "QtCore.Property",
1011 | "QtCore.Signal": "QtCore.Signal",
1012 | "QtCore.Slot": "QtCore.Slot",
1013 | "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
1014 | "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel",
1015 | "QtCore.QItemSelection": "QtCore.QItemSelection",
1016 | "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel",
1017 | "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange",
1018 | "QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi],
1019 | "shiboken2.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance],
1020 | "shiboken2.getCppPointer": ["QtCompat.getCppPointer", _getcpppointer],
1021 | "shiboken2.isValid": ["QtCompat.isValid", _isvalid],
1022 | "QtWidgets.qApp": "QtWidgets.QApplication.instance()",
1023 | "QtCore.QCoreApplication.translate": [
1024 | "QtCompat.translate", _translate
1025 | ],
1026 | "QtWidgets.QApplication.translate": [
1027 | "QtCompat.translate", _translate
1028 | ],
1029 | "QtCore.qInstallMessageHandler": [
1030 | "QtCompat.qInstallMessageHandler", _qInstallMessageHandler
1031 | ],
1032 | "QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4",
1033 | },
1034 | "PyQt5": {
1035 | "QtCore.pyqtProperty": "QtCore.Property",
1036 | "QtCore.pyqtSignal": "QtCore.Signal",
1037 | "QtCore.pyqtSlot": "QtCore.Slot",
1038 | "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
1039 | "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel",
1040 | "QtCore.QStringListModel": "QtCore.QStringListModel",
1041 | "QtCore.QItemSelection": "QtCore.QItemSelection",
1042 | "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel",
1043 | "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange",
1044 | "uic.loadUi": ["QtCompat.loadUi", _loadUi],
1045 | "sip.wrapinstance": ["QtCompat.wrapInstance", _wrapinstance],
1046 | "sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer],
1047 | "sip.isdeleted": ["QtCompat.isValid", _isvalid],
1048 | "QtWidgets.qApp": "QtWidgets.QApplication.instance()",
1049 | "QtCore.QCoreApplication.translate": [
1050 | "QtCompat.translate", _translate
1051 | ],
1052 | "QtWidgets.QApplication.translate": [
1053 | "QtCompat.translate", _translate
1054 | ],
1055 | "QtCore.qInstallMessageHandler": [
1056 | "QtCompat.qInstallMessageHandler", _qInstallMessageHandler
1057 | ],
1058 | "QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4",
1059 | },
1060 | "PySide": {
1061 | "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
1062 | "QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel",
1063 | "QtGui.QStringListModel": "QtCore.QStringListModel",
1064 | "QtGui.QItemSelection": "QtCore.QItemSelection",
1065 | "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel",
1066 | "QtCore.Property": "QtCore.Property",
1067 | "QtCore.Signal": "QtCore.Signal",
1068 | "QtCore.Slot": "QtCore.Slot",
1069 | "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange",
1070 | "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog",
1071 | "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog",
1072 | "QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog",
1073 | "QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine",
1074 | "QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog",
1075 | "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget",
1076 | "QtGui.QPrinter": "QtPrintSupport.QPrinter",
1077 | "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo",
1078 | "QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi],
1079 | "shiboken.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance],
1080 | "shiboken.unwrapInstance": ["QtCompat.getCppPointer", _getcpppointer],
1081 | "shiboken.isValid": ["QtCompat.isValid", _isvalid],
1082 | "QtGui.qApp": "QtWidgets.QApplication.instance()",
1083 | "QtCore.QCoreApplication.translate": [
1084 | "QtCompat.translate", _translate
1085 | ],
1086 | "QtGui.QApplication.translate": [
1087 | "QtCompat.translate", _translate
1088 | ],
1089 | "QtCore.qInstallMsgHandler": [
1090 | "QtCompat.qInstallMessageHandler", _qInstallMessageHandler
1091 | ],
1092 | "QtGui.QStyleOptionViewItemV4": "QtCompat.QStyleOptionViewItemV4",
1093 | },
1094 | "PyQt4": {
1095 | "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel",
1096 | "QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel",
1097 | "QtGui.QItemSelection": "QtCore.QItemSelection",
1098 | "QtGui.QStringListModel": "QtCore.QStringListModel",
1099 | "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel",
1100 | "QtCore.pyqtProperty": "QtCore.Property",
1101 | "QtCore.pyqtSignal": "QtCore.Signal",
1102 | "QtCore.pyqtSlot": "QtCore.Slot",
1103 | "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange",
1104 | "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog",
1105 | "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog",
1106 | "QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog",
1107 | "QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine",
1108 | "QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog",
1109 | "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget",
1110 | "QtGui.QPrinter": "QtPrintSupport.QPrinter",
1111 | "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo",
1112 | # "QtCore.pyqtSignature": "QtCore.Slot",
1113 | "uic.loadUi": ["QtCompat.loadUi", _loadUi],
1114 | "sip.wrapinstance": ["QtCompat.wrapInstance", _wrapinstance],
1115 | "sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer],
1116 | "sip.isdeleted": ["QtCompat.isValid", _isvalid],
1117 | "QtCore.QString": "str",
1118 | "QtGui.qApp": "QtWidgets.QApplication.instance()",
1119 | "QtCore.QCoreApplication.translate": [
1120 | "QtCompat.translate", _translate
1121 | ],
1122 | "QtGui.QApplication.translate": [
1123 | "QtCompat.translate", _translate
1124 | ],
1125 | "QtCore.qInstallMsgHandler": [
1126 | "QtCompat.qInstallMessageHandler", _qInstallMessageHandler
1127 | ],
1128 | "QtGui.QStyleOptionViewItemV4": "QtCompat.QStyleOptionViewItemV4",
1129 | }
1130 | }
1131 |
1132 | """ Compatibility Members
1133 |
1134 | This dictionary is used to build Qt.QtCompat objects that provide a consistent
1135 | interface for obsolete members, and differences in binding return values.
1136 |
1137 | {
1138 | "binding": {
1139 | "classname": {
1140 | "targetname": "binding_namespace",
1141 | }
1142 | }
1143 | }
1144 | """
1145 | _compatibility_members = {
1146 | "PySide2": {
1147 | "QWidget": {
1148 | "grab": "QtWidgets.QWidget.grab",
1149 | },
1150 | "QHeaderView": {
1151 | "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable",
1152 | "setSectionsClickable":
1153 | "QtWidgets.QHeaderView.setSectionsClickable",
1154 | "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode",
1155 | "setSectionResizeMode":
1156 | "QtWidgets.QHeaderView.setSectionResizeMode",
1157 | "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable",
1158 | "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable",
1159 | },
1160 | "QFileDialog": {
1161 | "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName",
1162 | "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
1163 | "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
1164 | },
1165 | },
1166 | "PyQt5": {
1167 | "QWidget": {
1168 | "grab": "QtWidgets.QWidget.grab",
1169 | },
1170 | "QHeaderView": {
1171 | "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable",
1172 | "setSectionsClickable":
1173 | "QtWidgets.QHeaderView.setSectionsClickable",
1174 | "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode",
1175 | "setSectionResizeMode":
1176 | "QtWidgets.QHeaderView.setSectionResizeMode",
1177 | "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable",
1178 | "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable",
1179 | },
1180 | "QFileDialog": {
1181 | "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName",
1182 | "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
1183 | "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
1184 | },
1185 | },
1186 | "PySide": {
1187 | "QWidget": {
1188 | "grab": "QtWidgets.QPixmap.grabWidget",
1189 | },
1190 | "QHeaderView": {
1191 | "sectionsClickable": "QtWidgets.QHeaderView.isClickable",
1192 | "setSectionsClickable": "QtWidgets.QHeaderView.setClickable",
1193 | "sectionResizeMode": "QtWidgets.QHeaderView.resizeMode",
1194 | "setSectionResizeMode": "QtWidgets.QHeaderView.setResizeMode",
1195 | "sectionsMovable": "QtWidgets.QHeaderView.isMovable",
1196 | "setSectionsMovable": "QtWidgets.QHeaderView.setMovable",
1197 | },
1198 | "QFileDialog": {
1199 | "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName",
1200 | "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
1201 | "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
1202 | },
1203 | },
1204 | "PyQt4": {
1205 | "QWidget": {
1206 | "grab": "QtWidgets.QPixmap.grabWidget",
1207 | },
1208 | "QHeaderView": {
1209 | "sectionsClickable": "QtWidgets.QHeaderView.isClickable",
1210 | "setSectionsClickable": "QtWidgets.QHeaderView.setClickable",
1211 | "sectionResizeMode": "QtWidgets.QHeaderView.resizeMode",
1212 | "setSectionResizeMode": "QtWidgets.QHeaderView.setResizeMode",
1213 | "sectionsMovable": "QtWidgets.QHeaderView.isMovable",
1214 | "setSectionsMovable": "QtWidgets.QHeaderView.setMovable",
1215 | },
1216 | "QFileDialog": {
1217 | "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName",
1218 | "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames",
1219 | "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName",
1220 | },
1221 | },
1222 | }
1223 |
1224 |
1225 | def _apply_site_config():
1226 | try:
1227 | import QtSiteConfig
1228 | except ImportError:
1229 | # If no QtSiteConfig module found, no modifications
1230 | # to _common_members are needed.
1231 | pass
1232 | else:
1233 | # Provide the ability to modify the dicts used to build Qt.py
1234 | if hasattr(QtSiteConfig, 'update_members'):
1235 | QtSiteConfig.update_members(_common_members)
1236 |
1237 | if hasattr(QtSiteConfig, 'update_misplaced_members'):
1238 | QtSiteConfig.update_misplaced_members(members=_misplaced_members)
1239 |
1240 | if hasattr(QtSiteConfig, 'update_compatibility_members'):
1241 | QtSiteConfig.update_compatibility_members(
1242 | members=_compatibility_members)
1243 |
1244 |
1245 | def _new_module(name):
1246 | return types.ModuleType(__name__ + "." + name)
1247 |
1248 |
1249 | def _import_sub_module(module, name):
1250 | """import_sub_module will mimic the function of importlib.import_module"""
1251 | module = __import__(module.__name__ + "." + name)
1252 | for level in name.split("."):
1253 | module = getattr(module, level)
1254 | return module
1255 |
1256 |
1257 | def _setup(module, extras):
1258 | """Install common submodules"""
1259 |
1260 | Qt.__binding__ = module.__name__
1261 |
1262 | def _warn_import_error(exc, module):
1263 | msg = str(exc)
1264 | if "No module named" in msg:
1265 | return
1266 | _warn("ImportError(%s): %s" % (module, msg))
1267 |
1268 | for name in list(_common_members) + extras:
1269 | try:
1270 | submodule = _import_sub_module(
1271 | module, name)
1272 | except ImportError as e:
1273 | try:
1274 | # For extra modules like sip and shiboken that may not be
1275 | # children of the binding.
1276 | submodule = __import__(name)
1277 | except ImportError as e2:
1278 | _warn_import_error(e, name)
1279 | _warn_import_error(e2, name)
1280 | continue
1281 |
1282 | setattr(Qt, "_" + name, submodule)
1283 |
1284 | if name not in extras:
1285 | # Store reference to original binding,
1286 | # but don't store speciality modules
1287 | # such as uic or QtUiTools
1288 | setattr(Qt, name, _new_module(name))
1289 |
1290 |
1291 | def _reassign_misplaced_members(binding):
1292 | """Apply misplaced members from `binding` to Qt.py
1293 |
1294 | Arguments:
1295 | binding (dict): Misplaced members
1296 |
1297 | """
1298 |
1299 | for src, dst in _misplaced_members[binding].items():
1300 | dst_value = None
1301 |
1302 | src_parts = src.split(".")
1303 | src_module = src_parts[0]
1304 | src_member = None
1305 | if len(src_parts) > 1:
1306 | src_member = src_parts[1:]
1307 |
1308 | if isinstance(dst, (list, tuple)):
1309 | dst, dst_value = dst
1310 |
1311 | dst_parts = dst.split(".")
1312 | dst_module = dst_parts[0]
1313 | dst_member = None
1314 | if len(dst_parts) > 1:
1315 | dst_member = dst_parts[1]
1316 |
1317 | # Get the member we want to store in the namesapce.
1318 | if not dst_value:
1319 | try:
1320 | _part = getattr(Qt, "_" + src_module)
1321 | while src_member:
1322 | member = src_member.pop(0)
1323 | _part = getattr(_part, member)
1324 | dst_value = _part
1325 | except AttributeError:
1326 | # If the member we want to store in the namespace does not
1327 | # exist, there is no need to continue. This can happen if a
1328 | # request was made to rename a member that didn't exist, for
1329 | # example if QtWidgets isn't available on the target platform.
1330 | _log("Misplaced member has no source: {0}".format(src))
1331 | continue
1332 |
1333 | try:
1334 | src_object = getattr(Qt, dst_module)
1335 | except AttributeError:
1336 | if dst_module not in _common_members:
1337 | # Only create the Qt parent module if its listed in
1338 | # _common_members. Without this check, if you remove QtCore
1339 | # from _common_members, the default _misplaced_members will add
1340 | # Qt.QtCore so it can add Signal, Slot, etc.
1341 | msg = 'Not creating missing member module "{m}" for "{c}"'
1342 | _log(msg.format(m=dst_module, c=dst_member))
1343 | continue
1344 | # If the dst is valid but the Qt parent module does not exist
1345 | # then go ahead and create a new module to contain the member.
1346 | setattr(Qt, dst_module, _new_module(dst_module))
1347 | src_object = getattr(Qt, dst_module)
1348 | # Enable direct import of the new module
1349 | sys.modules[__name__ + "." + dst_module] = src_object
1350 |
1351 | if not dst_value:
1352 | dst_value = getattr(Qt, "_" + src_module)
1353 | if src_member:
1354 | dst_value = getattr(dst_value, src_member)
1355 |
1356 | setattr(
1357 | src_object,
1358 | dst_member or dst_module,
1359 | dst_value
1360 | )
1361 |
1362 |
1363 | def _build_compatibility_members(binding, decorators=None):
1364 | """Apply `binding` to QtCompat
1365 |
1366 | Arguments:
1367 | binding (str): Top level binding in _compatibility_members.
1368 | decorators (dict, optional): Provides the ability to decorate the
1369 | original Qt methods when needed by a binding. This can be used
1370 | to change the returned value to a standard value. The key should
1371 | be the classname, the value is a dict where the keys are the
1372 | target method names, and the values are the decorator functions.
1373 |
1374 | """
1375 |
1376 | decorators = decorators or dict()
1377 |
1378 | # Allow optional site-level customization of the compatibility members.
1379 | # This method does not need to be implemented in QtSiteConfig.
1380 | try:
1381 | import QtSiteConfig
1382 | except ImportError:
1383 | pass
1384 | else:
1385 | if hasattr(QtSiteConfig, 'update_compatibility_decorators'):
1386 | QtSiteConfig.update_compatibility_decorators(binding, decorators)
1387 |
1388 | _QtCompat = type("QtCompat", (object,), {})
1389 |
1390 | for classname, bindings in _compatibility_members[binding].items():
1391 | attrs = {}
1392 | for target, binding in bindings.items():
1393 | namespaces = binding.split('.')
1394 | try:
1395 | src_object = getattr(Qt, "_" + namespaces[0])
1396 | except AttributeError as e:
1397 | _log("QtCompat: AttributeError: %s" % e)
1398 | # Skip reassignment of non-existing members.
1399 | # This can happen if a request was made to
1400 | # rename a member that didn't exist, for example
1401 | # if QtWidgets isn't available on the target platform.
1402 | continue
1403 |
1404 | # Walk down any remaining namespace getting the object assuming
1405 | # that if the first namespace exists the rest will exist.
1406 | for namespace in namespaces[1:]:
1407 | src_object = getattr(src_object, namespace)
1408 |
1409 | # decorate the Qt method if a decorator was provided.
1410 | if target in decorators.get(classname, []):
1411 | # staticmethod must be called on the decorated method to
1412 | # prevent a TypeError being raised when the decorated method
1413 | # is called.
1414 | src_object = staticmethod(
1415 | decorators[classname][target](src_object))
1416 |
1417 | attrs[target] = src_object
1418 |
1419 | # Create the QtCompat class and install it into the namespace
1420 | compat_class = type(classname, (_QtCompat,), attrs)
1421 | setattr(Qt.QtCompat, classname, compat_class)
1422 |
1423 |
1424 | def _pyside2():
1425 | """Initialise PySide2
1426 |
1427 | These functions serve to test the existence of a binding
1428 | along with set it up in such a way that it aligns with
1429 | the final step; adding members from the original binding
1430 | to Qt.py
1431 |
1432 | """
1433 |
1434 | import PySide2 as module
1435 | extras = ["QtUiTools"]
1436 | try:
1437 | try:
1438 | # Before merge of PySide and shiboken
1439 | import shiboken2
1440 | except ImportError:
1441 | # After merge of PySide and shiboken, May 2017
1442 | from PySide2 import shiboken2
1443 | extras.append("shiboken2")
1444 | except ImportError:
1445 | pass
1446 |
1447 | _setup(module, extras)
1448 | Qt.__binding_version__ = module.__version__
1449 |
1450 | if hasattr(Qt, "_shiboken2"):
1451 | Qt.QtCompat.wrapInstance = _wrapinstance
1452 | Qt.QtCompat.getCppPointer = _getcpppointer
1453 | Qt.QtCompat.delete = shiboken2.delete
1454 |
1455 | if hasattr(Qt, "_QtUiTools"):
1456 | Qt.QtCompat.loadUi = _loadUi
1457 |
1458 | if hasattr(Qt, "_QtCore"):
1459 | Qt.__qt_version__ = Qt._QtCore.qVersion()
1460 | Qt.QtCompat.dataChanged = (
1461 | lambda self, topleft, bottomright, roles=None:
1462 | self.dataChanged.emit(topleft, bottomright, roles or [])
1463 | )
1464 |
1465 | if hasattr(Qt, "_QtWidgets"):
1466 | Qt.QtCompat.setSectionResizeMode = \
1467 | Qt._QtWidgets.QHeaderView.setSectionResizeMode
1468 |
1469 | _reassign_misplaced_members("PySide2")
1470 | _build_compatibility_members("PySide2")
1471 |
1472 |
1473 | def _pyside():
1474 | """Initialise PySide"""
1475 |
1476 | import PySide as module
1477 | extras = ["QtUiTools"]
1478 | try:
1479 | try:
1480 | # Before merge of PySide and shiboken
1481 | import shiboken
1482 | except ImportError:
1483 | # After merge of PySide and shiboken, May 2017
1484 | from PySide import shiboken
1485 | extras.append("shiboken")
1486 | except ImportError:
1487 | pass
1488 |
1489 | _setup(module, extras)
1490 | Qt.__binding_version__ = module.__version__
1491 |
1492 | if hasattr(Qt, "_shiboken"):
1493 | Qt.QtCompat.wrapInstance = _wrapinstance
1494 | Qt.QtCompat.getCppPointer = _getcpppointer
1495 | Qt.QtCompat.delete = shiboken.delete
1496 |
1497 | if hasattr(Qt, "_QtUiTools"):
1498 | Qt.QtCompat.loadUi = _loadUi
1499 |
1500 | if hasattr(Qt, "_QtGui"):
1501 | setattr(Qt, "QtWidgets", _new_module("QtWidgets"))
1502 | setattr(Qt, "_QtWidgets", Qt._QtGui)
1503 | if hasattr(Qt._QtGui, "QX11Info"):
1504 | setattr(Qt, "QtX11Extras", _new_module("QtX11Extras"))
1505 | Qt.QtX11Extras.QX11Info = Qt._QtGui.QX11Info
1506 |
1507 | Qt.QtCompat.setSectionResizeMode = Qt._QtGui.QHeaderView.setResizeMode
1508 |
1509 | if hasattr(Qt, "_QtCore"):
1510 | Qt.__qt_version__ = Qt._QtCore.qVersion()
1511 | Qt.QtCompat.dataChanged = (
1512 | lambda self, topleft, bottomright, roles=None:
1513 | self.dataChanged.emit(topleft, bottomright)
1514 | )
1515 |
1516 | _reassign_misplaced_members("PySide")
1517 | _build_compatibility_members("PySide")
1518 |
1519 |
1520 | def _pyqt5():
1521 | """Initialise PyQt5"""
1522 |
1523 | import PyQt5 as module
1524 | extras = ["uic"]
1525 |
1526 | try:
1527 | # Relevant to PyQt5 5.11 and above
1528 | from PyQt5 import sip
1529 | extras += ["sip"]
1530 | except ImportError:
1531 |
1532 | try:
1533 | import sip
1534 | extras += ["sip"]
1535 | except ImportError:
1536 | sip = None
1537 |
1538 | _setup(module, extras)
1539 | if hasattr(Qt, "_sip"):
1540 | Qt.QtCompat.wrapInstance = _wrapinstance
1541 | Qt.QtCompat.getCppPointer = _getcpppointer
1542 | Qt.QtCompat.delete = sip.delete
1543 |
1544 | if hasattr(Qt, "_uic"):
1545 | Qt.QtCompat.loadUi = _loadUi
1546 |
1547 | if hasattr(Qt, "_QtCore"):
1548 | Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR
1549 | Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR
1550 | Qt.QtCompat.dataChanged = (
1551 | lambda self, topleft, bottomright, roles=None:
1552 | self.dataChanged.emit(topleft, bottomright, roles or [])
1553 | )
1554 |
1555 | if hasattr(Qt, "_QtWidgets"):
1556 | Qt.QtCompat.setSectionResizeMode = \
1557 | Qt._QtWidgets.QHeaderView.setSectionResizeMode
1558 |
1559 | _reassign_misplaced_members("PyQt5")
1560 | _build_compatibility_members('PyQt5')
1561 |
1562 |
1563 | def _pyqt4():
1564 | """Initialise PyQt4"""
1565 |
1566 | import sip
1567 |
1568 | # Validation of envivornment variable. Prevents an error if
1569 | # the variable is invalid since it's just a hint.
1570 | try:
1571 | hint = int(QT_SIP_API_HINT)
1572 | except TypeError:
1573 | hint = None # Variable was None, i.e. not set.
1574 | except ValueError:
1575 | raise ImportError("QT_SIP_API_HINT=%s must be a 1 or 2")
1576 |
1577 | for api in ("QString",
1578 | "QVariant",
1579 | "QDate",
1580 | "QDateTime",
1581 | "QTextStream",
1582 | "QTime",
1583 | "QUrl"):
1584 | try:
1585 | sip.setapi(api, hint or 2)
1586 | except AttributeError:
1587 | raise ImportError("PyQt4 < 4.6 isn't supported by Qt.py")
1588 | except ValueError:
1589 | actual = sip.getapi(api)
1590 | if not hint:
1591 | raise ImportError("API version already set to %d" % actual)
1592 | else:
1593 | # Having provided a hint indicates a soft constraint, one
1594 | # that doesn't throw an exception.
1595 | sys.stderr.write(
1596 | "Warning: API '%s' has already been set to %d.\n"
1597 | % (api, actual)
1598 | )
1599 |
1600 | import PyQt4 as module
1601 | extras = ["uic"]
1602 | try:
1603 | import sip
1604 | extras.append(sip.__name__)
1605 | except ImportError:
1606 | sip = None
1607 |
1608 | _setup(module, extras)
1609 | if hasattr(Qt, "_sip"):
1610 | Qt.QtCompat.wrapInstance = _wrapinstance
1611 | Qt.QtCompat.getCppPointer = _getcpppointer
1612 | Qt.QtCompat.delete = sip.delete
1613 |
1614 | if hasattr(Qt, "_uic"):
1615 | Qt.QtCompat.loadUi = _loadUi
1616 |
1617 | if hasattr(Qt, "_QtGui"):
1618 | setattr(Qt, "QtWidgets", _new_module("QtWidgets"))
1619 | setattr(Qt, "_QtWidgets", Qt._QtGui)
1620 | if hasattr(Qt._QtGui, "QX11Info"):
1621 | setattr(Qt, "QtX11Extras", _new_module("QtX11Extras"))
1622 | Qt.QtX11Extras.QX11Info = Qt._QtGui.QX11Info
1623 |
1624 | Qt.QtCompat.setSectionResizeMode = \
1625 | Qt._QtGui.QHeaderView.setResizeMode
1626 |
1627 | if hasattr(Qt, "_QtCore"):
1628 | Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR
1629 | Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR
1630 | Qt.QtCompat.dataChanged = (
1631 | lambda self, topleft, bottomright, roles=None:
1632 | self.dataChanged.emit(topleft, bottomright)
1633 | )
1634 |
1635 | _reassign_misplaced_members("PyQt4")
1636 |
1637 | # QFileDialog QtCompat decorator
1638 | def _standardizeQFileDialog(some_function):
1639 | """Decorator that makes PyQt4 return conform to other bindings"""
1640 | def wrapper(*args, **kwargs):
1641 | ret = (some_function(*args, **kwargs))
1642 |
1643 | # PyQt4 only returns the selected filename, force it to a
1644 | # standard return of the selected filename, and a empty string
1645 | # for the selected filter
1646 | return ret, ''
1647 |
1648 | wrapper.__doc__ = some_function.__doc__
1649 | wrapper.__name__ = some_function.__name__
1650 |
1651 | return wrapper
1652 |
1653 | decorators = {
1654 | "QFileDialog": {
1655 | "getOpenFileName": _standardizeQFileDialog,
1656 | "getOpenFileNames": _standardizeQFileDialog,
1657 | "getSaveFileName": _standardizeQFileDialog,
1658 | }
1659 | }
1660 | _build_compatibility_members('PyQt4', decorators)
1661 |
1662 |
1663 | def _none():
1664 | """Internal option (used in installer)"""
1665 |
1666 | Mock = type("Mock", (), {"__getattr__": lambda Qt, attr: None})
1667 |
1668 | Qt.__binding__ = "None"
1669 | Qt.__qt_version__ = "0.0.0"
1670 | Qt.__binding_version__ = "0.0.0"
1671 | Qt.QtCompat.loadUi = lambda uifile, baseinstance=None: None
1672 | Qt.QtCompat.setSectionResizeMode = lambda *args, **kwargs: None
1673 |
1674 | for submodule in _common_members.keys():
1675 | setattr(Qt, submodule, Mock())
1676 | setattr(Qt, "_" + submodule, Mock())
1677 |
1678 |
1679 | def _log(text):
1680 | if QT_VERBOSE:
1681 | sys.stdout.write("Qt.py [info]: %s\n" % text)
1682 |
1683 |
1684 | def _warn(text):
1685 | sys.stderr.write("Qt.py [warning]: %s\n" % text)
1686 |
1687 |
1688 | def _convert(lines):
1689 | """Convert compiled .ui file from PySide2 to Qt.py
1690 |
1691 | Arguments:
1692 | lines (list): Each line of of .ui file
1693 |
1694 | Usage:
1695 | >> with open("myui.py") as f:
1696 | .. lines = _convert(f.readlines())
1697 |
1698 | """
1699 |
1700 | def parse(line):
1701 | line = line.replace("from PySide2 import", "from Qt import QtCompat,")
1702 | line = line.replace("QtWidgets.QApplication.translate",
1703 | "QtCompat.translate")
1704 | if "QtCore.SIGNAL" in line:
1705 | raise NotImplementedError("QtCore.SIGNAL is missing from PyQt5 "
1706 | "and so Qt.py does not support it: you "
1707 | "should avoid defining signals inside "
1708 | "your ui files.")
1709 | return line
1710 |
1711 | parsed = list()
1712 | for line in lines:
1713 | line = parse(line)
1714 | parsed.append(line)
1715 |
1716 | return parsed
1717 |
1718 |
1719 | def _cli(args):
1720 | """Qt.py command-line interface"""
1721 | import argparse
1722 |
1723 | parser = argparse.ArgumentParser()
1724 | parser.add_argument("--convert",
1725 | help="Path to compiled Python module, e.g. my_ui.py")
1726 | parser.add_argument("--compile",
1727 | help="Accept raw .ui file and compile with native "
1728 | "PySide2 compiler.")
1729 | parser.add_argument("--stdout",
1730 | help="Write to stdout instead of file",
1731 | action="store_true")
1732 | parser.add_argument("--stdin",
1733 | help="Read from stdin instead of file",
1734 | action="store_true")
1735 |
1736 | args = parser.parse_args(args)
1737 |
1738 | if args.stdout:
1739 | raise NotImplementedError("--stdout")
1740 |
1741 | if args.stdin:
1742 | raise NotImplementedError("--stdin")
1743 |
1744 | if args.compile:
1745 | raise NotImplementedError("--compile")
1746 |
1747 | if args.convert:
1748 | sys.stdout.write("#\n"
1749 | "# WARNING: --convert is an ALPHA feature.\n#\n"
1750 | "# See https://github.com/mottosso/Qt.py/pull/132\n"
1751 | "# for details.\n"
1752 | "#\n")
1753 |
1754 | #
1755 | # ------> Read
1756 | #
1757 | with open(args.convert) as f:
1758 | lines = _convert(f.readlines())
1759 |
1760 | backup = "%s_backup%s" % os.path.splitext(args.convert)
1761 | sys.stdout.write("Creating \"%s\"..\n" % backup)
1762 | shutil.copy(args.convert, backup)
1763 |
1764 | #
1765 | # <------ Write
1766 | #
1767 | with open(args.convert, "w") as f:
1768 | f.write("".join(lines))
1769 |
1770 | sys.stdout.write("Successfully converted \"%s\"\n" % args.convert)
1771 |
1772 |
1773 | class MissingMember(object):
1774 | """
1775 | A placeholder type for a missing Qt object not
1776 | included in Qt.py
1777 |
1778 | Args:
1779 | name (str): The name of the missing type
1780 | details (str): An optional custom error message
1781 | """
1782 | ERR_TMPL = ("{} is not a common object across PySide2 "
1783 | "and the other Qt bindings. It is not included "
1784 | "as a common member in the Qt.py layer")
1785 |
1786 | def __init__(self, name, details=''):
1787 | self.__name = name
1788 | self.__err = self.ERR_TMPL.format(name)
1789 |
1790 | if details:
1791 | self.__err = "{}: {}".format(self.__err, details)
1792 |
1793 | def __repr__(self):
1794 | return "<{}: {}>".format(self.__class__.__name__, self.__name)
1795 |
1796 | def __getattr__(self, name):
1797 | raise NotImplementedError(self.__err)
1798 |
1799 | def __call__(self, *a, **kw):
1800 | raise NotImplementedError(self.__err)
1801 |
1802 |
1803 | def _install():
1804 | # Default order (customize order and content via QT_PREFERRED_BINDING)
1805 | default_order = ("PySide2", "PyQt5", "PySide", "PyQt4")
1806 | preferred_order = None
1807 | if QT_PREFERRED_BINDING_JSON:
1808 | # A per-vendor preferred binding customization was defined
1809 | # This should be a dictionary of the full Qt.py module namespace to
1810 | # apply binding settings to. The "default" key can be used to apply
1811 | # custom bindings to all modules not explicitly defined. If the json
1812 | # data is invalid this will raise a exception.
1813 | # Example:
1814 | # {"mylibrary.vendor.Qt": ["PySide2"], "default":["PyQt5","PyQt4"]}
1815 | try:
1816 | preferred_bindings = json.loads(QT_PREFERRED_BINDING_JSON)
1817 | except ValueError:
1818 | # Python 2 raises ValueError, Python 3 raises json.JSONDecodeError
1819 | # a subclass of ValueError
1820 | _warn("Failed to parse QT_PREFERRED_BINDING_JSON='%s'"
1821 | % QT_PREFERRED_BINDING_JSON)
1822 | _warn("Falling back to default preferred order")
1823 | else:
1824 | preferred_order = preferred_bindings.get(__name__)
1825 | # If no matching binding was used, optionally apply a default.
1826 | if preferred_order is None:
1827 | preferred_order = preferred_bindings.get("default", None)
1828 | if preferred_order is None:
1829 | # If a json preferred binding was not used use, respect the
1830 | # QT_PREFERRED_BINDING environment variable if defined.
1831 | preferred_order = list(
1832 | b for b in QT_PREFERRED_BINDING.split(os.pathsep) if b
1833 | )
1834 |
1835 | order = preferred_order or default_order
1836 |
1837 | available = {
1838 | "PySide2": _pyside2,
1839 | "PyQt5": _pyqt5,
1840 | "PySide": _pyside,
1841 | "PyQt4": _pyqt4,
1842 | "None": _none
1843 | }
1844 |
1845 | _log("Order: '%s'" % "', '".join(order))
1846 |
1847 | # Allow site-level customization of the available modules.
1848 | _apply_site_config()
1849 |
1850 | found_binding = False
1851 | for name in order:
1852 | _log("Trying %s" % name)
1853 |
1854 | try:
1855 | available[name]()
1856 | found_binding = True
1857 | break
1858 |
1859 | except ImportError as e:
1860 | _log("ImportError: %s" % e)
1861 |
1862 | except KeyError:
1863 | _log("ImportError: Preferred binding '%s' not found." % name)
1864 |
1865 | if not found_binding:
1866 | # If not binding were found, throw this error
1867 | raise ImportError("No Qt binding were found.")
1868 |
1869 | # Install individual members
1870 | for name, members in _common_members.items():
1871 | try:
1872 | their_submodule = getattr(Qt, "_%s" % name)
1873 | except AttributeError:
1874 | continue
1875 |
1876 | our_submodule = getattr(Qt, name)
1877 |
1878 | # Enable import *
1879 | __all__.append(name)
1880 |
1881 | # Enable direct import of submodule,
1882 | # e.g. import Qt.QtCore
1883 | sys.modules[__name__ + "." + name] = our_submodule
1884 |
1885 | for member in members:
1886 | # Accept that a submodule may miss certain members.
1887 | try:
1888 | their_member = getattr(their_submodule, member)
1889 | except AttributeError:
1890 | _log("'%s.%s' was missing." % (name, member))
1891 | continue
1892 |
1893 | setattr(our_submodule, member, their_member)
1894 |
1895 | # Install missing member placeholders
1896 | for name, members in _missing_members.items():
1897 | our_submodule = getattr(Qt, name)
1898 |
1899 | for member in members:
1900 |
1901 | # If the submodule already has this member installed,
1902 | # either by the common members, or the site config,
1903 | # then skip installing this one over it.
1904 | if hasattr(our_submodule, member):
1905 | continue
1906 |
1907 | placeholder = MissingMember("{}.{}".format(name, member),
1908 | details=members[member])
1909 | setattr(our_submodule, member, placeholder)
1910 |
1911 | # Enable direct import of QtCompat
1912 | sys.modules[__name__ + ".QtCompat"] = Qt.QtCompat
1913 |
1914 | # Backwards compatibility
1915 | if hasattr(Qt.QtCompat, 'loadUi'):
1916 | Qt.QtCompat.load_ui = Qt.QtCompat.loadUi
1917 |
1918 |
1919 | _install()
1920 |
1921 | # Setup Binding Enum states
1922 | Qt.IsPySide2 = Qt.__binding__ == 'PySide2'
1923 | Qt.IsPyQt5 = Qt.__binding__ == 'PyQt5'
1924 | Qt.IsPySide = Qt.__binding__ == 'PySide'
1925 | Qt.IsPyQt4 = Qt.__binding__ == 'PyQt4'
1926 |
1927 | """Augment QtCompat
1928 |
1929 | QtCompat contains wrappers and added functionality
1930 | to the original bindings, such as the CLI interface
1931 | and otherwise incompatible members between bindings,
1932 | such as `QHeaderView.setSectionResizeMode`.
1933 |
1934 | """
1935 |
1936 | Qt.QtCompat._cli = _cli
1937 | Qt.QtCompat._convert = _convert
1938 |
1939 | # Enable command-line interface
1940 | if __name__ == "__main__":
1941 | _cli(sys.argv[1:])
1942 |
1943 |
1944 | # The MIT License (MIT)
1945 | #
1946 | # Copyright (c) 2016-2017 Marcus Ottosson
1947 | #
1948 | # Permission is hereby granted, free of charge, to any person obtaining a copy
1949 | # of this software and associated documentation files (the "Software"), to deal
1950 | # in the Software without restriction, including without limitation the rights
1951 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1952 | # copies of the Software, and to permit persons to whom the Software is
1953 | # furnished to do so, subject to the following conditions:
1954 | #
1955 | # The above copyright notice and this permission notice shall be included in
1956 | # all copies or substantial portions of the Software.
1957 | #
1958 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1959 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1960 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1961 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1963 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1964 | # SOFTWARE.
1965 | #
1966 | # In PySide(2), loadUi does not exist, so we implement it
1967 | #
1968 | # `_UiLoader` is adapted from the qtpy project, which was further influenced
1969 | # by qt-helpers which was released under a 3-clause BSD license which in turn
1970 | # is based on a solution at:
1971 | #
1972 | # - https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8
1973 | #
1974 | # The License for this code is as follows:
1975 | #
1976 | # qt-helpers - a common front-end to various Qt modules
1977 | #
1978 | # Copyright (c) 2015, Chris Beaumont and Thomas Robitaille
1979 | #
1980 | # All rights reserved.
1981 | #
1982 | # Redistribution and use in source and binary forms, with or without
1983 | # modification, are permitted provided that the following conditions are
1984 | # met:
1985 | #
1986 | # * Redistributions of source code must retain the above copyright
1987 | # notice, this list of conditions and the following disclaimer.
1988 | # * Redistributions in binary form must reproduce the above copyright
1989 | # notice, this list of conditions and the following disclaimer in the
1990 | # documentation and/or other materials provided with the
1991 | # distribution.
1992 | # * Neither the name of the Glue project nor the names of its contributors
1993 | # may be used to endorse or promote products derived from this software
1994 | # without specific prior written permission.
1995 | #
1996 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
1997 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
1998 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1999 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
2000 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2001 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2002 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2003 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2004 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2005 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2006 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2007 | #
2008 | # Which itself was based on the solution at
2009 | #
2010 | # https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8
2011 | #
2012 | # which was released under the MIT license:
2013 | #
2014 | # Copyright (c) 2011 Sebastian Wiesner
2015 | # Modifications by Charl Botha
2016 | #
2017 | # Permission is hereby granted, free of charge, to any person obtaining a
2018 | # copy of this software and associated documentation files
2019 | # (the "Software"),to deal in the Software without restriction,
2020 | # including without limitation
2021 | # the rights to use, copy, modify, merge, publish, distribute, sublicense,
2022 | # and/or sell copies of the Software, and to permit persons to whom the
2023 | # Software is furnished to do so, subject to the following conditions:
2024 | #
2025 | # The above copyright notice and this permission notice shall be included
2026 | # in all copies or substantial portions of the Software.
2027 | #
2028 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
2029 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2030 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2031 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2032 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2033 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2034 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2035 |
--------------------------------------------------------------------------------