Esempio n. 1
0
def build_nested_qmenu(widget, context_options, name=None):
    """ builds nested menu for context menus but can be used for other menu
    related things.

    References:
        http://pyqt.sourceforge.net/Docs/PyQt4/qkeysequence.html
    """
    if name is None:
        menu = QtWidgets.QMenu(widget)
    else:
        menu = QtWidgets.QMenu(name, widget)
    action_list = []
    for option_tup in context_options:
        if len(option_tup) == 2:
            opt, func = option_tup
            shortcut = QtGui.QKeySequence(0)
        elif len(option_tup) == 3:
            opt, shortcut_str, func = option_tup
            shortcut = QtGui.QKeySequence(shortcut_str)

        if func is None:
            action = menu.addAction(opt, lambda: None, shortcut)
            action_list.append(action)
        elif isinstance(func, list):
            # Recursive case
            sub_menu, sub_action_list = build_nested_qmenu(widget, func, opt)
            menu.addMenu(sub_menu)
            action_list.append((sub_menu, sub_action_list))
        else:
            action = menu.addAction(opt, func, shortcut)
            action_list.append(action)
    return menu, action_list
Esempio n. 2
0
 def setupUi(self, editPrefSkel):
     editPrefSkel.setObjectName(_fromUtf8('editPrefSkel'))
     editPrefSkel.resize(668, 530)
     # Add Pane for TreeView
     self.verticalLayout = QtWidgets.QVBoxLayout(editPrefSkel)
     self.verticalLayout.setObjectName(_fromUtf8('verticalLayout'))
     # The TreeView for QtCore.QAbstractItemModel to attach to
     self.prefTreeView = QtWidgets.QTreeView(editPrefSkel)
     self.prefTreeView.setObjectName(_fromUtf8('prefTreeView'))
     self.verticalLayout.addWidget(self.prefTreeView)
     # Add Pane for buttons
     self.horizontalLayout = QtWidgets.QHBoxLayout()
     self.horizontalLayout.setObjectName(_fromUtf8('horizontalLayout'))
     #
     # self.redrawBUT = QtWidgets.QPushButton(editPrefSkel)
     # self.redrawBUT.setObjectName(_fromUtf8('redrawBUT'))
     # self.horizontalLayout.addWidget(self.redrawBUT)
     ##
     # self.unloadFeaturesAndModelsBUT = QtWidgets.QPushButton(editPrefSkel)
     # self.unloadFeaturesAndModelsBUT.setObjectName(_fromUtf8('unloadFeaturesAndModelsBUT'))
     # self.horizontalLayout.addWidget(self.unloadFeaturesAndModelsBUT)
     #
     self.defaultPrefsBUT = QtWidgets.QPushButton(editPrefSkel)
     self.defaultPrefsBUT.setObjectName(_fromUtf8('defaultPrefsBUT'))
     self.horizontalLayout.addWidget(self.defaultPrefsBUT)
     # Buttons are a child of the View
     self.verticalLayout.addLayout(self.horizontalLayout)
     self.retranslateUi(editPrefSkel)
     QtCore.QMetaObject.connectSlotsByName(editPrefSkel)
