[PATCH 01 of 14] qtcompat: fully qualify QStyleOptionViewItem enums

43 views
Skip to first unread message

Matt Harbison

unread,
Apr 7, 2022, 8:14:13 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649271699 14400
# Wed Apr 06 15:01:39 2022 -0400
# Node ID 296df1d2a620c6c882a8f6430c383bc7ee618dcd
# Parent 8650f93868f73299cbb483fbbf55c0f22e50bcad
# EXP-Topic pyqt6
qtcompat: fully qualify QStyleOptionViewItem enums

diff --git a/tortoisehg/hgqt/repoview.py b/tortoisehg/hgqt/repoview.py
--- a/tortoisehg/hgqt/repoview.py
+++ b/tortoisehg/hgqt/repoview.py
@@ -818,7 +818,7 @@
lay = self._makeLabelsLayout(labels, option)
option.decorationSize = QSize(lay.width(), lay.height())
if isinstance(option, QStyleOptionViewItemV2):
- option.features |= QStyleOptionViewItemV2.HasDecoration
+ option.features |= QStyleOptionViewItemV2.ViewItemFeature.HasDecoration

def paint(self, painter, option, index):
super(LabeledDelegate, self).paint(painter, option, index)

Matt Harbison

unread,
Apr 7, 2022, 8:14:14 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649265077 14400
# Wed Apr 06 13:11:17 2022 -0400
# Node ID d34e7e5cbc3b60dd0d23902dc2b4c9c7c9bc63f5
# Parent 296df1d2a620c6c882a8f6430c383bc7ee618dcd
# EXP-Topic pyqt6
qtcompat: drop obsolete aliases for QStyleOptionViewItem

For whatever reason, pytype complained about these in a PyQt6 conditional in the
qtgui module. They were removed from Qt5, and I don't feel like investigating.

diff --git a/tortoisehg/hgqt/htmldelegate.py b/tortoisehg/hgqt/htmldelegate.py
--- a/tortoisehg/hgqt/htmldelegate.py
+++ b/tortoisehg/hgqt/htmldelegate.py
@@ -15,7 +15,7 @@
QAbstractTextDocumentLayout,
QPalette,
QStyle,
- QStyleOptionViewItemV4,
+ QStyleOptionViewItem,
QStyledItemDelegate,
QTextDocument,
)
@@ -24,7 +24,7 @@

def paint(self, painter, option, index):
# draw selection
- option = QStyleOptionViewItemV4(option)
+ option = QStyleOptionViewItem(option)
self.parent().style().drawControl(QStyle.ControlElement.CE_ItemViewItem, option, painter)

# draw text
diff --git a/tortoisehg/hgqt/qtgui.py b/tortoisehg/hgqt/qtgui.py
--- a/tortoisehg/hgqt/qtgui.py
+++ b/tortoisehg/hgqt/qtgui.py
@@ -14,8 +14,5 @@
if QT_API == 'PyQt5':
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
- QStyleOptionViewItemV2 = QStyleOptionViewItem
- QStyleOptionViewItemV3 = QStyleOptionViewItem
- QStyleOptionViewItemV4 = QStyleOptionViewItem
else:
raise RuntimeError('unsupported Qt API: %s' % QT_API)
diff --git a/tortoisehg/hgqt/repoview.py b/tortoisehg/hgqt/repoview.py
--- a/tortoisehg/hgqt/repoview.py
+++ b/tortoisehg/hgqt/repoview.py
@@ -55,8 +55,7 @@
QProxyStyle,
QSpinBox,
QStyle,
- QStyleOptionViewItemV2,
- QStyleOptionViewItemV4,
+ QStyleOptionViewItem,
QStyledItemDelegate,
QTableView,
)
@@ -817,8 +816,8 @@
return
lay = self._makeLabelsLayout(labels, option)
option.decorationSize = QSize(lay.width(), lay.height())
- if isinstance(option, QStyleOptionViewItemV2):
- option.features |= QStyleOptionViewItemV2.ViewItemFeature.HasDecoration
+ if isinstance(option, QStyleOptionViewItem):
+ option.features |= QStyleOptionViewItem.ViewItemFeature.HasDecoration

def paint(self, painter, option, index):
super(LabeledDelegate, self).paint(painter, option, index)
@@ -826,7 +825,7 @@
if not labels:
return

- option = QStyleOptionViewItemV4(option)
+ option = QStyleOptionViewItem(option)
self.initStyleOption(option, index)
if option.widget:
style = option.widget.style()
@@ -848,7 +847,7 @@
size = super(LabeledDelegate, self).sizeHint(option, index)
# give additional margins for each row (even if it has no labels
# because all rows must have the same height)
- option = QStyleOptionViewItemV4(option)
+ option = QStyleOptionViewItem(option)
self.initStyleOption(option, index)
lay = self._makeLabelsLayout([], option)
return QSize(size.width(), max(size.height(), lay.height() + 2))

Matt Harbison

unread,
Apr 7, 2022, 8:14:15 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1648830684 14400
# Fri Apr 01 12:31:24 2022 -0400
# Node ID bf9d055265735411ecd7de6d54da16da9e8b47e0
# Parent d34e7e5cbc3b60dd0d23902dc2b4c9c7c9bc63f5
# EXP-Topic pyqt6
qtcompat: add support for PyQt6

The changes to `setup.py` are likely incomplete. Where PyQt4 was previously
referenced, PyQt6 has been swapped in. Support for PyQt4 was dropped in
261da91d3495, and these were left as a search convenience to find the places to
update.

diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -346,7 +346,8 @@
def _findrcc(self):
from tortoisehg.hgqt.qtcore import QT_API
try:
- rcc = {'PyQt4': 'pyrcc4', 'PyQt5': 'pyrcc5'}[QT_API]
+ # TODO: Figure out how to handle the removal of pyrcc
+ rcc = {'PyQt5': 'pyrcc5'}[QT_API]
except KeyError:
raise RuntimeError('unsupported Qt API: %s' % QT_API)
if os.name != 'nt' or QT_API == 'PyQt5':
@@ -406,16 +407,10 @@
def _build_translations(self, basepath):
"""Build translations_rc.py which inclues qt_xx.qm"""
from tortoisehg.hgqt.qtcore import QT_API
- if QT_API == 'PyQt4':
- if os.name == 'nt':
- import PyQt4
- trpath = os.path.join(
- os.path.dirname(PyQt4.__file__), 'translations')
- else:
- from PyQt4.QtCore import QLibraryInfo
- # TODO: use QLibraryInfo.LibraryPath.TranslationsPath with PyQt6
- trpath = unicode(
- QLibraryInfo.location(QLibraryInfo.TranslationsPath))
+ if QT_API == 'PyQt6':
+ from PyQt6.QtCore import QLibraryInfo
+ trpath = unicode(
+ QLibraryInfo.location(QLibraryInfo.LibraryPath.TranslationsPath))
else:
if os.name == 'nt' and sys.version_info[0] == 2:
import PyQt5
@@ -569,12 +564,15 @@
if f.endswith('.ico') or f == 'README.txt']))

# for PyQt, see http://www.py2exe.org/index.cgi/Py2exeAndPyQt
- includes = ['PyQt5.sip']
+ if QT_API == 'PyQt6':
+ includes = ['PyQt6.sip']
+ else:
+ includes = ['PyQt5.sip']

# Qt4 plugins, see http://stackoverflow.com/questions/2206406/
- def qt4_plugins(subdir, *dlls):
- import PyQt4
- pluginsdir = os.path.join(os.path.dirname(PyQt4.__file__), 'plugins')
+ def qt6_plugins(subdir, *dlls):
+ from PyQt6.QtCore import QLibraryInfo
+ pluginsdir = QLibraryInfo.location(QLibraryInfo.LibraryPath.PluginsPath)
return subdir, [os.path.join(pluginsdir, subdir, e) for e in dlls]

def qt5_plugins(subdir, *dlls):
@@ -589,9 +587,12 @@
return subdir, [os.path.join(pluginsdir, subdir, e) for e in dlls]