Esempio n. 3
0
    def paint(self, painter, option, index):
        version4 = GUITOOL_PYQT_VERSION == 4
        # if GUITOOL_PYQT_VERSION:
        #     return super(ConfigValueDelegate, self).paint(painter, option, index)
        # Get Item Data
        # value = index.data(QtCore.Qt.DisplayRole).toInt()[0]
        leafNode = index.internalPointer()
        # if (VERBOSE_CONFIG and False):
        #     print('[DELEGATE] * painting editor for %s at %s' % (leafNode, qindexstr(index)))
        #     leafNode.print_tree()
        #     # print('[DELEGATE] * painting editor for %s at %s' % (leafNode, qindexstr(index)))
        if leafNode.is_combo:
            # print('[DELEGATE] * painting editor for %s at %s' % (leafNode, qindexstr(index)))
            # painter.save()
            curent_value = six.text_type(index.model().data(index))
            style = QtWidgets.QApplication.style()
            opt = QtWidgets.QStyleOptionComboBox()

            opt.currentText = curent_value
            opt.rect = option.rect
            opt.editable = False
            opt.frame = True

            if leafNode.qt_is_editable():
                opt.state |= style.State_On
                opt.state |= style.State_Enabled
                opt.state = style.State_Enabled | style.State_Active

            element = QtWidgets.QStyle.CE_ComboBoxLabel
            control = QtWidgets.QStyle.CC_ComboBox

            style.drawComplexControl(control, opt, painter)
            style.drawControl(element, opt, painter)
        elif (leafNode is not None and leafNode.is_spin) and version4:
            # fill style options with item data
            style = QtWidgets.QApplication.style()
            opt = QtWidgets.QStyleOptionSpinBox()
            # opt.currentText doesn't exist for SpinBox
            # opt.currentText = curent_value  #
            opt.rect = option.rect
            # opt.editable = False
            if leafNode.qt_is_editable():
                opt.state |= style.State_Enabled
            element = QtWidgets.QStyle.CE_ItemViewItem
            control = QtWidgets.QStyle.CC_SpinBox
            painter.save()
            style.drawComplexControl(control, opt, painter)
            style.drawControl(element, opt, painter)
            # self.drawDisplay(painter, opt, opt.rect, str(curent_value))
            option.rect.setLeft(option.rect.left() + 3)
            curent_value = six.text_type(index.model().data(index))
            painter.drawText(option.rect, Qt.AlignLeft, str(curent_value))
            painter.restore()
        else:
            return super(ConfigValueDelegate, self).paint(painter, option, index)
Esempio n. 4
0
 def __init__(
     cltw,
     col_data_list=None,
     col_name_list=None,
     niceheader_list=None,
     col_type_list=None,
     col_edit_list=None,
     display_indices=False,
     col_sort_index=None,
     parent=None,
 ):
     super(ColumnListTableWidget, cltw).__init__(parent)
     # QtWidgets.QWidget.__init__(cltw, parent)
     # Create vertical layout for the table to go into
     cltw.vert_layout = QtWidgets.QVBoxLayout(cltw)
     # Instansiate the AbstractItemModel
     cltw.model = ColumnListItemModel(parent=cltw)
     # Create a ColumnListTableView for the AbstractItemModel
     cltw.view = ColumnListTableView(cltw)
     cltw.view.setModel(cltw.model)
     cltw.vert_layout.addWidget(cltw.view)
     # Make sure we don't call a childs method
     ColumnListTableWidget.change_data(
         cltw,
         col_data_list,
         col_name_list,
         niceheader_list,
         col_type_list,
         col_edit_list,
         display_indices,
         col_sort_index,
     )
Esempio n. 5
0
def infer_delegates(view, **headers):
    """ Infers which columns should be given item delegates """
    get_thumb_size = headers.get('get_thumb_size', None)
    col_type_list = headers.get('col_type_list', [])
    num_cols = view.model().columnCount()
    num_duplicates = int(num_cols / len(col_type_list))
    col_type_list = col_type_list * num_duplicates
    view.has_thumbs = False
    for colx, coltype in enumerate(col_type_list):
        if coltype in qtype.QT_PIXMAP_TYPES:
            if VERBOSE:
                print('[view] colx=%r is a PIXMAP' % colx)
            thumb_delegate = api_thumb_delegate.APIThumbDelegate(
                view, get_thumb_size)
            view.setItemDelegateForColumn(colx, thumb_delegate)
            view.has_thumbs = True
            # HACK
            # verticalHeader = view.verticalHeader()
            # verticalHeader.setDefaultSectionSize(256)
        elif coltype in qtype.QT_BUTTON_TYPES:
            if VERBOSE:
                print('[view] colx=%r is a BUTTON' % colx)
            button_delegate = api_button_delegate.APIButtonDelegate(view)
            view.setItemDelegateForColumn(colx, button_delegate)
        elif isinstance(coltype, QtWidgets.QAbstractItemDelegate):
            if VERBOSE:
                print('[view] colx=%r is a CUSTOM DELEGATE' % colx)
            view.setItemDelegateForColumn(colx, coltype)
        else:
            if VERBOSE:
                print('[view] colx=%r does not have a delgate' % colx)
            # Effectively unsets any existing delegates
            default_delegate = QtWidgets.QStyledItemDelegate(view)
            view.setItemDelegateForColumn(colx, default_delegate)
Esempio n. 6
0
def get_number_of_monitors():
    gt.ensure_qtapp()
    desktop = QtWidgets.QDesktopWidget()
    if hasattr(desktop, 'numScreens'):
        n = desktop.numScreens()
    else:
        n = desktop.screenCount()
    return n
Esempio n. 7
0
def get_monitor_geometries():
    gt.ensure_qtapp()
    monitor_geometries = {}
    desktop = QtWidgets.QDesktopWidget()
    if hasattr(desktop, 'numScreens'):
        n = desktop.numScreens()
    else:
        n = desktop.screenCount()
    for screenx in range(n):
        rect = desktop.availableGeometry(screen=screenx)
        geom = (rect.x(), rect.y(), rect.width(), rect.height())
        monitor_geometries[screenx] = geom
    return monitor_geometries
Esempio n. 8
0
    def init_layout(self):
        import wbia.guitool as gt

        # Create the tree view and buttons
        self.tree_view = QtWidgets.QTreeView(self)
        self.delegate = ConfigValueDelegate(self.tree_view)
        self.tree_view.setItemDelegateForColumn(1, self.delegate)

        if self._with_buttons:
            buttons = []
            self.default_but = gt.newButton(
                self, 'Defaults', pressed=self.reset_to_default
            )
            buttons.append(self.default_but)

            self.orig_but = gt.newButton(self, 'Original', pressed=self.reset_to_original)
            buttons.append(self.orig_but)

            if not self.user_mode:
                self.print_internals = gt.newButton(
                    self, 'Print Internals', pressed=self.print_internals
                )
                buttons.append(self.print_internals)

            # Add compoments to the layout
            self.hbox = QtWidgets.QHBoxLayout()
            for button in buttons:
                self.hbox.addWidget(button)
                button.setSizePolicy(
                    QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum
                )

        self.vbox = QtWidgets.QVBoxLayout(self)
        self.vbox.addWidget(self.tree_view)
        if self._with_buttons:
            self.vbox.addLayout(self.hbox)
        self.setWindowTitle(_translate('self', 'Edit Config Widget', None))
 def createEditor(self, parent, option, index):
     combo = QtWidgets.QComboBox(parent)
     combo.addItems(['option1', 'option2', 'option3'])
     # FIXME: Change to newstyle signal slot
     if GUITOOL_PYQT_VERSION == 5:
         self.connect(combo.currentIndexChanged, self.currentIndexChanged)
     else:
         # I believe this particular option is broken in pyqt4
         self.connect(
             combo,
             QtCore.SIGNAL('currentIndexChanged(int)'),
             self,
             QtCore.SLOT('currentIndexChanged()'),
         )
     return combo