from tortoisehg.hgqt.qtcore import QT_API
- if QT_API == 'PyQt4':
- _data_files.append(qt4_plugins('imageformats',
- 'qico4.dll', 'qsvg4.dll'))
+ if QT_API == 'PyQt6':
+ _data_files.append(qt6_plugins('platforms', 'qwindows.dll'))
+ _data_files.append(qt6_plugins('imageformats',
+ 'qico.dll', 'qsvg.dll', 'qjpeg.dll',
+ 'qgif.dll', 'qicns.dll', 'qtga.dll',
+ 'qtiff.dll', 'qwbmp.dll', 'qwebp.dll'))
else:
_data_files.append(qt5_plugins('platforms', 'qwindows.dll'))
_data_files.append(qt5_plugins('imageformats',
@@ -670,10 +671,19 @@
# PyQt 5.13.2/Qt 5.9.9
pluginsdir = QLibraryInfo.location(QLibraryInfo.PluginsPath)
return subdir, [os.path.join(pluginsdir, subdir, e) for e in dlls]
+ def qt6_plugins(subdir, *dlls):
+ from PyQt6.QtCore import QLibraryInfo
+ pluginsdir = QLibraryInfo.location(QLibraryInfo.LibraryPath.PluginsPath)
+ return subdir, [os.path.join(pluginsdir, subdir, e) for e in dlls]
+

from tortoisehg.hgqt.qtcore import QT_API
- _data_files.append(qt5_plugins('platforms', 'libqcocoa.dylib'))
- _data_files.append(qt5_plugins('imageformats', 'libqsvg.dylib'))
+ if QT_API == 'PyQt6':
+ _data_files.append(qt6_plugins('platforms', 'libqcocoa.dylib'))
+ _data_files.append(qt6_plugins('imageformats', 'libqsvg.dylib'))
+ else:
+ _data_files.append(qt5_plugins('platforms', 'libqcocoa.dylib'))
+ _data_files.append(qt5_plugins('imageformats', 'libqsvg.dylib'))

_py2app_options = {
'arch': 'x86_64',
diff --git a/tortoisehg/hgqt/qsci.py b/tortoisehg/hgqt/qsci.py
--- a/tortoisehg/hgqt/qsci.py
+++ b/tortoisehg/hgqt/qsci.py
@@ -1,4 +1,4 @@
-# qsci.py - PyQt4/5 compatibility wrapper
+# qsci.py - PyQt5/6 compatibility wrapper
#
# Copyright 2015 Yuya Nishihara <yu...@tcha.org>
#
@@ -11,9 +11,9 @@

from .qtcore import QT_API

-if QT_API == 'PyQt4':
- from PyQt4.Qsci import * # pytype: disable=import-error
+if QT_API == 'PyQt6':
+ from PyQt6.Qsci import * # pytype: disable=import-error
elif QT_API == 'PyQt5':
- from PyQt5.Qsci import *
+ from PyQt5.Qsci import * # pytype: disable=import-error
else:
raise RuntimeError('unsupported Qt API: %s' % QT_API)
diff --git a/tortoisehg/hgqt/qtcore.py b/tortoisehg/hgqt/qtcore.py
--- a/tortoisehg/hgqt/qtcore.py
+++ b/tortoisehg/hgqt/qtcore.py
@@ -1,4 +1,4 @@
-# qtcore.py - PyQt4/5 compatibility wrapper
+# qtcore.py - PyQt5/6 compatibility wrapper
#
# Copyright 2015 Yuya Nishihara <yu...@tcha.org>
#
@@ -13,7 +13,7 @@
import sys

def _detectapi():
- candidates = ['PyQt5']
+ candidates = ['PyQt5', 'PyQt6']
if not getattr(sys, 'frozen', False):
api = os.environ.get('THG_QT_API')
if api:
@@ -32,7 +32,9 @@
except (AttributeError, ImportError):
QT_API = _detectapi()

-if QT_API == 'PyQt5':
- from PyQt5.QtCore import *
+if QT_API == 'PyQt6':
+ from PyQt6.QtCore import * # pytype: disable=import-error
+elif QT_API == 'PyQt5':
+ from PyQt5.QtCore import * # pytype: disable=import-error
else:
raise RuntimeError('unsupported Qt API: %s' % QT_API)
diff --git a/tortoisehg/hgqt/qtgui.py b/tortoisehg/hgqt/qtgui.py
--- a/tortoisehg/hgqt/qtgui.py
+++ b/tortoisehg/hgqt/qtgui.py
@@ -1,18 +1,21 @@
-# qtgui.py - PyQt4/5 compatibility wrapper
+# qtgui.py - PyQt5/6 compatibility wrapper
#
# Copyright 2015 Yuya Nishihara <yu...@tcha.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

-"""Thin compatibility wrapper for QtGui and QtWidgets of Qt5"""
+"""Thin compatibility wrapper for QtGui and QtWidgets of Qt5/6"""

from __future__ import absolute_import

from .qtcore import QT_API

-if QT_API == 'PyQt5':
- from PyQt5.QtGui import *
- from PyQt5.QtWidgets import *
+if QT_API == 'PyQt6':
+ from PyQt6.QtGui import * # pytype: disable=import-error
+ from PyQt6.QtWidgets import * # pytype: disable=import-error
+elif QT_API == 'PyQt5':
+ from PyQt5.QtGui import * # pytype: disable=import-error
+ from PyQt5.QtWidgets import * # pytype: disable=import-error
else:
raise RuntimeError('unsupported Qt API: %s' % QT_API)
diff --git a/tortoisehg/hgqt/qtlib.py b/tortoisehg/hgqt/qtlib.py
--- a/tortoisehg/hgqt/qtlib.py
+++ b/tortoisehg/hgqt/qtlib.py
@@ -12,7 +12,6 @@
import posixpath
import re
import shutil
-import sip
import stat
import subprocess
import sys
@@ -30,6 +29,7 @@
QSize,
QUrl,
QT_VERSION,
+ QT_API,
Qt,
pyqtSignal,
pyqtSlot,
@@ -63,6 +63,11 @@
QWidget,
)

+if QT_API == "PyQt6":
+ import PyQt6.sip as sip # pytype: disable=import-error
+else:
+ import sip # pytype: disable=import-error
+
from mercurial import (
color,
encoding,
diff --git a/tortoisehg/hgqt/qtnetwork.py b/tortoisehg/hgqt/qtnetwork.py
--- a/tortoisehg/hgqt/qtnetwork.py
+++ b/tortoisehg/hgqt/qtnetwork.py
@@ -1,4 +1,4 @@
-# qtnetwork.py - PyQt4/5 compatibility wrapper
+# qtnetwork.py - PyQt5/6 compatibility wrapper
#
# Copyright 2015 Yuya Nishihara <yu...@tcha.org>
#
@@ -11,9 +11,9 @@

from .qtcore import QT_API

-if QT_API == 'PyQt4':
- from PyQt4.QtNetwork import * # pytype: disable=import-error
+if QT_API == 'PyQt6':
+ from PyQt6.QtNetwork import * # pytype: disable=import-error
elif QT_API == 'PyQt5':
- from PyQt5.QtNetwork import *
+ from PyQt5.QtNetwork import * # pytype: disable=import-error

Matt Harbison

unread,
Apr 7, 2022, 8:14:15 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649270104 14400
# Wed Apr 06 14:35:04 2022 -0400
# Node ID 4aad8d6d58729ff89ce632c0ecc25f8386e1be7f
# Parent bf9d055265735411ecd7de6d54da16da9e8b47e0
# EXP-Topic pyqt6
qtcompat: obtain the screen size in a PyQt6 compatible manner

This started as dropping `qApp` (which was removed in Qt6), but also the
`QApplication::desktop()` method was removed.

diff --git a/tortoisehg/hgqt/bugreport.py b/tortoisehg/hgqt/bugreport.py
--- a/tortoisehg/hgqt/bugreport.py
+++ b/tortoisehg/hgqt/bugreport.py
@@ -247,7 +247,7 @@
bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, centerButtons=True)
bb.rejected.connect(self.reject)
self.layout().addWidget(bb)
- desktopgeom = qApp.desktop().availableGeometry()
+ desktopgeom = QApplication.primaryScreen().availableGeometry()
self.resize(desktopgeom.size() * 0.20)

@pyqtSlot(str)
diff --git a/tortoisehg/hgqt/purge.py b/tortoisehg/hgqt/purge.py
--- a/tortoisehg/hgqt/purge.py
+++ b/tortoisehg/hgqt/purge.py
@@ -20,11 +20,11 @@
pyqtSlot,
)
from .qtgui import (
+ QApplication,
QCheckBox,
QDialog,
QDialogButtonBox,
QVBoxLayout,
- qApp,
)

from hgext.largefiles import (
@@ -108,7 +108,7 @@
self.bb.setEnabled(False)
self.progress.emit(*cmdui.startProgress(_('Checking'), '...'))
s = QSettings()
- desktopgeom = qApp.desktop().availableGeometry()
+ desktopgeom = QApplication.primaryScreen().availableGeometry()
self.resize(desktopgeom.size() * 0.25)
self.restoreGeometry(qtlib.readByteArray(s, 'purge/geom'))

diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
--- a/tortoisehg/hgqt/qscilib.py
+++ b/tortoisehg/hgqt/qscilib.py
@@ -35,6 +35,7 @@
)
from .qtgui import (
QAction,
+ QApplication,
QCheckBox,
QDialog,
QDialogButtonBox,
@@ -45,7 +46,6 @@
QMenu,
QToolBar,
QVBoxLayout,
- qApp,
)

from ..util import hglib
@@ -851,7 +851,7 @@

s = QSettings()
geomname = 'editor-geom'
- desktopgeom = qApp.desktop().availableGeometry()
+ desktopgeom = QApplication.primaryScreen().availableGeometry()
dialog.resize(desktopgeom.size() * 0.5)
dialog.restoreGeometry(qtlib.readByteArray(s, geomname))

diff --git a/tortoisehg/hgqt/workbench.py b/tortoisehg/hgqt/workbench.py
--- a/tortoisehg/hgqt/workbench.py
+++ b/tortoisehg/hgqt/workbench.py
@@ -33,7 +33,6 @@
QShortcut,
QSizePolicy,
QToolBar,
- qApp,
)