Esempio n. 10
0
def newFileDialog(directory_,
                  other_sidebar_dpaths=[],
                  use_sidebar_cwd=True,
                  mode='open',
                  exec_=False):
    r"""
    Args:
        directory_ (?):
        other_sidebar_dpaths (list): (default = [])
        use_sidebar_cwd (bool): (default = True)
        _dialog_class_ (wrappertype): (default = <class 'PyQt5.QtWidgets.QFileDialog'>)

    Returns:
        ?: qdlg

    CommandLine:
        python -m wbia.guitool.guitool_dialogs newFileDialog --show

    Example:
        >>> # DISABLE_DOCTEST
        >>> # GUI_DOCTEST
        >>> # xdoctest: +REQUIRES(--gui)
        >>> from wbia.guitool.guitool_dialogs import *  # NOQA
        >>> import wbia.guitool
        >>> guitool.ensure_qtapp()
        >>> directory_ = '.'
        >>> _dialog_class_ = QtWidgets.QFileDialog
        >>> if ut.show_was_requested():
        >>>     files = newFileDialog(directory_, mode='save', exec_=True)
        >>>     print('files = %r' % (files,))
        >>> else:
        >>>     dlg = newFileDialog(directory_, mode='save', exec_=True)
    """
    qdlg = QtWidgets.QFileDialog()
    if mode == 'open':
        qdlg.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
    elif mode == 'save':
        qdlg.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
    else:
        raise KeyError(mode)

    _set_dialog_sidebar(qdlg, directory_, use_sidebar_cwd,
                        other_sidebar_dpaths)
    if exec_:
        qdlg.exec_()
        return qdlg.selectedFiles()
    else:
        return qdlg
Esempio n. 11
0
def _newMsgBox(
    msg='', title='', parent=None, options=None, cache_reply=False, resizable=False
):
    if resizable:
        msgbox = ResizableMessageBox(parent)
    else:
        msgbox = QtWidgets.QMessageBox(parent)
    # msgbox.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    # std_buts = QtWidgets.QMessageBox.Close
    # std_buts = QtWidgets.QMessageBox.NoButton
    std_buts = QtWidgets.QMessageBox.Cancel
    msgbox.setStandardButtons(std_buts)
    msgbox.setWindowTitle(title)
    msgbox.setText(msg)
    msgbox.setModal(parent is not None)
    return msgbox
 def paint(self, painter, option, index):
     # This method will be called every time a particular cell is
     # in view and that view is changed in some way. We ask the
     # delegates parent (in this case a table view) if the index
     # in question (the table cell) already has a widget associated
     # with it. If not, create one with the text for this index and
     # connect its clicked signal to a slot in the parent view so
     # we are notified when its used and can do something.
     if not self.parent().indexWidget(index):
         self.parent().setIndexWidget(
             index,
             QtWidgets.QPushButton(
                 index.data().toString(),
                 self.parent(),
                 clicked=self.parent().cellButtonClicked,
             ),
         )
    def __init__(co_wgt, ibs, gid_list, parent=None, hack=False):
        logger.info('[co_gui] Initializing')
        logger.info('[co_gui] gid_list = %r' % (gid_list, ))

        QtWidgets.QWidget.__init__(co_wgt, parent=parent)

        co_wgt.fnum = pt.next_fnum()

        co_wgt.main_layout = QtWidgets.QVBoxLayout(co_wgt)

        co_wgt.text_layout = gt.newWidget(co_wgt,
                                          orientation=Qt.Vertical,
                                          verticalStretch=10)
        co_wgt.main_layout.addWidget(co_wgt.text_layout)

        co_wgt.control_layout = gt.newWidget(
            co_wgt,
            orientation=Qt.Vertical,
            verticalSizePolicy=QtWidgets.QSizePolicy.MinimumExpanding,
        )
        co_wgt.main_layout.addWidget(co_wgt.control_layout)

        co_wgt.button_layout = gt.newWidget(co_wgt, orientation=Qt.Horizontal)
        co_wgt.control_layout.addWidget(co_wgt.button_layout)

        co_wgt.combo_layout = gt.newWidget(co_wgt, orientation=Qt.Horizontal)
        co_wgt.control_layout.addWidget(co_wgt.combo_layout)

        co_wgt.hack = hack  # hack for edting the time of a single image

        co_wgt.imfig = None
        co_wgt.imax = None

        co_wgt.dtime = None

        co_wgt.ibs = ibs
        co_wgt.gid_list = gid_list
        co_wgt.current_gindex = 0
        co_wgt.offset = 0
        # Set image datetime with first image
        co_wgt.get_image_datetime_()
        co_wgt.add_label()
        co_wgt.add_combo_boxes()
        co_wgt.add_buttons()
        co_wgt.update_ui()
Esempio n. 14
0
    def __init__(
        widget,
        headers=None,
        parent=None,
        model_class=APIItemModel,
        view_class=APITableView,
        tblnice='APIItemWidget',
        doubleClicked=None,
    ):
        WIDGET_BASE.__init__(widget, parent)
        if isinstance(view_class, six.string_types):
            if view_class == 'tree':
                view_class = APITreeView
            elif view_class == 'table':
                view_class = APITableView
            else:
                raise ValueError('Unknown view_class=%r' % (view_class,))
        # Create vertical layout for the table to go into
        widget.vert_layout = QtWidgets.QVBoxLayout(widget)
        # Create a ColumnListTableView for the AbstractItemModel
        widget.view = view_class(parent=widget)
        # Instantiate the AbstractItemModel
        # FIXME: It is very bad to give the model a view.
        # Only the view should have a model
        # Also the model should not be an attribute because
        # it might live in another thread
        widget._model = model_class(parent=widget.view)

        widget.view.setModel(widget._model)
        widget.vert_layout.addWidget(widget.view)
        widget.tblnice = tblnice
        if headers is not None:
            # Make sure we don't call a subclass method
            APIItemWidget.change_headers(widget, headers)
        widget.connect_signals()
        widget.api = None
        if doubleClicked:
            widget.view.doubleClicked.connect(doubleClicked)
def paint_button(
    painter,
    option,
    text='button',
    pressed=True,
    bgcolor=None,
    fgcolor=None,
    clicked=None,
    button=None,
    view=None,
):
    # http://www.qtcentre.org/archive/index.php/t-31029.html
    opt = QtWidgets.QStyleOptionButton()
    opt.text = text
    opt.rect = option.rect
    opt.palette = option.palette
    if pressed:
        opt.state = QtWidgets.QStyle.State_Enabled | QtWidgets.QStyle.State_Sunken
    else:
        opt.state = QtWidgets.QStyle.State_Enabled | QtWidgets.QStyle.State_Raised

    # style = QtGui.Q Application.style()
    style = button.style()
    style.drawControl(QtWidgets.QStyle.CE_PushButton, opt, painter, button)
Esempio n. 16
0
def get_monitor_geom(monitor_num=0):
    r"""
    Args:
        monitor_num (int): (default = 0)

    Returns:
        tuple: geom

    CommandLine:
        python -m wbia.plottool.screeninfo get_monitor_geom --show

    Example:
        >>> # DISABLE_DOCTEST
        >>> from wbia.plottool.screeninfo import *  # NOQA
        >>> monitor_num = 0
        >>> geom = get_monitor_geom(monitor_num)
        >>> result = ('geom = %s' % (ut.repr2(geom),))
        >>> print(result)
    """
    gt.ensure_qtapp()
    desktop = QtWidgets.QDesktopWidget()
    rect = desktop.availableGeometry(screen=monitor_num)
    geom = (rect.x(), rect.y(), rect.width(), rect.height())
    return geom