from mercurial import (
@@ -122,7 +121,7 @@
lambda self, dlgmeth: dlgmeth(self), parent=self)

def setupUi(self):
- desktopgeom = qApp.desktop().availableGeometry()
+ desktopgeom = QApplication.primaryScreen().availableGeometry()
self.resize(desktopgeom.size() * 0.8)

self.repoTabsWidget = tw = repotab.RepoTabWidget(

Matt Harbison

unread,
Apr 7, 2022, 8:14:16 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649270155 14400
# Wed Apr 06 14:35:55 2022 -0400
# Node ID 226b62a16c487c089178414d3ff3a5bdbd805531
# Parent 4aad8d6d58729ff89ce632c0ecc25f8386e1be7f
# EXP-Topic pyqt6
qtcompat: drop `qApp` references

This was apparently removed in PyQt6.

diff --git a/tortoisehg/hgqt/bugreport.py b/tortoisehg/hgqt/bugreport.py
--- a/tortoisehg/hgqt/bugreport.py
+++ b/tortoisehg/hgqt/bugreport.py
@@ -29,7 +29,6 @@
QTextBrowser,
QTextOption,
QVBoxLayout,
- qApp,
)

from mercurial import (
@@ -101,7 +100,7 @@
bb.button(QDialogButtonBox.StandardButton.Save).clicked.connect(self.save)
bb.button(QDialogButtonBox.StandardButton.Ok).setDefault(True)
bb.addButton(_('Copy'), QDialogButtonBox.ButtonRole.HelpRole).clicked.connect(self.copyText)
- bb.addButton(_('Quit'), QDialogButtonBox.ButtonRole.DestructiveRole).clicked.connect(qApp.quit)
+ bb.addButton(_('Quit'), QDialogButtonBox.ButtonRole.DestructiveRole).clicked.connect(QApplication.instance().quit)
layout.addWidget(bb)

self.setWindowTitle(_('TortoiseHg Bug Report'))

Matt Harbison

unread,
Apr 7, 2022, 8:14:17 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649272943 14400
# Wed Apr 06 15:22:23 2022 -0400
# Node ID 378b7e88f6e2acee6291097aac45c3804e7da802
# Parent 226b62a16c487c089178414d3ff3a5bdbd805531
# EXP-Topic pyqt6
qtcompat: don't set Qt.AA_UseHighDpiPixmaps on PyQt6

This doesn't exist, because it's on by default and can't be disabled.

diff --git a/tortoisehg/hgqt/qtapp.py b/tortoisehg/hgqt/qtapp.py
--- a/tortoisehg/hgqt/qtapp.py
+++ b/tortoisehg/hgqt/qtapp.py
@@ -429,7 +429,10 @@
self._config = hgconfig.HgConfig(ui)
self._mainapp = QApplication(sys.argv)

- self._mainapp.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
+ if QT_API == "PyQt5":
+ # pytype: disable=attribute-error
+ self._mainapp.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
+ # pytype: enable=attribute-error
if sys.platform == 'darwin':
self._mainapp.setAttribute(Qt.ApplicationAttribute.AA_DontShowIconsInMenus, True)

Matt Harbison

unread,
Apr 7, 2022, 8:14:18 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649348816 14400
# Thu Apr 07 12:26:56 2022 -0400
# Node ID 3ac79270239e96b5a2c1feb8709454d64faba904
# Parent 1b979df0ca7372b3c12badb9517f3d31539cb78b
# EXP-Topic pyqt6
qtcompat: rename `Qt.TextColorRole` to `Qt.ItemDataRole.ForegroundRole`

The former was removed from PyQt6, but the latter is available on PyQt5 and 6.

diff --git a/tortoisehg/hgqt/guess.py b/tortoisehg/hgqt/guess.py
--- a/tortoisehg/hgqt/guess.py
+++ b/tortoisehg/hgqt/guess.py
@@ -389,7 +389,7 @@
f = self.displayformats[index.column()]
return f(s)
'''
- elif role == Qt.TextColorRole:
+ elif role == Qt.ItemDataRole.ForegroundRole:
src, dst, pct = self.rows[index.row()]
if pct == 1.0:
return QColor('green')
diff --git a/tortoisehg/hgqt/status.py b/tortoisehg/hgqt/status.py
--- a/tortoisehg/hgqt/status.py
+++ b/tortoisehg/hgqt/status.py
@@ -929,7 +929,7 @@
return _('Checked count: %d') % self.checkCount
elif role == Qt.ItemDataRole.DisplayRole:
return self.rows[index.row()][index.column()]
- elif role == Qt.TextColorRole:
+ elif role == Qt.ItemDataRole.ForegroundRole:
path, status, mst, upath, ext, sz = self.rows[index.row()]
if mst:
return _colors.get(mst.lower(), QColor('black'))

Matt Harbison

unread,
Apr 7, 2022, 8:14:18 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649277148 14400
# Wed Apr 06 16:32:28 2022 -0400
# Node ID 6c317eb2d9079cd5d2edda3314c5d39cba84f074
# Parent 378b7e88f6e2acee6291097aac45c3804e7da802
# EXP-Topic pyqt6
qtcompat: rename `Qt.MidButton` to `Qt.MouseButton.MiddleButton`

The former was removed from PyQt6, but the latter is available on PyQt5 and 6.

diff --git a/tortoisehg/hgqt/repotab.py b/tortoisehg/hgqt/repotab.py
--- a/tortoisehg/hgqt/repotab.py
+++ b/tortoisehg/hgqt/repotab.py
@@ -45,7 +45,7 @@
class _TabBar(QTabBar):

def mouseReleaseEvent(self, event):
- if event.button() == Qt.MidButton:
+ if event.button() == Qt.MouseButton.MiddleButton:
self.tabCloseRequested.emit(self.tabAt(event.pos()))
super(_TabBar, self).mouseReleaseEvent(event)

diff --git a/tortoisehg/hgqt/repoview.py b/tortoisehg/hgqt/repoview.py
--- a/tortoisehg/hgqt/repoview.py
+++ b/tortoisehg/hgqt/repoview.py
@@ -141,7 +141,7 @@
index = self.indexAt(event.pos())
if not index.isValid():
return
- if event.button() == Qt.MidButton:
+ if event.button() == Qt.MouseButton.MiddleButton:
self.gotoAncestor(index)
return
QTableView.mousePressEvent(self, event)

Matt Harbison

unread,
Apr 7, 2022, 8:14:18 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649278002 14400
# Wed Apr 06 16:46:42 2022 -0400
# Node ID 1b979df0ca7372b3c12badb9517f3d31539cb78b
# Parent 6c317eb2d9079cd5d2edda3314c5d39cba84f074
# EXP-Topic pyqt6
qtcompat: rename `Qt.ImMicroFocus` to `Qt.InputMethodQuery.ImCursorRectangle`

The former was removed from PyQt6, but the latter is available on PyQt5 and 6.

diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
--- a/tortoisehg/hgqt/qscilib.py
+++ b/tortoisehg/hgqt/qscilib.py
@@ -166,7 +166,7 @@
self._imsupport = _SciImSupport(self)

def inputMethodQuery(self, query):
- if query == Qt.ImMicroFocus:
+ if query == Qt.InputMethodQuery.ImCursorRectangle:
# a rectangle (in viewport coords) including the cursor
l, i = self.getCursorPosition()
p = self.positionFromLineIndex(l, i)

Matt Harbison

unread,
Apr 7, 2022, 8:14:19 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649349394 14400
# Thu Apr 07 12:36:34 2022 -0400
# Node ID 0b60f0ea344bfb2d7a5f33f535a38b62a84865a6
# Parent 3ac79270239e96b5a2c1feb8709454d64faba904
# EXP-Topic pyqt6
qtcompat: replace QDirModel with QFileSystemModel

The former was obsoleted in Qt4.7, and removed from 6.0.

diff --git a/tortoisehg/hgqt/settings.py b/tortoisehg/hgqt/settings.py
--- a/tortoisehg/hgqt/settings.py
+++ b/tortoisehg/hgqt/settings.py
@@ -23,8 +23,8 @@
QCompleter,
QDialog,
QDialogButtonBox,
- QDirModel,
QFileDialog,
+ QFileSystemModel,
QFont,
QFontDialog,
QFormLayout,
@@ -485,7 +485,7 @@
# use QCompleter(model, parent) to avoid ownership bug of
# QCompleter(parent /TransferBack/) in PyQt<4.11.4
completer = QCompleter(None, self)
- completer.setModel(QDirModel(completer))
+ completer.setModel(QFileSystemModel(completer))
self.lineEdit.setCompleter(completer)

self.browseButton = QPushButton(_('&Browse...'))

Matt Harbison

unread,
Apr 7, 2022, 8:14:20 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649350204 14400
# Thu Apr 07 12:50:04 2022 -0400
# Node ID 103f7cf8ce7bcbbded38a24b80b516aa6fb7a6d1
# Parent 0b60f0ea344bfb2d7a5f33f535a38b62a84865a6
# EXP-Topic pyqt6
qtcompat: replace QFontMetrics.width() with QFontMetrics.horizontalAdvance()

The former was removed in PyQt6.

diff --git a/tortoisehg/hgqt/chunks.py b/tortoisehg/hgqt/chunks.py
--- a/tortoisehg/hgqt/chunks.py
+++ b/tortoisehg/hgqt/chunks.py
@@ -530,7 +530,7 @@
def paintEvent(self, event):
p = QPainter()
fm = QFontMetrics(self.font())
- if fm.width(self.text()): # > self.contentsRect().width():
+ if fm.horizontalAdvance(self.text()): # > self.contentsRect().width():
elided = fm.elidedText(self.text(), Qt.TextElideMode.ElideLeft,
self.rect().width(), 0)
p.drawText(self.rect(), Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignRight |
@@ -617,7 +617,7 @@

self.sci.setMarginType(1, qsci.MarginType.SymbolMargin)
self.sci.setMarginLineNumbers(1, False)
- self.sci.setMarginWidth(1, QFontMetrics(self.font()).width('XX'))
+ self.sci.setMarginWidth(1, QFontMetrics(self.font()).horizontalAdvance('XX'))
self.sci.setMarginSensitivity(1, True)
self.sci.marginClicked.connect(self.marginClicked)

diff --git a/tortoisehg/hgqt/commit.py b/tortoisehg/hgqt/commit.py
--- a/tortoisehg/hgqt/commit.py
+++ b/tortoisehg/hgqt/commit.py
@@ -499,7 +499,7 @@
self.committb = committb = CommitToolButton(self)
committb.setBold()
committb.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
- fmk = lambda s: committb.fontMetrics().width(hglib.tounicode(s[2]))
+ fmk = lambda s: committb.fontMetrics().horizontalAdvance(hglib.tounicode(s[2]))
committb._width = (max(pycompat.maplist(fmk, acts))
+ 4*committb.menuButtonWidth())

diff --git a/tortoisehg/hgqt/fileview.py b/tortoisehg/hgqt/fileview.py
--- a/tortoisehg/hgqt/fileview.py
+++ b/tortoisehg/hgqt/fileview.py
@@ -695,7 +695,7 @@
# assume that the longest line has the largest width;
# fm.width() is too slow to apply to each line.
longestline = max(lines, key=len)
- maxWidth = fm.width(longestline)
+ maxWidth = fm.horizontalAdvance(longestline)
else:
maxWidth = 0
# setScrollWidth() expects the value to be > 0
diff --git a/tortoisehg/hgqt/rejects.py b/tortoisehg/hgqt/rejects.py
--- a/tortoisehg/hgqt/rejects.py
+++ b/tortoisehg/hgqt/rejects.py
@@ -279,7 +279,7 @@

self.setMarginType(1, qsci.MarginType.SymbolMargin)
self.setMarginLineNumbers(1, False)
- self.setMarginWidth(1, QFontMetrics(self.font()).width('XX'))
+ self.setMarginWidth(1, QFontMetrics(self.font()).horizontalAdvance('XX'))
self.setMarginSensitivity(1, True)
self.addedMark = self.markerDefine(qsci.MarkerSymbol.Plus, -1)
self.removedMark = self.markerDefine(qsci.MarkerSymbol.Minus, -1)
diff --git a/tortoisehg/hgqt/reporegistry.py b/tortoisehg/hgqt/reporegistry.py
--- a/tortoisehg/hgqt/reporegistry.py
+++ b/tortoisehg/hgqt/reporegistry.py
@@ -215,7 +215,7 @@

def sizeHint(self):
size = super(RepoTreeView, self).sizeHint()
- size.setWidth(QFontMetrics(self.font()).width('M') * 15)
+ size.setWidth(QFontMetrics(self.font()).horizontalAdvance('M') * 15)
return size

class RepoRegistryView(QDockWidget):
diff --git a/tortoisehg/hgqt/repoview.py b/tortoisehg/hgqt/repoview.py
--- a/tortoisehg/hgqt/repoview.py
+++ b/tortoisehg/hgqt/repoview.py
@@ -304,7 +304,7 @@
if isinstance(w, int):
pass
elif w is not None:
- w = fontm.width(hglib.tounicode(str(w)) + 'w')
+ w = fontm.horizontalAdvance(hglib.tounicode(str(w)) + 'w')
else:
w = super(HgRepoView, self).sizeHintForColumn(c)
self.setColumnWidth(c, w)
@@ -759,7 +759,7 @@
font.setBold(False)
self._font = font
fm = QFontMetrics(font)
- self._twidths = [fm.width(t) for t, _s in labels]
+ self._twidths = [fm.horizontalAdvance(t) for t, _s in labels]
self._th = fm.height()
self._padw = 2
self._padh = 1 # may overwrite horizontal frame to fit row
diff --git a/tortoisehg/hgqt/webconf.py b/tortoisehg/hgqt/webconf.py
--- a/tortoisehg/hgqt/webconf.py
+++ b/tortoisehg/hgqt/webconf.py
@@ -232,7 +232,7 @@
super(_PathDialog, self).__init__(parent)
self.setWindowFlags((self.windowFlags() | Qt.WindowType.WindowMinimizeButtonHint)
& ~Qt.WindowType.WindowContextHelpButtonHint)
- self.resize(QFontMetrics(self.font()).width('M') * 50, self.height())
+ self.resize(QFontMetrics(self.font()).horizontalAdvance('M') * 50, self.height())
self.setWindowTitle(title)
self._invalidpaths = set(invalidpaths or [])
self.setLayout(QFormLayout(fieldGrowthPolicy=QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow))

Matt Harbison

unread,
Apr 7, 2022, 8:14:21 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649307303 14400
# Thu Apr 07 00:55:03 2022 -0400
# Node ID 0ff7aabc50da793ddd56a93bc7bcd690d32d9eb6
# Parent 103f7cf8ce7bcbbded38a24b80b516aa6fb7a6d1
# EXP-Topic pyqt6
qtcompat: explicitly convert between ints and enums when handling settings

The set functions reject ints on PyQt6, and unless translated back to ints when
saving the settings, the next launch with PyQt5 emits these warnings:

QVariant::load: unknown user type with name QsciScintilla::WrapMode.
QVariant::load: unknown user type with name QsciScintilla::WhitespaceVisibility.

diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
--- a/tortoisehg/hgqt/qscilib.py
+++ b/tortoisehg/hgqt/qscilib.py
@@ -382,8 +382,13 @@
a.setChecked(self.autoCompletionThreshold() > 0)

def saveSettings(self, qs, prefix):
- qs.setValue(prefix+'/wrap', self.wrapMode())
- qs.setValue(prefix+'/whitespace', self.whitespaceVisibility())
+ if QT_VERSION >= 0x60000:
+ value = lambda e: e.value
+ else:
+ value = lambda e: e
+
+ qs.setValue(prefix+'/wrap', value(self.wrapMode()))
+ qs.setValue(prefix+'/whitespace', value(self.whitespaceVisibility()))
qs.setValue(prefix+'/eol', self.eolVisibility())
if self.autoUseTabs:
qs.setValue(prefix+'/usetabs', -1)
@@ -392,8 +397,14 @@
qs.setValue(prefix+'/autocomplete', self.autoCompletionThreshold())

def loadSettings(self, qs, prefix):
- self.setWrapMode(qtlib.readInt(qs, prefix + '/wrap'))
- self.setWhitespaceVisibility(qtlib.readInt(qs, prefix + '/whitespace'))
+ self.setWrapMode(
+ QsciScintilla.WrapMode(qtlib.readInt(qs, prefix + '/wrap'))
+ )
+ self.setWhitespaceVisibility(
+ QsciScintilla.WhitespaceVisibility(
+ qtlib.readInt(qs, prefix + '/whitespace')
+ )
+ )
self.setEolVisibility(qtlib.readBool(qs, prefix + '/eol'))
# usetabs = -1, False, or True
usetabs = qtlib.readInt(qs, prefix + '/usetabs')

Matt Harbison

unread,
Apr 7, 2022, 8:14:21 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649364243 14400
# Thu Apr 07 16:44:03 2022 -0400
# Node ID c66fbdc177f29ff154c75e12ae90802c1ad28c21
# Parent 0ff7aabc50da793ddd56a93bc7bcd690d32d9eb6
# EXP-Topic pyqt6
qtcompat: fix a default enum set to be PyQt6 compatible

This crashed on PyQt6 because the callable form wants an integer argument. The
new default value is the default QWidget type, and evaluates to 0.

diff --git a/tortoisehg/hgqt/qtlib.py b/tortoisehg/hgqt/qtlib.py
--- a/tortoisehg/hgqt/qtlib.py
+++ b/tortoisehg/hgqt/qtlib.py
@@ -1437,7 +1437,7 @@
parent=self)

def getTextInput(parent, title, label, mode=QLineEdit.EchoMode.Normal, text='',
- flags=Qt.WindowFlags()):
+ flags=Qt.WindowType.Widget):
flags |= (Qt.WindowType.CustomizeWindowHint | Qt.WindowType.WindowTitleHint
| Qt.WindowType.WindowCloseButtonHint)
dlg = _EncodingSafeInputDialog(parent, flags)

Matt Harbison

unread,
Apr 7, 2022, 8:14:22 PM4/7/22
to thg...@googlegroups.com
# HG changeset patch
# User Matt Harbison <matt_h...@yahoo.com>
# Date 1649366798 14400
# Thu Apr 07 17:26:38 2022 -0400
# Node ID 9e9f0c22ed3f6f6e46d3cbe38821d90f54e1457a
# Parent c66fbdc177f29ff154c75e12ae90802c1ad28c21
# EXP-Topic pyqt6
qtcompat: replace a missing QComboBox.SizeAdjustPolicy value

This was missed in the scripted replacement because the original value was
dropped in Qt6. The Qt4 docs simply say to use either `AdjustToContents` or
`AdjustToContentsOnFirstShow` instead.

diff --git a/tortoisehg/hgqt/sync.py b/tortoisehg/hgqt/sync.py
--- a/tortoisehg/hgqt/sync.py
+++ b/tortoisehg/hgqt/sync.py
@@ -218,7 +218,7 @@
self.targetcombo = QComboBox()
self.targetcombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.targetcombo.setSizeAdjustPolicy(
- QComboBox.AdjustToMinimumContentsLength)
+ QComboBox.SizeAdjustPolicy.AdjustToContents)
self.targetcombo.setEnabled(False)
self.targetcheckbox = QCheckBox(_('Target:'))
self.targetcheckbox.toggled.connect(self.targetcombo.setEnabled)

Yuya Nishihara

unread,
Apr 8, 2022, 12:16:56 AM4/8/22
to Matt Harbison, thg...@googlegroups.com
On Thu, 07 Apr 2022 20:14:09 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_h...@yahoo.com>
> # Date 1649271699 14400
> # Wed Apr 06 15:01:39 2022 -0400
> # Node ID 296df1d2a620c6c882a8f6430c383bc7ee618dcd
> # Parent 8650f93868f73299cbb483fbbf55c0f22e50bcad
> # EXP-Topic pyqt6
> qtcompat: fully qualify QStyleOptionViewItem enums

Queued except for the QDirModel one, thanks.

Yuya Nishihara

unread,
Apr 8, 2022, 12:16:58 AM4/8/22
to Matt Harbison, thg...@googlegroups.com
On Thu, 07 Apr 2022 20:14:18 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_h...@yahoo.com>
> # Date 1649349394 14400
> # Thu Apr 07 12:36:34 2022 -0400
> # Node ID 0b60f0ea344bfb2d7a5f33f535a38b62a84865a6
> # Parent 3ac79270239e96b5a2c1feb8709454d64faba904
> # EXP-Topic pyqt6
> qtcompat: replace QDirModel with QFileSystemModel

Dropped this.

> diff --git a/tortoisehg/hgqt/settings.py b/tortoisehg/hgqt/settings.py
> --- a/tortoisehg/hgqt/settings.py
> +++ b/tortoisehg/hgqt/settings.py
> @@ -23,8 +23,8 @@
> QCompleter,
> QDialog,
> QDialogButtonBox,
> - QDirModel,
> QFileDialog,
> + QFileSystemModel,
> QFont,
> QFontDialog,
> QFormLayout,
> @@ -485,7 +485,7 @@
> # use QCompleter(model, parent) to avoid ownership bug of
> # QCompleter(parent /TransferBack/) in PyQt<4.11.4
> completer = QCompleter(None, self)
> - completer.setModel(QDirModel(completer))
> + completer.setModel(QFileSystemModel(completer))

We'll probably need to set up the model to list only directories.

Yuya Nishihara

unread,
Apr 8, 2022, 12:16:59 AM4/8/22
to Matt Harbison, thg...@googlegroups.com
On Thu, 07 Apr 2022 20:14:22 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_h...@yahoo.com>
> # Date 1649366798 14400
> # Thu Apr 07 17:26:38 2022 -0400
> # Node ID 9e9f0c22ed3f6f6e46d3cbe38821d90f54e1457a
> # Parent c66fbdc177f29ff154c75e12ae90802c1ad28c21
> # EXP-Topic pyqt6
> qtcompat: replace a missing QComboBox.SizeAdjustPolicy value
>
> This was missed in the scripted replacement because the original value was
> dropped in Qt6. The Qt4 docs simply say to use either `AdjustToContents` or
> `AdjustToContentsOnFirstShow` instead.
>
> diff --git a/tortoisehg/hgqt/sync.py b/tortoisehg/hgqt/sync.py
> --- a/tortoisehg/hgqt/sync.py
> +++ b/tortoisehg/hgqt/sync.py
> @@ -218,7 +218,7 @@
> self.targetcombo = QComboBox()
> self.targetcombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
> self.targetcombo.setSizeAdjustPolicy(
> - QComboBox.AdjustToMinimumContentsLength)
> + QComboBox.SizeAdjustPolicy.AdjustToContents)

This will change the behavior, but I can't figure out what's the original
intent, so queued.

Yuya Nishihara

unread,
Apr 8, 2022, 12:17:00 AM4/8/22
to Matt Harbison, thg...@googlegroups.com
On Thu, 07 Apr 2022 20:14:12 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_h...@yahoo.com>
> # Date 1649270104 14400
> # Wed Apr 06 14:35:04 2022 -0400
> # Node ID 4aad8d6d58729ff89ce632c0ecc25f8386e1be7f
> # Parent bf9d055265735411ecd7de6d54da16da9e8b47e0
> # EXP-Topic pyqt6
> qtcompat: obtain the screen size in a PyQt6 compatible manner
>
> This started as dropping `qApp` (which was removed in Qt6), but also the

Nit: s/Qt6/PyQt6/

> --- a/tortoisehg/hgqt/bugreport.py
> +++ b/tortoisehg/hgqt/bugreport.py
> @@ -247,7 +247,7 @@
> bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, centerButtons=True)
> bb.rejected.connect(self.reject)
> self.layout().addWidget(bb)
> - desktopgeom = qApp.desktop().availableGeometry()
> + desktopgeom = QApplication.primaryScreen().availableGeometry()

Perhaps, it should instead do self.screen().availableGeometry(), but I don't
know if the window handle is already available here.

Anyway, this patch itself looks good.

Yuya Nishihara

unread,
Apr 8, 2022, 12:17:02 AM4/8/22
to Matt Harbison, thg...@googlegroups.com
On Thu, 07 Apr 2022 20:14:19 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_h...@yahoo.com>
> # Date 1649350204 14400
> # Thu Apr 07 12:50:04 2022 -0400
> # Node ID 103f7cf8ce7bcbbded38a24b80b516aa6fb7a6d1
> # Parent 0b60f0ea344bfb2d7a5f33f535a38b62a84865a6
> # EXP-Topic pyqt6
> qtcompat: replace QFontMetrics.width() with QFontMetrics.horizontalAdvance()

Just a reminder. horizontalAdvance() is relatively new function. We might
have to upgrade the packaged Qt version.

For modern Linux environments, I think Qt 5.11 is reasonable.

https://doc.qt.io/qt-5/qfontmetrics.html#horizontalAdvance-1

Matt Harbison

unread,
Apr 8, 2022, 1:15:39 AM4/8/22
to TortoiseHg Developers
One strange behavior I've noticed with PyQt6 is that the initial launch of the app is sized for the full screen, but shifted down and to the right several inches.  That doesn't happen with the PyQt5 app using this same function.  (There's probably also something wrong with how settings are saved, because cycling through py2/PyQt5 -> py3/PyQt6 -> py3/PyQt5 seems to reset the app size and columns, but for now, I'm just trying to get it to launch.)
 

Yuya Nishihara

unread,
Apr 8, 2022, 5:50:23 AM4/8/22
to Matt Harbison, thg...@googlegroups.com
Nope. Showing files was fine, but appears that we need to set the root path
to trigger fetching?

https://code.qt.io/cgit/qt/qtbase.git/tree/examples/widgets/tools/completer/mainwindow.cpp?h=v5.15.3-lts-lgpl#n248

Matt Harbison

unread,
Apr 8, 2022, 6:39:03 PM4/8/22
to TortoiseHg Developers
I see the call to ` fsModel->setRootPath(QString())`, but I'm not sure what the point is.  When I click the browse buttons in File|Clone, it opens in the directory I'd expect (at least on Windows).  The text syncing between the edit fields also still works.

 

Yuya Nishihara

unread,
Apr 8, 2022, 9:51:13 PM4/8/22
to 'Matt Harbison' via TortoiseHg Developers
It's the completion model of edit boxes. If you type "/" for example, popup
listing /* entries should be displayed. This appears not working without
setRootPath() call.
Reply all
Reply to author
Forward
0 new messages