Esempio n. 17
0
def user_option(
    parent=None,
    msg='msg',
    title='user_option',
    options=['Yes', 'No'],
    use_cache=False,
    default=None,
    detailed_msg=None,
):
    """
    Prompts user with several options with ability to save decision

    Args:
        parent (None):
        msg (str):
        title (str):
        options (list):
        use_cache (bool):
        default (str): default option

    Returns:
        str: reply

    CommandLine:
        python -m wbia.guitool.guitool_dialogs --test-user_option

    Example:
        >>> # GUI_DOCTEST
        >>> # xdoctest: +REQUIRES(--gui)
        >>> from wbia.guitool.guitool_dialogs import *  # NOQA
        >>> import wbia.guitool as gt
        >>> gt.ensure_qtapp()
        >>> parent = None
        >>> msg = 'msg'
        >>> title = 'user_option'
        >>> options = ['Yes', 'No']
        >>> use_cache = False
        >>> default = 'Yes'
        >>> # execute function
        >>> detailed_msg = 'hi'
        >>> reply = user_option(parent, msg, title, options, use_cache, default, detailed_msg)
        >>> result = str(reply)
        >>> print(result)
        >>> # xdoctest: +REQUIRES(--show)
        >>> ut.quit_if_noshow()
        >>> #gt.guitool_main.qtapp_loop()
    """
    if ut.VERBOSE:
        print('[gt] user_option:\n %r: %s' % (title, msg))
    # Recall decision
    cache_id = title + msg
    if use_cache:
        reply = _guitool_cache_read(cache_id, default=None)
        if reply is not None:
            return reply
    # Create message box
    msgbox = _newMsgBox(msg, title, parent, resizable=detailed_msg is not None)
    #     _addOptions(msgbox, options)
    # def _addOptions(msgbox, options):
    # msgbox.addButton(QtWidgets.QMessageBox.Close)
    options = list(options)[::-1]
    for opt in options:
        role = QtWidgets.QMessageBox.ApplyRole
        msgbox.addButton(QtWidgets.QPushButton(opt), role)
    # Set default button
    if default is not None:
        assert default in options, 'default=%r is not in options=%r' % (default, options,)
        for qbutton in msgbox.buttons():
            if default == qbutton.text():
                msgbox.setDefaultButton(qbutton)
    if use_cache:
        # Add a remember me option if caching is on
        dontPrompt = _cacheReply(msgbox)

    if detailed_msg is not None:
        msgbox.setDetailedText(detailed_msg)
    # Wait for output
    optx = msgbox.exec_()
    if optx == QtWidgets.QMessageBox.Cancel:
        # User Canceled
        return None
    try:
        # User Selected an option
        reply = options[optx]
    except KeyError as ex:
        # This should be unreachable code.
        print('[gt] USER OPTION EXCEPTION !')
        print('[gt] optx = %r' % optx)
        print('[gt] options = %r' % options)
        print('[gt] ex = %r' % ex)
        raise
    # Remember decision if caching is on
    if use_cache and dontPrompt.isChecked():
        _guitool_cache_write(cache_id, reply)
    # Close the message box
    del msgbox
    return reply
def test_show_qimg(qimg):
    qpixmap = QtGui.QPixmap(qimg)
    lbl = QtWidgets.QLabel()
    lbl.setPixmap(qpixmap)
    lbl.show()  # show label with qim image
    return lbl
Esempio n. 19
0
def _cacheReply(msgbox):
    dontPrompt = QtWidgets.QCheckBox('dont ask me again', parent=msgbox)
    dontPrompt.blockSignals(True)
    msgbox.addButton(dontPrompt, QtWidgets.QMessageBox.ActionRole)
    return dontPrompt
Esempio n. 20
0
def get_resolution_info(monitor_num=0):
    r"""
    Args:
        monitor_num (int): (default = 0)

    Returns:
        dict: info

    CommandLine:
        python -m wbia.plottool.screeninfo get_resolution_info --show
        xrandr | grep ' connected'
        grep "NVIDIA" /var/log/Xorg.0.log

    Example:
        >>> # DISABLE_DOCTEST
        >>> from wbia.plottool.screeninfo import *  # NOQA
        >>> monitor_num = 1
        >>> for monitor_num in range(get_number_of_monitors()):
        >>>     info = get_resolution_info(monitor_num)
        >>>     print('monitor(%d).info = %s' % (monitor_num, ut.repr3(info, precision=3)))
    """
    import wbia.guitool as gt

    app = gt.ensure_qtapp()[0]  # NOQA
    # screen_resolution = app.desktop().screenGeometry()
    # width, height = screen_resolution.width(), screen_resolution.height()
    # print('height = %r' % (height,))
    # print('width = %r' % (width,))

    desktop = QtWidgets.QDesktopWidget()
    screen = desktop.screen(monitor_num)
    ppi_x = screen.logicalDpiX()
    ppi_y = screen.logicalDpiY()
    dpi_x = screen.physicalDpiX()
    dpi_y = screen.physicalDpiY()
    # This call is not rotated correctly
    # rect = screen.screenGeometry()

    # This call has bad offsets
    rect = desktop.screenGeometry(screen=monitor_num)

    # This call subtracts offsets weirdly
    # desktop.availableGeometry(screen=monitor_num)

    pixels_w = rect.width()
    # for num in range(desktop.screenCount()):
    # pass
    pixels_h = rect.height()
    # + rect.y()

    """
    I have two monitors (screens), after rotation effects they have
    the geometry: (for example)
        S1 = {x: 0, y=300, w: 1920, h:1080}
        S2 = {x=1920, y=0, w: 1080, h:1920}

    Here is a pictoral example
    G--------------------------------------C-------------------
    |                                      |                  |
    A--------------------------------------|                  |
    |                                      |                  |
    |                                      |                  |
    |                                      |                  |
    |                 S1                   |                  |
    |                                      |        S2        |
    |                                      |                  |
    |                                      |                  |
    |                                      |                  |
    |--------------------------------------B                  |
    |                                      |                  |
    |                                      |                  |
    ----------------------------------------------------------D
    Desired Info

    G = (0, 0)
    A = (S1.x, S1.y)
    B = (S1.x + S1.w, S1.y + S1.h)

    C = (S2.x, S2.y)
    D = (S2.x + S1.w, S2.y + S2.h)

    from PyQt4 import QtGui, QtCore
    app = QtCore.QCoreApplication.instance()
    if app is None:
        import sys
        app = QtGui.QApplication(sys.argv)
    desktop = QtGui.QDesktopWidget()
    rect1 = desktop.screenGeometry(screen=0)
    rect2 = desktop.screenGeometry(screen=1)
    """

    # I want to get the relative positions of my monitors
    # pt = screen.pos()
    # pt = screen.mapToGlobal(pt)
    # pt = screen.mapToGlobal(screen.pos())
    # Screen offsets seem bugged
    # off_x = pt.x()
    # off_y = pt.y()
    # print(pt.x())
    # print(pt.y())
    # pt = screen.mapToGlobal(QtCore.QPoint(0, 0))
    # print(pt.x())
    # print(pt.y())
    off_x = rect.x()
    off_y = rect.y()
    # pt.x(), pt.y()

    inches_w = pixels_w / dpi_x
    inches_h = pixels_h / dpi_y
    inches_diag = (inches_w ** 2 + inches_h ** 2) ** 0.5

    mm_w = inches_w * ut.MM_PER_INCH
    mm_h = inches_h * ut.MM_PER_INCH
    mm_diag = inches_diag * ut.MM_PER_INCH

    ratio = min(mm_w, mm_h) / max(mm_w, mm_h)

    # pixel_density = dpi_x / ppi_x
    info = ut.odict(
        [
            ('monitor_num', monitor_num),
            ('off_x', off_x),
            ('off_y', off_y),
            ('ratio', ratio),
            ('ppi_x', ppi_x),
            ('ppi_y', ppi_y),
            ('dpi_x', dpi_x),
            ('dpi_y', dpi_y),
            # 'pixel_density', pixel_density),
            ('inches_w', inches_w),
            ('inches_h', inches_h),
            ('inches_diag', inches_diag),
            ('mm_w', mm_w),
            ('mm_h', mm_h),
            ('mm_diag', mm_diag),
            ('pixels_w', pixels_w),
            ('pixels_h', pixels_h),
        ]
    )
    return info