コード例 #1
0
    def __init__(self, main):

        self.main = main

        def updateLogDisplay():
            self.pyLogTextDisplay.setText(getLastBytesOfFile(ARMORY_LOG_FILE))
            self.pyLogTextDisplay.moveCursor(QtGui.QTextCursor.End)
            self.cppLogTextDisplay.setText(getLastBytesOfFile(ARMCPP_LOG_FILE))
            self.cppLogTextDisplay.moveCursor(QtGui.QTextCursor.End)

        lblHeader = QRichLabel(tr("""<b>Log File Display</b>"""), doWrap=False)
        self.updateButton = QPushButton("Update")
        self.main.connect(self.updateButton, SIGNAL('clicked()'),
                          updateLogDisplay)
        topRow = makeHorizFrame([lblHeader, self.updateButton, 'stretch'])

        self.pyLogTextDisplay = self.createLogDisplay()
        self.cppLogTextDisplay = self.createLogDisplay()
        logTabPanel = QTabWidget()
        logTabPanel.addTab(self.pyLogTextDisplay, "Python Log")
        logTabPanel.addTab(self.cppLogTextDisplay, "C++ Log")

        self.logFrame = makeVertFrame([topRow, logTabPanel])

        # Now set the scrollarea widget to the layout
        self.tabToDisplay = QScrollArea()
        self.tabToDisplay.setWidgetResizable(True)
        self.tabToDisplay.setWidget(self.logFrame)
        updateLogDisplay()
コード例 #2
0
ファイル: single.py プロジェクト: siebert/calibre
    def setupUi(self, *args):  # {{{
        self.resize(990, 670)

        self.download_shortcut = QShortcut(self)
        self.download_shortcut.setKey(
            QKeySequence('Ctrl+D', QKeySequence.PortableText))
        p = self.parent()
        if hasattr(p, 'keyboard'):
            kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download'
            sc = p.keyboard.keys_map.get(kname, None)
            if sc:
                self.download_shortcut.setKey(sc[0])

        self.button_box = bb = QDialogButtonBox(self)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
                                       self)
        self.next_button.setShortcut(QKeySequence('Alt+Right'))
        self.next_button.clicked.connect(self.next_clicked)
        self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'),
                                       self)
        self.prev_button.setShortcut(QKeySequence('Alt+Left'))

        self.button_box.addButton(self.prev_button, bb.ActionRole)
        self.button_box.addButton(self.next_button, bb.ActionRole)
        self.prev_button.clicked.connect(self.prev_clicked)
        bb.setStandardButtons(bb.Ok | bb.Cancel)
        bb.button(bb.Ok).setDefault(True)

        self.scroll_area = QScrollArea(self)
        self.scroll_area.setFrameShape(QScrollArea.NoFrame)
        self.scroll_area.setWidgetResizable(True)
        self.central_widget = QTabWidget(self)
        self.scroll_area.setWidget(self.central_widget)

        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.l.addWidget(self.scroll_area)
        ll = self.button_box_layout = QHBoxLayout()
        self.l.addLayout(ll)
        ll.addSpacing(10)
        ll.addWidget(self.button_box)

        self.setWindowIcon(QIcon(I('edit_input.png')))
        self.setWindowTitle(BASE_TITLE)

        self.create_basic_metadata_widgets()

        if len(self.db.custom_column_label_map):
            self.create_custom_metadata_widgets()

        self.do_layout()
        geom = gprefs.get('metasingle_window_geometry3', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))
コード例 #3
0
class PluginObject(object):

    tabName = 'Armory Search'
    maxVersion = '0.93.99'

    #############################################################################
    def __init__(self, main):
        def searchItem():
            searchString = str(self.searchEntry.text())
            if len(searchString) > 0:
                likelyDataType = isLikelyDataType(searchString)
                for wltID, wlt in self.main.walletMap.iteritems():
                    if wlt.hasAddr(searchString):
                        searchHash = searchString if likelyDataType == DATATYPE.Hex \
                              else addrStr_to_hash160(searchString)[1]
                        dialog = DlgAddressInfo(wlt,
                                                searchHash,
                                                main=self.main)
                        dialog.exec_()
                        break
                    if likelyDataType == DATATYPE.Hex:
                        walletLedger = wlt.cppWallet.getTxLedger()
                        txHashToFind = hex_to_binary(searchString,
                                                     endOut=BIGENDIAN)
                        txFound = False
                        for entry in walletLedger:
                            if entry.getTxHash() == txHashToFind:
                                cppTx = TheBDM.getTxByHash(txHashToFind)
                                serializedCppTx = cppTx.serialize()
                                pytx = PyTx().unserialize(serializedCppTx)
                                DlgDispTxInfo(pytx, wlt, self.main,
                                              self.main).exec_()
                                txFound = True
                                break
                        if txFound:
                            break

        self.main = main
        lblHeader = QRichLabel(tr("""<b>Search Armory: </b>"""), doWrap=False)
        self.searchButton = QPushButton("Search")
        self.searchEntry = QLineEdit()
        self.main.connect(self.searchButton, SIGNAL('clicked()'), searchItem)
        topRow = makeHorizFrame(
            [lblHeader, self.searchEntry, self.searchButton, 'stretch'])

        self.searchPanel = makeVertFrame([topRow, 'stretch'])
        # Now set the scrollarea widget to the layout
        self.tabToDisplay = QScrollArea()
        self.tabToDisplay.setWidgetResizable(True)
        self.tabToDisplay.setWidget(self.searchPanel)

    #############################################################################
    def getTabToDisplay(self):
        return self.tabToDisplay
コード例 #4
0
ファイル: driver.py プロジェクト: yakushi/calibre-kobo-driver
    def config_widget(cls):
        from PyQt4.Qt import QCoreApplication
        from PyQt4.Qt import QScrollArea

        cw = super(KOBOTOUCHEXTENDED, cls).config_widget()
        qsa = QScrollArea()
        qsa.setWidgetResizable(True)
        qsa.setWidget(cw)
        qsa.validate = cw.validate
        desktop_geom = QCoreApplication.instance().desktop().availableGeometry()
        if desktop_geom.height() < 800:
            qsa.setBaseSize(qsa.size().width(), desktop_geom.height() - 100)
        return qsa
コード例 #5
0
class PluginObject(object):

   tabName = 'Armory Search'
   maxVersion = '0.93.99'
   
   #############################################################################
   def __init__(self, main):

      def searchItem():
         searchString = str(self.searchEntry.text())
         if len(searchString) > 0:
            likelyDataType = isLikelyDataType(searchString)    
            for wltID, wlt in self.main.walletMap.iteritems():
               if wlt.hasAddr(searchString):
                  searchHash = searchString if likelyDataType == DATATYPE.Hex \
                        else addrStr_to_hash160(searchString)[1]
                  dialog = DlgAddressInfo(wlt, searchHash, main=self.main)
                  dialog.exec_()
                  break
               if likelyDataType == DATATYPE.Hex:
                  walletLedger = wlt.cppWallet.getTxLedger()
                  txHashToFind = hex_to_binary(searchString, endOut=BIGENDIAN)
                  txFound = False
                  for entry in walletLedger:
                     if entry.getTxHash() ==  txHashToFind:
                        cppTx = TheBDM.getTxByHash(txHashToFind)
                        serializedCppTx = cppTx.serialize()
                        pytx = PyTx().unserialize(serializedCppTx)
                        DlgDispTxInfo(pytx, wlt, self.main, self.main).exec_()
                        txFound = True
                        break
                  if txFound:
                     break
            
      self.main = main
      lblHeader = QRichLabel(tr("""<b>Search Armory: </b>"""), doWrap=False)
      self.searchButton = QPushButton("Search")
      self.searchEntry = QLineEdit()
      self.main.connect(self.searchButton, SIGNAL('clicked()'), searchItem)
      topRow =  makeHorizFrame([lblHeader, self.searchEntry, self.searchButton, 'stretch'])


      self.searchPanel = makeVertFrame([topRow, 'stretch' ])
      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(self.searchPanel)

   #############################################################################
   def getTabToDisplay(self):
      return self.tabToDisplay
コード例 #6
0
class PluginObject(object):

   tabName = 'Armory Log'
   maxVersion = '0.93.99'
   
   #############################################################################
   def __init__(self, main):

      self.main = main


      def updateLogDisplay():
         self.pyLogTextDisplay.setText(getLastBytesOfFile(ARMORY_LOG_FILE))
         self.pyLogTextDisplay.moveCursor(QtGui.QTextCursor.End)
         self.cppLogTextDisplay.setText(getLastBytesOfFile(ARMCPP_LOG_FILE))
         self.cppLogTextDisplay.moveCursor(QtGui.QTextCursor.End)

      lblHeader    = QRichLabel(tr("""<b>Log File Display</b>"""), doWrap=False)
      self.updateButton = QPushButton("Update")
      self.main.connect(self.updateButton, SIGNAL('clicked()'), updateLogDisplay)
      topRow =  makeHorizFrame([lblHeader, self.updateButton, 'stretch'])
      
      self.pyLogTextDisplay = self.createLogDisplay()
      self.cppLogTextDisplay = self.createLogDisplay()
      logTabPanel = QTabWidget()
      logTabPanel.addTab(self.pyLogTextDisplay, "Python Log")
      logTabPanel.addTab(self.cppLogTextDisplay, "C++ Log")
      

      self.logFrame = makeVertFrame([topRow, logTabPanel ])

      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(self.logFrame)
      updateLogDisplay()

   def createLogDisplay(self):
      logTextDisplay = QTextEdit()
      logTextDisplay.setFont(GETFONT('Fixed', 8))
      w,h = relaxedSizeNChar(logTextDisplay, 68)[0], int(12 * 8.2)
      logTextDisplay.setMinimumWidth(w)
      logTextDisplay.setMinimumHeight(h)
      logTextDisplay.setReadOnly(True)
      return logTextDisplay

   #############################################################################
   def getTabToDisplay(self):
      return self.tabToDisplay
コード例 #7
0
class PluginObject(object):

    tabName = 'Armory Log'
    maxVersion = '0.93.99'

    #############################################################################
    def __init__(self, main):

        self.main = main

        def updateLogDisplay():
            self.pyLogTextDisplay.setText(getLastBytesOfFile(ARMORY_LOG_FILE))
            self.pyLogTextDisplay.moveCursor(QtGui.QTextCursor.End)
            self.cppLogTextDisplay.setText(getLastBytesOfFile(ARMCPP_LOG_FILE))
            self.cppLogTextDisplay.moveCursor(QtGui.QTextCursor.End)

        lblHeader = QRichLabel(tr("""<b>Log File Display</b>"""), doWrap=False)
        self.updateButton = QPushButton("Update")
        self.main.connect(self.updateButton, SIGNAL('clicked()'),
                          updateLogDisplay)
        topRow = makeHorizFrame([lblHeader, self.updateButton, 'stretch'])

        self.pyLogTextDisplay = self.createLogDisplay()
        self.cppLogTextDisplay = self.createLogDisplay()
        logTabPanel = QTabWidget()
        logTabPanel.addTab(self.pyLogTextDisplay, "Python Log")
        logTabPanel.addTab(self.cppLogTextDisplay, "C++ Log")

        self.logFrame = makeVertFrame([topRow, logTabPanel])

        # Now set the scrollarea widget to the layout
        self.tabToDisplay = QScrollArea()
        self.tabToDisplay.setWidgetResizable(True)
        self.tabToDisplay.setWidget(self.logFrame)
        updateLogDisplay()

    def createLogDisplay(self):
        logTextDisplay = QTextEdit()
        logTextDisplay.setFont(GETFONT('Fixed', 8))
        w, h = relaxedSizeNChar(logTextDisplay, 68)[0], int(12 * 8.2)
        logTextDisplay.setMinimumWidth(w)
        logTextDisplay.setMinimumHeight(h)
        logTextDisplay.setReadOnly(True)
        return logTextDisplay

    #############################################################################
    def getTabToDisplay(self):
        return self.tabToDisplay
コード例 #8
0
   def __init__(self, main):

      self.main = main


      def updateLogDisplay():
         self.pyLogTextDisplay.setText(getLastBytesOfFile(ARMORY_LOG_FILE))
         self.pyLogTextDisplay.moveCursor(QtGui.QTextCursor.End)
         self.cppLogTextDisplay.setText(getLastBytesOfFile(ARMCPP_LOG_FILE))
         self.cppLogTextDisplay.moveCursor(QtGui.QTextCursor.End)

      lblHeader    = QRichLabel(tr("""<b>Log File Display</b>"""), doWrap=False)
      self.updateButton = QPushButton("Update")
      self.main.connect(self.updateButton, SIGNAL('clicked()'), updateLogDisplay)
      topRow =  makeHorizFrame([lblHeader, self.updateButton, 'stretch'])
      
      self.pyLogTextDisplay = self.createLogDisplay()
      self.cppLogTextDisplay = self.createLogDisplay()
      logTabPanel = QTabWidget()
      logTabPanel.addTab(self.pyLogTextDisplay, "Python Log")
      logTabPanel.addTab(self.cppLogTextDisplay, "C++ Log")
      

      self.logFrame = makeVertFrame([topRow, logTabPanel ])

      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(self.logFrame)
      updateLogDisplay()
コード例 #9
0
    def __init__(self, names, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_('Choose master file'))
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        self.la = la = QLabel(
            _('Choose the master file. All selected files will be merged into the master file:'
              ))
        la.setWordWrap(True)
        l.addWidget(la)
        self.sa = sa = QScrollArea(self)
        l.addWidget(sa)
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
        l.addWidget(bb)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.w = w = QWidget(self)
        w.l = QVBoxLayout()
        w.setLayout(w.l)

        buttons = self.buttons = [QRadioButton(n) for n in names]
        buttons[0].setChecked(True)
        map(w.l.addWidget, buttons)
        sa.setWidget(w)

        self.resize(self.sizeHint() + QSize(150, 20))
コード例 #10
0
ファイル: mtp_config.py プロジェクト: siebert/calibre
    def __init__(self, device, rules):
        QGroupBox.__init__(self, _('Format specific sending'))
        self._device = weakref.ref(device)
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        self.la = la = QLabel(
            '<p>' +
            _('''You can create rules that control where ebooks of a specific
            format are sent to on the device. These will take precedence over
            the folders specified above.'''))
        la.setWordWrap(True)
        l.addWidget(la)
        self.sa = sa = QScrollArea(self)
        sa.setWidgetResizable(True)
        self.w = w = QWidget(self)
        w.l = QVBoxLayout()
        w.setLayout(w.l)
        sa.setWidget(w)
        l.addWidget(sa)
        self.widgets = []
        for rule in rules:
            r = Rule(device, rule)
            self.widgets.append(r)
            w.l.addWidget(r)
            r.remove.connect(self.remove_rule)

        if not self.widgets:
            self.add_rule()

        self.b = b = QPushButton(QIcon(I('plus.png')), _('Add a &new rule'))
        l.addWidget(b)
        b.clicked.connect(self.add_rule)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)
コード例 #11
0
ファイル: live_css.py プロジェクト: mrmac123/calibre
    def __init__(self, preview, parent=None):
        QWidget.__init__(self, parent)
        self.preview = preview
        self.preview_is_refreshing = False
        self.refresh_needed = False
        preview.refresh_starting.connect(self.preview_refresh_starting)
        preview.refreshed.connect(self.preview_refreshed)
        self.apply_theme()
        self.setAutoFillBackground(True)
        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_data)
        self.update_timer.setSingleShot(True)
        self.update_timer.setInterval(500)
        self.now_showing = (None, None, None)

        self.stack = s = QStackedLayout(self)
        self.setLayout(s)

        self.clear_label = la = QLabel('<h3>' + _(
            'No style information found') + '</h3><p>' + _(
                'Move the cursor inside a HTML tag to see what styles'
                ' apply to that tag.'))
        la.setWordWrap(True)
        la.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        s.addWidget(la)

        self.box = box = Box(self)
        box.hyperlink_activated.connect(self.goto_declaration, type=Qt.QueuedConnection)
        self.scroll = sc = QScrollArea(self)
        sc.setWidget(box)
        sc.setWidgetResizable(True)
        s.addWidget(sc)
コード例 #12
0
ファイル: single.py プロジェクト: Hainish/calibre
    def setupUi(self, *args):  # {{{
        self.resize(990, 670)

        self.download_shortcut = QShortcut(self)
        self.download_shortcut.setKey(QKeySequence('Ctrl+D',
            QKeySequence.PortableText))
        p = self.parent()
        if hasattr(p, 'keyboard'):
            kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download'
            sc = p.keyboard.keys_map.get(kname, None)
            if sc:
                self.download_shortcut.setKey(sc[0])

        self.button_box = bb = QDialogButtonBox(self)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
                self)
        self.next_button.setShortcut(QKeySequence('Alt+Right'))
        self.next_button.clicked.connect(self.next_clicked)
        self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'),
                self)
        self.prev_button.setShortcut(QKeySequence('Alt+Left'))

        self.button_box.addButton(self.prev_button, bb.ActionRole)
        self.button_box.addButton(self.next_button, bb.ActionRole)
        self.prev_button.clicked.connect(self.prev_clicked)
        bb.setStandardButtons(bb.Ok|bb.Cancel)
        bb.button(bb.Ok).setDefault(True)

        self.scroll_area = QScrollArea(self)
        self.scroll_area.setFrameShape(QScrollArea.NoFrame)
        self.scroll_area.setWidgetResizable(True)
        self.central_widget = QTabWidget(self)
        self.scroll_area.setWidget(self.central_widget)

        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.l.addWidget(self.scroll_area)
        ll = self.button_box_layout = QHBoxLayout()
        self.l.addLayout(ll)
        ll.addSpacing(10)
        ll.addWidget(self.button_box)

        self.setWindowIcon(QIcon(I('edit_input.png')))
        self.setWindowTitle(BASE_TITLE)

        self.create_basic_metadata_widgets()

        if len(self.db.custom_column_label_map):
            self.create_custom_metadata_widgets()

        self.do_layout()
        geom = gprefs.get('metasingle_window_geometry3', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))
コード例 #13
0
    def __init__(self, parent=None):
        QScrollArea.__init__(self, parent)
        self.setWidgetResizable(True)

        category_map, category_names = {}, {}
        for plugin in preferences_plugins():
            if plugin.category not in category_map:
                category_map[plugin.category] = plugin.category_order
            if category_map[plugin.category] < plugin.category_order:
                category_map[plugin.category] = plugin.category_order
            if plugin.category not in category_names:
                category_names[plugin.category] = (plugin.gui_category
                                                   if plugin.gui_category else
                                                   plugin.category)

        self.category_names = category_names

        categories = list(category_map.keys())
        categories.sort(cmp=lambda x, y: cmp(category_map[x], category_map[y]))

        self.category_map = OrderedDict()
        for c in categories:
            self.category_map[c] = []

        for plugin in preferences_plugins():
            self.category_map[plugin.category].append(plugin)

        for plugins in self.category_map.values():
            plugins.sort(cmp=lambda x, y: cmp(x.name_order, y.name_order))

        self.widgets = []
        self._layout = QVBoxLayout()
        self.container = QWidget(self)
        self.container.setLayout(self._layout)
        self.setWidget(self.container)

        for name, plugins in self.category_map.items():
            w = Category(name, plugins, self.category_names[name], parent=self)
            self.widgets.append(w)
            self._layout.addWidget(w)
            w.plugin_activated.connect(self.show_plugin.emit)
コード例 #14
0
    def setup_ui(self):
        self.block_show = False
        self.properties = []
        self.l = l  = QVBoxLayout(self)
        self.setLayout(l)
        h = QHBoxLayout()
        l.addLayout(h)
        self.la = la = QLabel(_('&Edit theme:'))
        h.addWidget(la)
        self.theme = t = QComboBox(self)
        la.setBuddy(t)
        t.addItems(sorted(custom_theme_names()))
        t.setMinimumWidth(200)
        if t.count() > 0:
            t.setCurrentIndex(0)
        t.currentIndexChanged[int].connect(self.show_theme)
        h.addWidget(t)

        self.add_button = b = QPushButton(QIcon(I('plus.png')), _('Add &new theme'), self)
        b.clicked.connect(self.create_new_theme)
        h.addWidget(b)

        self.remove_button = b = QPushButton(QIcon(I('minus.png')), _('&Remove theme'), self)
        b.clicked.connect(self.remove_theme)
        h.addWidget(b)
        h.addStretch(1)

        self.scroll = s = QScrollArea(self)
        self.w = w = QWidget(self)
        s.setWidget(w), s.setWidgetResizable(True)
        self.cl = cl = QVBoxLayout()
        w.setLayout(cl)

        from calibre.gui2.tweak_book.editor.text import TextEdit
        self.preview = p = TextEdit(self, expected_geometry=(73, 50))
        p.load_text(HELP_TEXT.format(
                *['<b>%s</b>' % x for x in (
                    'Normal', 'Visual', 'CursorLine', 'LineNr', 'MatchParen',
                    'Function', 'Type', 'Statement', 'Constant', 'SpecialCharacter', 'Error', 'SpellError', 'Comment'
                )]
            ))
        p.setMaximumWidth(p.size_hint.width() + 5)
        s.setMinimumWidth(600)
        self.splitter = sp = QSplitter(self)
        l.addWidget(sp)
        sp.addWidget(s), sp.addWidget(p)

        self.bb.clear()
        self.bb.addButton(self.bb.Close)
        l.addWidget(self.bb)

        if self.theme.count() > 0:
            self.show_theme()
コード例 #15
0
ファイル: main.py プロジェクト: Eksmo/calibre
    def __init__(self, parent=None):
        QScrollArea.__init__(self, parent)
        self.setWidgetResizable(True)

        category_map, category_names = {}, {}
        for plugin in preferences_plugins():
            if plugin.category not in category_map:
                category_map[plugin.category] = plugin.category_order
            if category_map[plugin.category] < plugin.category_order:
                category_map[plugin.category] = plugin.category_order
            if plugin.category not in category_names:
                category_names[plugin.category] = (plugin.gui_category if
                    plugin.gui_category else plugin.category)

        self.category_names = category_names

        categories = list(category_map.keys())
        categories.sort(cmp=lambda x, y: cmp(category_map[x], category_map[y]))

        self.category_map = OrderedDict()
        for c in categories:
            self.category_map[c] = []

        for plugin in preferences_plugins():
            self.category_map[plugin.category].append(plugin)

        for plugins in self.category_map.values():
            plugins.sort(cmp=lambda x, y: cmp(x.name_order, y.name_order))

        self.widgets = []
        self._layout = QVBoxLayout()
        self.container = QWidget(self)
        self.container.setLayout(self._layout)
        self.setWidget(self.container)

        for name, plugins in self.category_map.items():
            w = Category(name, plugins, self.category_names[name], parent=self)
            self.widgets.append(w)
            self._layout.addWidget(w)
            w.plugin_activated.connect(self.show_plugin.emit)
コード例 #16
0
ファイル: image_popup.py プロジェクト: sss/calibre
    def __init__(self,
                 parent,
                 current_img,
                 current_url,
                 geom_name='viewer_image_popup_geometry'):
        QDialog.__init__(self)
        dw = QApplication.instance().desktop()
        self.avail_geom = dw.availableGeometry(parent)
        self.current_img = current_img
        self.current_url = current_url
        self.factor = 1.0
        self.geom_name = geom_name

        self.label = l = QLabel()
        l.setBackgroundRole(QPalette.Base)
        l.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        l.setScaledContents(True)

        self.scrollarea = sa = QScrollArea()
        sa.setBackgroundRole(QPalette.Dark)
        sa.setWidget(l)

        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Close)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.zi_button = zi = bb.addButton(_('Zoom &in'), bb.ActionRole)
        self.zo_button = zo = bb.addButton(_('Zoom &out'), bb.ActionRole)
        self.save_button = so = bb.addButton(_('&Save as'), bb.ActionRole)
        self.rotate_button = ro = bb.addButton(_('&Rotate'), bb.ActionRole)
        zi.setIcon(QIcon(I('plus.png')))
        zo.setIcon(QIcon(I('minus.png')))
        so.setIcon(QIcon(I('save.png')))
        ro.setIcon(QIcon(I('rotate-right.png')))
        zi.clicked.connect(self.zoom_in)
        zo.clicked.connect(self.zoom_out)
        so.clicked.connect(self.save_image)
        ro.clicked.connect(self.rotate_image)

        self.l = l = QVBoxLayout()
        self.setLayout(l)
        l.addWidget(sa)
        l.addWidget(bb)
コード例 #17
0
ファイル: themes.py プロジェクト: dusual/calibre
    def setup_ui(self):
        self.block_show = False
        self.properties = []
        self.l = l = QVBoxLayout(self)
        self.setLayout(l)
        h = QHBoxLayout()
        l.addLayout(h)
        self.la = la = QLabel(_('&Edit theme:'))
        h.addWidget(la)
        self.theme = t = QComboBox(self)
        la.setBuddy(t)
        t.addItems(sorted(custom_theme_names()))
        t.setMinimumWidth(200)
        if t.count() > 0:
            t.setCurrentIndex(0)
        t.currentIndexChanged[int].connect(self.show_theme)
        h.addWidget(t)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                                          _('Add &new theme'), self)
        b.clicked.connect(self.create_new_theme)
        h.addWidget(b)

        self.remove_button = b = QPushButton(QIcon(I('minus.png')),
                                             _('&Remove theme'), self)
        b.clicked.connect(self.remove_theme)
        h.addWidget(b)
        h.addStretch(1)

        self.scroll = s = QScrollArea(self)
        self.w = w = QWidget(self)
        s.setWidget(w), s.setWidgetResizable(True)
        self.cl = cl = QVBoxLayout()
        w.setLayout(cl)

        from calibre.gui2.tweak_book.editor.text import TextEdit
        self.preview = p = TextEdit(self, expected_geometry=(73, 50))
        p.load_text(
            textwrap.dedent(
                _('''\
            <h2>Creating a custom theme</h2>

            <p id="attribute" lang="und">You can create a custom syntax highlighting
            theme, with your own colors and font styles. The most important
            types of highlighting rules are described below. Note that not
            every rule supports every kind of customization, for example,
            changing font or underline styles for the <code>Cursor</code> rule
            does not have any effect as that rule is used only for the color of
            the blinking cursor.</p>

            <p>As you make changes to your them on the left, the changes will
            be reflected live in this panel.</p>

            <p xml:lang="und">
            {0}
                The most important rule. Sets the
                foreground and background colors for the editor as well as the
                style of "normal" text, that is, text that does not match any
                special syntax.

            {1}
                Defines the colors for text selected by the mouse.

            {2}
                Defines the color for the line containing the cursor.

            {3}
                Defines the colors for the line numbers on the left.

            {4}
                Defines the colors for matching tags in HTML and matching
                braces in CSS.

            {5}
                Used for highlighting tags in HTML

            {6}
                Used for highlighting attributes in HTML

            {7}
                Tag names in HTML

            {8}
                Namespace prefixes in XML and constants in CSS

            {9}
                Non-breaking spaces/hyphens in HTML

            {10}
                Syntax errors such as <this <>

            {11}
                Misspelled words such as <span lang="en">thisword</span>

            {12}
                Comments like <!-- this one -->
            </p>

            <style type="text/css">
            /* Some CSS so you can see how the highlighting rules affect it */

            p.someclass {{
                font-family: serif;
                font-size: 12px;
                line-height: 1.2;
            }}
            </style>
            ''')).format(*[
                    '<b>%s</b>' % x
                    for x in ('Normal', 'Visual', 'CursorLine', 'LineNr',
                              'MatchParen', 'Function', 'Type', 'Statement',
                              'Constant', 'SpecialCharacter', 'Error',
                              'SpellError', 'Comment')
                ]))
        p.setMaximumWidth(p.size_hint.width() + 5)
        s.setMinimumWidth(600)
        self.splitter = sp = QSplitter(self)
        l.addWidget(sp)
        sp.addWidget(s), sp.addWidget(p)

        self.bb.clear()
        self.bb.addButton(self.bb.Close)
        l.addWidget(self.bb)

        if self.theme.count() > 0:
            self.show_theme()
コード例 #18
0
ファイル: copy_to_library.py プロジェクト: siebert/calibre
def ask_about_cc_mismatch(gui, db, newdb, missing_cols, incompatible_cols):  # {{{
    source_metadata = db.field_metadata.custom_field_metadata(include_composites=True)
    ndbname = os.path.basename(newdb.library_path)

    d = QDialog(gui)
    d.setWindowTitle(_('Different custom columns'))
    l = QFormLayout()
    tl = QVBoxLayout()
    d.setLayout(tl)
    d.s = QScrollArea(d)
    tl.addWidget(d.s)
    d.w = QWidget(d)
    d.s.setWidget(d.w)
    d.s.setWidgetResizable(True)
    d.w.setLayout(l)
    d.setMinimumWidth(600)
    d.setMinimumHeight(500)
    d.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)

    msg = _('The custom columns in the <i>{0}</i> library are different from the '
        'custom columns in the <i>{1}</i> library. As a result, some metadata might not be copied.').format(
        os.path.basename(db.library_path), ndbname)
    d.la = la = QLabel(msg)
    la.setWordWrap(True)
    la.setStyleSheet('QLabel { margin-bottom: 1.5ex }')
    l.addRow(la)
    if incompatible_cols:
        la = d.la2 = QLabel(_('The following columns are incompatible - they have the same name'
                ' but different data types. They will be ignored: ') +
                    ', '.join(sorted(incompatible_cols, key=sort_key)))
        la.setWordWrap(True)
        la.setStyleSheet('QLabel { margin-bottom: 1.5ex }')
        l.addRow(la)

    missing_widgets = []
    if missing_cols:
        la = d.la3 = QLabel(_('The following columns are missing in the <i>{0}</i> library.'
                                ' You can choose to add them automatically below.').format(
                                    ndbname))
        la.setWordWrap(True)
        l.addRow(la)
        for k in missing_cols:
            widgets = (k, QCheckBox(_('Add to the %s library') % ndbname))
            l.addRow(QLabel(k), widgets[1])
            missing_widgets.append(widgets)
    d.la4 = la = QLabel(_('This warning is only shown once per library, per session'))
    la.setWordWrap(True)
    tl.addWidget(la)

    tl.addWidget(d.bb)
    d.bb.accepted.connect(d.accept)
    d.bb.rejected.connect(d.reject)
    d.resize(d.sizeHint())
    if d.exec_() == d.Accepted:
        for k, cb in missing_widgets:
            if cb.isChecked():
                col_meta = source_metadata[k]
                newdb.create_custom_column(
                            col_meta['label'], col_meta['name'], col_meta['datatype'],
                            len(col_meta['is_multiple']) > 0,
                            col_meta['is_editable'], col_meta['display'])
        return True
    return False
コード例 #19
0
   def __init__(self, main):

      def updateDustLimit():
         try:
            self.dustTableModel.updateDustList(self.getSelectedWlt(),
                  str2coin(self.dustLimitText.text()))
            self.beGoneDustButton.setEnabled(len(self.dustTableModel.dustTxOutlist)>0)
            if self.dustTableModel.wlt:
               self.lblHeader.setText(tr("""<b>Dust Outputs for Wallet: %s</b>""" 
                     % self.dustTableModel.wlt.labelName))
         except NegativeValueError:
            pass
         except TooMuchPrecisionError:
            pass
         except:
            LOGEXCEPT("Unexpected exception")
            pass

      def sendDust():
         try:
            utxiList = []
            for utxo in self.dustTableModel.dustTxOutlist:
               # The PyCreateAndSignTx method require PyTx and PyBtcAddress objects
               rawTx = TheBDM.getTxByHash(utxo.getTxHash()).serialize()
               a160 = CheckHash160(utxo.getRecipientScrAddr())
               for pyAddr in self.dustTableModel.wlt.addrMap.values():
                  if a160 == pyAddr.getAddr160():
                     pubKey = pyAddr.binPublicKey65.toBinStr()
                     txoIdx = utxo.getTxOutIndex()
                     utxiList.append(UnsignedTxInput(rawTx, txoIdx, None, pubKey))
                     break
            # Make copies, destroy them in the finally clause
            privKeyMap = {}
            for addrObj in self.dustTableModel.wlt.addrMap.values():
               scrAddr = SCRADDR_P2PKH_BYTE + addrObj.getAddr160()
               if self.dustTableModel.wlt.useEncryption and self.dustTableModel.wlt.isLocked:
                  # Target wallet is encrypted...
                  unlockdlg = DlgUnlockWallet(self.dustTableModel.wlt,
                        self.main, self.main, 'Unlock Wallet to Import')
                  if not unlockdlg.exec_():
                     QMessageBox.critical(self, 'Wallet is Locked', \
                        'Cannot send dust without unlocking the wallet!', \
                        QMessageBox.Ok)
                     return
               privKeyMap[scrAddr] = addrObj.binPrivKey32_Plain.copy()
            signedTx = PyCreateAndSignTx(utxiList,
                  [],
                  privKeyMap, SIGHASH_NONE|SIGHASH_ANYONECANPAY )
            
            print "-------------"
            print binary_to_hex(signedTx.serialize())
            
            # sock = socket.create_connection(('dust-b-gone.bitcoin.petertodd.org',80))
            # sock.send(signedTx.serialize())
            # sock.send(b'\n')
            # sock.close()
                  

         except socket.error as err:
            QMessageBox.critical(self.main, tr('Negative Value'), tr("""
               Failed to connect to dust-b-gone server: %s""" % err.strerror), QMessageBox.Ok)            
         except NegativeValueError:
            QMessageBox.critical(self.main, tr('Negative Value'), tr("""
               You must enter a positive value of at least 0.0000 0001 
               and less than %s for the dust limit.""" % MAX_DUST_LIMIT_STR), QMessageBox.Ok)
         except TooMuchPrecisionError:
            QMessageBox.critical(self.main.main, tr('Too much precision'), tr("""
               Bitcoins can only be specified down to 8 decimal places. 
               The smallest unit of a Groestlcoin is 0.0000 0001 GRS.
               Please enter a dust limit of at least 0.0000 0001 and less than %s.""" % MAX_DUST_LIMIT_STR), QMessageBox.Ok)
         finally:
            for scraddr in privKeyMap:
               privKeyMap[scraddr].destroy()
         
         
         
          
      self.main = main
 
      self.lblHeader    = QRichLabel(tr("""<b>Dust Outputs for Wallet: None Selected</b>"""), doWrap=False)
      self.beGoneDustButton = QPushButton("Remove Dust")
      self.beGoneDustButton.setEnabled(False)
      self.main.connect(self.beGoneDustButton, SIGNAL('clicked()'), sendDust)
      topRow =  makeHorizFrame([self.lblHeader,'stretch'])
      secondRow =  makeHorizFrame([self.beGoneDustButton, 'stretch'])
      
      self.dustLimitLabel = QLabel("Max Dust Value (GRS): ")
      self.dustLimitText = QLineEdit()
      self.dustLimitText.setFont(GETFONT('Fixed'))
      self.dustLimitText.setMinimumWidth(tightSizeNChar(self.dustLimitText, 6)[0])
      self.dustLimitText.setMaximumWidth(tightSizeNChar(self.dustLimitText, 12)[0])
      self.dustLimitText.setAlignment(Qt.AlignRight)
      self.dustLimitText.setText(coin2str(DEFAULT_DUST_LIMIT))
      self.main.connect(self.dustLimitText, SIGNAL('textChanged(QString)'), updateDustLimit)
      
      
      limitPanel = makeHorizFrame([self.dustLimitLabel, self.dustLimitText, 'stretch'])
      
      
      self.dustTableModel = DustDisplayModel()
      self.dustTableView = QTableView()
      self.dustTableView.setModel(self.dustTableModel)
      self.dustTableView.setSelectionMode(QTableView.NoSelection)
      self.dustTableView.verticalHeader().setDefaultSectionSize(20)
      self.dustTableView.verticalHeader().hide()
      h = tightSizeNChar(self.dustTableView, 1)[1]
      self.dustTableView.setMinimumHeight(2 * (1.3 * h))
      self.dustTableView.setMaximumHeight(10 * (1.3 * h))
      initialColResize(self.dustTableView, [100, .7, .3])

      self.dustTableView.setContextMenuPolicy(Qt.CustomContextMenu)

      self.lblTxioInfo = QRichLabel('')
      self.lblTxioInfo.setMinimumWidth(tightSizeNChar(self.lblTxioInfo, 30)[0])
      
      self.main.connect(self.main.walletsView, SIGNAL('clicked(QModelIndex)'), 
                   updateDustLimit)

      self.dustBGoneFrame = makeVertFrame([topRow, secondRow, limitPanel, self.dustTableView, 'stretch'])

      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(self.dustBGoneFrame)
コード例 #20
0
ファイル: single.py プロジェクト: Hainish/calibre
    def do_layout(self):
        self.central_widget.clear()
        self.labels = []
        sto = QWidget.setTabOrder

        self.central_widget.tabBar().setVisible(False)
        tab0 = QWidget(self)
        self.central_widget.addTab(tab0, _("&Metadata"))
        l = QGridLayout()
        tab0.setLayout(l)

        # Basic metadata in col 0
        tl = QGridLayout()
        gb = QGroupBox(_('Basic metadata'), tab0)
        l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        # Custom metadata in col 1
        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('Custom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(gb)
            sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            l.addWidget(gb, 0, 1, 1, 1)
            sp = QSizePolicy()
            sp.setVerticalStretch(10)
            sp.setHorizontalPolicy(QSizePolicy.Minimum)
            sp.setVerticalPolicy(QSizePolicy.Expanding)
            gb.setSizePolicy(sp)
            self.set_custom_metadata_tab_order()

        # comments span col 0 & 1
        w = QGroupBox(_('Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        lb = QHBoxLayout()
        w.setLayout(lb)
        lb.addWidget(self.comments)
        l.addWidget(w, 1, 0, 1, 2)

        # Cover & formats in col 3
        gb = QGroupBox(_('Cover'), tab0)
        lb = QGridLayout()
        gb.setLayout(lb)
        lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
        sto(self.manage_authors_button, self.cover.buttons[0])
        for i, b in enumerate(self.cover.buttons[:3]):
            lb.addWidget(b, 1, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        lb.addLayout(hl, 2, 0, 1, 3)
        l.addWidget(gb, 0, 2, 1, 1)
        l.addWidget(self.formats_manager, 1, 2, 1, 1)
        sto(self.cover.buttons[-1], self.formats_manager)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(32, 32))
コード例 #21
0
    def __init__(self, main):
        def updateDustLimit():
            try:
                self.dustTableModel.updateDustList(
                    self.getSelectedWlt(), str2coin(self.dustLimitText.text()))
                self.beGoneDustButton.setEnabled(
                    len(self.dustTableModel.dustTxOutlist) > 0)
                if self.dustTableModel.wlt:
                    self.lblHeader.setText(
                        tr("""<b>Dust Outputs for Wallet: %s</b>""" %
                           self.dustTableModel.wlt.labelName))
            except NegativeValueError:
                pass
            except TooMuchPrecisionError:
                pass
            except:
                LOGEXCEPT("Unexpected exception")
                pass

        def sendDust():
            try:
                utxiList = []
                for utxo in self.dustTableModel.dustTxOutlist:
                    # The PyCreateAndSignTx method require PyTx and PyBtcAddress objects
                    rawTx = TheBDM.getTxByHash(utxo.getTxHash()).serialize()
                    a160 = CheckHash160(utxo.getRecipientScrAddr())
                    for pyAddr in self.dustTableModel.wlt.addrMap.values():
                        if a160 == pyAddr.getAddr160():
                            pubKey = pyAddr.binPublicKey65.toBinStr()
                            txoIdx = utxo.getTxOutIndex()
                            utxiList.append(
                                UnsignedTxInput(rawTx, txoIdx, None, pubKey))
                            break
                # Make copies, destroy them in the finally clause
                privKeyMap = {}
                for addrObj in self.dustTableModel.wlt.addrMap.values():
                    scrAddr = SCRADDR_P2PKH_BYTE + addrObj.getAddr160()
                    if self.dustTableModel.wlt.useEncryption and self.dustTableModel.wlt.isLocked:
                        # Target wallet is encrypted...
                        unlockdlg = DlgUnlockWallet(self.dustTableModel.wlt,
                                                    self.main, self.main,
                                                    'Unlock Wallet to Import')
                        if not unlockdlg.exec_():
                            QMessageBox.critical(self, 'Wallet is Locked', \
                               'Cannot send dust without unlocking the wallet!', \
                               QMessageBox.Ok)
                            return
                    privKeyMap[scrAddr] = addrObj.binPrivKey32_Plain.copy()
                signedTx = PyCreateAndSignTx(
                    utxiList, [], privKeyMap,
                    SIGHASH_NONE | SIGHASH_ANYONECANPAY)

                print "-------------"
                print binary_to_hex(signedTx.serialize())

                # sock = socket.create_connection(('dust-b-gone.bitcoin.petertodd.org',80))
                # sock.send(signedTx.serialize())
                # sock.send(b'\n')
                # sock.close()

            except socket.error as err:
                QMessageBox.critical(
                    self.main, tr('Negative Value'),
                    tr("""
               Failed to connect to dust-b-gone server: %s""" % err.strerror),
                    QMessageBox.Ok)
            except NegativeValueError:
                QMessageBox.critical(
                    self.main, tr('Negative Value'),
                    tr("""
               You must enter a positive value of at least 0.0000 0001 
               and less than %s for the dust limit.""" % MAX_DUST_LIMIT_STR),
                    QMessageBox.Ok)
            except TooMuchPrecisionError:
                QMessageBox.critical(
                    self.main.main, tr('Too much precision'),
                    tr("""
               Bitcoins can only be specified down to 8 decimal places. 
               The smallest unit of a Bitcoin is 0.0000 0001 BTC. 
               Please enter a dust limit of at least 0.0000 0001 and less than %s."""
                       % MAX_DUST_LIMIT_STR), QMessageBox.Ok)
            finally:
                for scraddr in privKeyMap:
                    privKeyMap[scraddr].destroy()

        self.main = main

        self.lblHeader = QRichLabel(
            tr("""<b>Dust Outputs for Wallet: None Selected</b>"""),
            doWrap=False)
        self.beGoneDustButton = QPushButton("Remove Dust")
        self.beGoneDustButton.setEnabled(False)
        self.main.connect(self.beGoneDustButton, SIGNAL('clicked()'), sendDust)
        topRow = makeHorizFrame([self.lblHeader, 'stretch'])
        secondRow = makeHorizFrame([self.beGoneDustButton, 'stretch'])

        self.dustLimitLabel = QLabel("Max Dust Value (BTC): ")
        self.dustLimitText = QLineEdit()
        self.dustLimitText.setFont(GETFONT('Fixed'))
        self.dustLimitText.setMinimumWidth(
            tightSizeNChar(self.dustLimitText, 6)[0])
        self.dustLimitText.setMaximumWidth(
            tightSizeNChar(self.dustLimitText, 12)[0])
        self.dustLimitText.setAlignment(Qt.AlignRight)
        self.dustLimitText.setText(coin2str(DEFAULT_DUST_LIMIT))
        self.main.connect(self.dustLimitText, SIGNAL('textChanged(QString)'),
                          updateDustLimit)

        limitPanel = makeHorizFrame(
            [self.dustLimitLabel, self.dustLimitText, 'stretch'])

        self.dustTableModel = DustDisplayModel()
        self.dustTableView = QTableView()
        self.dustTableView.setModel(self.dustTableModel)
        self.dustTableView.setSelectionMode(QTableView.NoSelection)
        self.dustTableView.verticalHeader().setDefaultSectionSize(20)
        self.dustTableView.verticalHeader().hide()
        h = tightSizeNChar(self.dustTableView, 1)[1]
        self.dustTableView.setMinimumHeight(2 * (1.3 * h))
        self.dustTableView.setMaximumHeight(10 * (1.3 * h))
        initialColResize(self.dustTableView, [100, .7, .3])

        self.dustTableView.setContextMenuPolicy(Qt.CustomContextMenu)

        self.lblTxioInfo = QRichLabel('')
        self.lblTxioInfo.setMinimumWidth(
            tightSizeNChar(self.lblTxioInfo, 30)[0])

        self.main.connect(self.main.walletsView,
                          SIGNAL('clicked(QModelIndex)'), updateDustLimit)

        self.dustBGoneFrame = makeVertFrame(
            [topRow, secondRow, limitPanel, self.dustTableView, 'stretch'])

        # Now set the scrollarea widget to the layout
        self.tabToDisplay = QScrollArea()
        self.tabToDisplay.setWidgetResizable(True)
        self.tabToDisplay.setWidget(self.dustBGoneFrame)
コード例 #22
0
   def __init__(self, main):
      self.searchThread = None
      self.passPhraseFinder = None
      self.isSearchOver = False
      
      def updateResults(resultStr):
         self.resultStr += resultStr
         
      def updateDisplay():
         if len(self.resultStr) > 0:
            self.resultsDisplay.append(self.resultStr)
            self.resultStr = ''
            self.resultsDisplay.moveCursor(QtGui.QTextCursor.End)
            self.resultsDisplay.repaint()
         if self.isSearchOver:
            endSearch()
      
      # Call this from another thread to end the search
      def terminateSearch():
         self.isSearchOver = True
      
      # Call this from the main thread to end the search
      def endSearch():
         self.main.extraHeartbeatAlways.remove(updateDisplay)
         self.searchButton.setEnabled(True)
         self.stopButton.setEnabled(False)
         # If the thread is still searching tell the pass phrase finder to stop
         if self.passPhraseFinder and self.searchThread  and not self.searchThread.isFinished():
            self.passPhraseFinder.isStopped = True
            
      def searchForPassphrase():
         # Get the selected wallet from the main screen
         wlt = self.getSelectedWlt()
         if wlt and not wlt.watchingOnly and wlt.isLocked:
            self.resultStr = ''
            self.passPhraseFinder = PassPhraseFinder(wlt)
            self.resultsDisplay.setText(QString(''))
            self.main.extraHeartbeatAlways.append(updateDisplay)
            if len(self.segOrdStrSet) > 0:
               # From self.segOrdStrList, create a list of lists of indexes that describe the segment orderings to search
               # In other words convert all of the strings in orderings list to lists of integers
               segOrdIntListList = []
               for ordStr in self.segOrdStrSet:
                  # The indexes provided by the users are 1 based, and the list indexes ought to be 0 based
                  segOrdIntListList.append([int(indexStr)-1 for indexStr in ordStr.split(',')])
               self.searchThread = PyBackgroundThread(self.passPhraseFinder.searchForPassPhrase, 
                  [segDef.getSegList() for segDef in self.segDefList], 
                  segOrdIntListList, 
                  updateResults,
                  terminateSearch )
               # Reset the isSearchOver flag
               self.isSearchOver = False
               self.searchThread.start()
               
               # Disable search button adn enabled stop button
               self.stopButton.setEnabled(True)
               self.searchButton.setEnabled(False)
            else:
               QMessageBox.warning(self.main, tr('Invalid'), tr("""
                  There are no valid segment combinations to search.
                  Please add at least one segment and ordering to search."""), QMessageBox.Ok)
         else:
            QMessageBox.warning(self.main, tr('Invalid'), tr("""
               No valid wallet is selected. Please select a locked
               non-watching-only from Available Wallets."""), QMessageBox.Ok)

      def addKnownSegment():
         dlgEnterSegment = DlgEnterSegment(main, main)
         if dlgEnterSegment.exec_():
            segmentText = str(dlgEnterSegment.editSegment.text())
            if len(segmentText)>0:
               self.segDefList.append(KnownSeg(segmentText))
               self.segDefTableModel.updateSegList(self.segDefList)
      
      def addUnknownCaseSegment():
         dlgEnterSegment = DlgEnterSegment(main, main)
         if dlgEnterSegment.exec_():
            segmentText = str(dlgEnterSegment.editSegment.text())
            if len(segmentText)>0:
               self.segDefList.append(UnknownCaseSeg(segmentText))
               self.segDefTableModel.updateSegList(self.segDefList)
               
      def addUnknownOrderSegment():
         dlgEnterSegment = DlgEnterSegment(main, main, isUnknownOrder=True)
         if dlgEnterSegment.exec_():
            segmentText = str(dlgEnterSegment.editSegment.text())
            minLen = int(str(dlgEnterSegment.minSelector.currentText()))
            maxLen = int(str(dlgEnterSegment.maxSelector.currentText()))
            if len(segmentText)>0:
               self.segDefList.append(UnknownSeg(segmentText, minLen, maxLen))
               self.segDefTableModel.updateSegList(self.segDefList)

      def addOrdering():
         if len(self.segDefList) > 0:
            dlgSpecifyOrdering = DlgSpecifyOrdering(main, main, len(self.segDefList))     
            if dlgSpecifyOrdering.exec_():
               self.segOrdStrSet.add(str(dlgSpecifyOrdering.parseOrderingList()).strip('[]'))
               self.updateOrderingListBox()
         else:
            QMessageBox.warning(self.main, tr('Not Ready'), tr("""
               No segments have been entered. You must enter some segments before you can order them."""), QMessageBox.Ok)

      
      self.main = main
      self.segDefList = []
      self.segOrdStrSet = set()
      segmentHeader = QRichLabel(tr("""<b>Build segments for pass phrase search: </b>"""), doWrap=False)
      self.knownButton = QPushButton("Add Known Segment")
      self.unknownCaseButton = QPushButton("Add Unknown Case Segment")
      self.unknownOrderButton = QPushButton("Add Unknown Order Segment")
      self.main.connect(self.knownButton, SIGNAL('clicked()'), addKnownSegment)
      self.main.connect(self.unknownCaseButton, SIGNAL('clicked()'), addUnknownCaseSegment)
      self.main.connect(self.unknownOrderButton, SIGNAL('clicked()'), addUnknownOrderSegment)
      topRow =  makeHorizFrame([segmentHeader, self.knownButton, self.unknownCaseButton, self.unknownOrderButton, 'stretch'])
      
      self.segDefTableModel = SegDefDisplayModel()
      self.segDefTableView = QTableView()
      self.segDefTableView.setModel(self.segDefTableModel)
      self.segDefTableView.setSelectionBehavior(QTableView.SelectRows)
      self.segDefTableView.setSelectionMode(QTableView.SingleSelection)
      self.segDefTableView.verticalHeader().setDefaultSectionSize(20)
      self.segDefTableView.verticalHeader().hide()
      
      h = tightSizeNChar(self.segDefTableView, 1)[1]
      self.segDefTableView.setMinimumHeight(2 * (1.3 * h))
      self.segDefTableView.setMaximumHeight(10 * (1.3 * h))      
      initialColResize(self.segDefTableView, [.1, .2, .4, .1, .1, .1])

      self.segDefTableView.customContextMenuRequested.connect(self.showSegContextMenu)
      self.segDefTableView.setContextMenuPolicy(Qt.CustomContextMenu)
      
      segmentOrderingsHeader = QRichLabel(tr("""<b>Specify orderings for pass phrase search: </b>"""), doWrap=False)
      self.addOrderingButton = QPushButton("Add Ordering")
      
      
      self.main.connect(self.addOrderingButton, SIGNAL('clicked()'), addOrdering)
      orderingButtonPanel = makeHorizFrame([segmentOrderingsHeader, self.addOrderingButton, 'stretch'])

      self.segOrdListBox  = QListWidget()
      
      self.segOrdListBox.customContextMenuRequested.connect(self.showOrdContextMenu)
      self.segOrdListBox.setContextMenuPolicy(Qt.CustomContextMenu)
      
      
      self.searchButton = QPushButton("Search")
      self.main.connect(self.searchButton, SIGNAL('clicked()'), searchForPassphrase)
      self.stopButton = QPushButton("Stop Searching")
      self.stopButton.setEnabled(False)
      self.main.connect(self.stopButton, SIGNAL('clicked()'), endSearch)
      totalSearchLabel = QRichLabel(tr("""<b>Total Passphrase Tries To Search: </b>"""), doWrap=False)
      self.totalSearchTriesDisplay = QLineEdit()
      self.totalSearchTriesDisplay.setReadOnly(True)
      self.totalSearchTriesDisplay.setText(QString('0'))
      self.totalSearchTriesDisplay.setFont(GETFONT('Fixed'))
      self.totalSearchTriesDisplay.setMinimumWidth(tightSizeNChar(self.totalSearchTriesDisplay, 6)[0])
      self.totalSearchTriesDisplay.setMaximumWidth(tightSizeNChar(self.totalSearchTriesDisplay, 12)[0])
      searchButtonPanel = makeHorizFrame([self.searchButton, self.stopButton, 'stretch', totalSearchLabel,  self.totalSearchTriesDisplay])
      
      self.resultsDisplay = QTextEdit()
      self.resultsDisplay.setReadOnly(True)
      self.resultsDisplay.setFont(GETFONT('Fixed'))
      self.resultsDisplay.setMinimumHeight(100)
      self.searchPanel = makeVertFrame([topRow, self.segDefTableView, orderingButtonPanel,
             self.segOrdListBox, searchButtonPanel, self.resultsDisplay, 'stretch'])
      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(self.searchPanel)
コード例 #23
0
ファイル: single.py プロジェクト: Hainish/calibre
    def do_layout(self):
        self.central_widget.clear()
        self.tabs = []
        self.labels = []
        sto = QWidget.setTabOrder

        self.on_drag_enter.connect(self.handle_drag_enter)
        self.tabs.append(DragTrackingWidget(self, self.on_drag_enter))
        self.central_widget.addTab(self.tabs[0], _("&Metadata"))
        self.tabs[0].l = QGridLayout()
        self.tabs[0].setLayout(self.tabs[0].l)

        self.tabs.append(QWidget(self))
        self.central_widget.addTab(self.tabs[1], _("&Cover and formats"))
        self.tabs[1].l = QGridLayout()
        self.tabs[1].setLayout(self.tabs[1].l)

        # accept drop events so we can automatically switch to the second tab to
        # drop covers and formats
        self.tabs[0].setAcceptDrops(True)

        # Tab 0
        tab0 = self.tabs[0]

        tl = QGridLayout()
        gb = QGroupBox(_('&Basic metadata'), self.tabs[0])
        self.tabs[0].l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('C&ustom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(tab0)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            self.tabs[0].l.addWidget(gb, 0, 1, 1, 1)
            sto(self.identifiers, gb)

        w = QGroupBox(_('&Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        l = QHBoxLayout()
        w.setLayout(l)
        l.addWidget(self.comments)
        tab0.l.addWidget(w, 1, 0, 1, 2)

        # Tab 1
        tab1 = self.tabs[1]

        wsp = QWidget(tab1)
        wgl = QVBoxLayout()
        wsp.setLayout(wgl)

        # right-hand side of splitter
        gb = QGroupBox(_('Change cover'), tab1)
        l = QGridLayout()
        gb.setLayout(l)
        for i, b in enumerate(self.cover.buttons[:3]):
            l.addWidget(b, 0, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        l.addLayout(hl, 1, 0, 1, 3)
        wgl.addWidget(gb)
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addWidget(self.formats_manager)

        self.splitter = QSplitter(Qt.Horizontal, tab1)
        tab1.l.addWidget(self.splitter)
        self.splitter.addWidget(self.cover)
        self.splitter.addWidget(wsp)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(64, 64))
コード例 #24
0
   def __init__(self, main):

      self.main = main

      ##########################################################################
      ##### Display the conversion values based on the Coinbase API
      self.lastPriceFetch = 0
      self.lblHeader    = QRichLabel(tr("""<b>Tracking buy and sell prices on 
         Coinbase every 60 seconds</b>"""), doWrap=False)
      self.lblLastTime  = QRichLabel('', doWrap=False)
      self.lblSellLabel = QRichLabel(tr('Coinbase <b>Sell</b> Price (USD):'), doWrap=False)
      self.lblBuyLabel  = QRichLabel(tr('Coinbase <b>Buy</b> Price (USD):'),  doWrap=False)
      self.lblSellPrice = QRichLabel('<Not Available>')
      self.lblBuyPrice  = QRichLabel('<Not Available>')

      self.lastSellStr = ''
      self.lastBuyStr = ''

      self.btnUpdate = QPushButton(tr('Check Now'))
      self.main.connect(self.btnUpdate, SIGNAL('clicked()'), self.checkUpdatePrice)

      ##########################################################################
      ##### A calculator for converting prices between USD and BTC
      lblCalcTitle = QRichLabel(tr("""Convert between USD and BTC using 
         Coinbase sell price"""), hAlign=Qt.AlignHCenter, doWrap=False)
      self.edtEnterUSD = QLineEdit()
      self.edtEnterBTC = QLineEdit()
      self.lblEnterUSD1 = QRichLabel('$')
      self.lblEnterUSD2 = QRichLabel('USD')
      self.lblEnterBTC = QRichLabel('BTC')
      btnClear = QPushButton('Clear')

      self.main.connect(self.edtEnterUSD, SIGNAL('textEdited(QString)'), self.updateCalcBTC)
      self.main.connect(self.edtEnterBTC, SIGNAL('textEdited(QString)'), self.updateCalcUSD)

      def clearCalc():
         self.edtEnterUSD.setText('')
         self.edtEnterBTC.setText('')

      self.main.connect(btnClear, SIGNAL('clicked()'), clearCalc)

      frmCalcMid = makeHorizFrame( [self.lblEnterUSD1,
                                    self.edtEnterUSD,
                                    self.lblEnterUSD2,
                                    'Stretch',
                                    self.edtEnterBTC,
                                    self.lblEnterBTC])

      frmCalcClear = makeHorizFrame(['Stretch', btnClear, 'Stretch'])
      frmCalc = makeVertFrame([lblCalcTitle, frmCalcMid, frmCalcClear], STYLE_PLAIN)

      ##########################################################################
      ##### A table showing you the total balance of each wallet in USD and BTC
      lblWltTableTitle = QRichLabel(tr("Wallet balances converted to USD"), 
                                            doWrap=False, hAlign=Qt.AlignHCenter)
      numWallets = len(self.main.walletMap)
      self.wltTable = QTableWidget(self.main)
      self.wltTable.setRowCount(numWallets)
      self.wltTable.setColumnCount(4)
      self.wltTable.horizontalHeader().setStretchLastSection(True)
      self.wltTable.setMinimumWidth(600)


      ##########################################################################
      ##### Setup the main layout for the tab
      mainLayout = QGridLayout()
      i=0
      mainLayout.addWidget(self.lblHeader,      i,0,  1,3)
      i+=1
      mainLayout.addItem(QSpacerItem(15,15),    i,0)
      mainLayout.addWidget(self.lblSellLabel,   i,1)
      mainLayout.addWidget(self.lblSellPrice,   i,2)
      i+=1
      mainLayout.addItem(QSpacerItem(15,15),    i,0)
      mainLayout.addWidget(self.lblBuyLabel,    i,1)
      mainLayout.addWidget(self.lblBuyPrice,    i,2)
      i+=1
      mainLayout.addWidget(self.lblLastTime,    i,0,  1,2)
      mainLayout.addWidget(self.btnUpdate,      i,2)
      i+=1
      mainLayout.addItem(QSpacerItem(20,20),    i,0)
      i+=1
      mainLayout.addWidget(frmCalc,             i,0,  1,3)
      i+=1
      mainLayout.addItem(QSpacerItem(30,30),    i,0)
      i+=1
      mainLayout.addWidget(lblWltTableTitle,    i,0,  1,3)
      i+=1
      mainLayout.addWidget(self.wltTable,       i,0,  1,3)

      mainLayout.setColumnStretch(0,0)
      mainLayout.setColumnStretch(1,1)
      mainLayout.setColumnStretch(2,1)
      tabWidget = QWidget()
      tabWidget.setLayout(mainLayout)

      frmH = makeHorizFrame(['Stretch', tabWidget, 'Stretch'])
      frm  = makeVertFrame(['Space(20)', frmH, 'Stretch'])


      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(frm)
コード例 #25
0
ファイル: single.py プロジェクト: Hainish/calibre
class MetadataSingleDialogBase(ResizableDialog):

    view_format = pyqtSignal(object, object)
    cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields']
    one_line_comments_toolbar = False
    use_toolbutton_for_config_metadata = True

    def __init__(self, db, parent=None):
        self.db = db
        self.changed = set()
        self.books_to_refresh = set()
        self.rows_to_refresh = set()
        ResizableDialog.__init__(self, parent)

    def setupUi(self, *args):  # {{{
        self.resize(990, 670)

        self.download_shortcut = QShortcut(self)
        self.download_shortcut.setKey(QKeySequence('Ctrl+D',
            QKeySequence.PortableText))
        p = self.parent()
        if hasattr(p, 'keyboard'):
            kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download'
            sc = p.keyboard.keys_map.get(kname, None)
            if sc:
                self.download_shortcut.setKey(sc[0])

        self.button_box = bb = QDialogButtonBox(self)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
                self)
        self.next_button.setShortcut(QKeySequence('Alt+Right'))
        self.next_button.clicked.connect(self.next_clicked)
        self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'),
                self)
        self.prev_button.setShortcut(QKeySequence('Alt+Left'))

        self.button_box.addButton(self.prev_button, bb.ActionRole)
        self.button_box.addButton(self.next_button, bb.ActionRole)
        self.prev_button.clicked.connect(self.prev_clicked)
        bb.setStandardButtons(bb.Ok|bb.Cancel)
        bb.button(bb.Ok).setDefault(True)

        self.scroll_area = QScrollArea(self)
        self.scroll_area.setFrameShape(QScrollArea.NoFrame)
        self.scroll_area.setWidgetResizable(True)
        self.central_widget = QTabWidget(self)
        self.scroll_area.setWidget(self.central_widget)

        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.l.addWidget(self.scroll_area)
        ll = self.button_box_layout = QHBoxLayout()
        self.l.addLayout(ll)
        ll.addSpacing(10)
        ll.addWidget(self.button_box)

        self.setWindowIcon(QIcon(I('edit_input.png')))
        self.setWindowTitle(BASE_TITLE)

        self.create_basic_metadata_widgets()

        if len(self.db.custom_column_label_map):
            self.create_custom_metadata_widgets()

        self.do_layout()
        geom = gprefs.get('metasingle_window_geometry3', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))
    # }}}

    def create_basic_metadata_widgets(self):  # {{{
        self.basic_metadata_widgets = []

        self.languages = LanguagesEdit(self)
        self.basic_metadata_widgets.append(self.languages)

        self.title = TitleEdit(self)
        self.title.textChanged.connect(self.update_window_title)
        self.deduce_title_sort_button = QToolButton(self)
        self.deduce_title_sort_button.setToolTip(
            _('Automatically create the title sort entry based on the current '
                'title entry.\nUsing this button to create title sort will '
                'change title sort from red to green.'))
        self.deduce_title_sort_button.setWhatsThis(
                self.deduce_title_sort_button.toolTip())
        self.title_sort = TitleSortEdit(self, self.title,
                self.deduce_title_sort_button, self.languages)
        self.basic_metadata_widgets.extend([self.title, self.title_sort])

        self.deduce_author_sort_button = b = QToolButton(self)
        b.setToolTip('<p>' +
            _('Automatically create the author sort entry based on the current '
              'author entry. Using this button to create author sort will '
              'change author sort from red to green.  There is a menu of '
              'functions available under this button. Click and hold '
              'on the button to see it.') + '</p>')
        b.m = m = QMenu()
        ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author'))
        ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort'))
        ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors'))
        ac4 = m.addAction(QIcon(I('next.png')),
                _('Copy author to author sort'))
        ac5 = m.addAction(QIcon(I('previous.png')),
                _('Copy author sort to author'))

        b.setMenu(m)
        self.authors = AuthorsEdit(self, ac3)
        self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac,
                ac2, ac4, ac5)
        self.basic_metadata_widgets.extend([self.authors, self.author_sort])

        self.swap_title_author_button = QToolButton(self)
        self.swap_title_author_button.setIcon(QIcon(I('swap.png')))
        self.swap_title_author_button.setToolTip(_(
            'Swap the author and title'))
        self.swap_title_author_button.clicked.connect(self.swap_title_author)

        self.manage_authors_button = QToolButton(self)
        self.manage_authors_button.setIcon(QIcon(I('user_profile.png')))
        self.manage_authors_button.setToolTip('<p>' + _(
            'Manage authors. Use to rename authors and correct '
            'individual author\'s sort values') + '</p>')
        self.manage_authors_button.clicked.connect(self.authors.manage_authors)

        self.series = SeriesEdit(self)
        self.clear_series_button = QToolButton(self)
        self.clear_series_button.setToolTip(
               _('Clear series'))
        self.clear_series_button.clicked.connect(self.series.clear)
        self.series_index = SeriesIndexEdit(self, self.series)
        self.basic_metadata_widgets.extend([self.series, self.series_index])

        self.formats_manager = FormatsManager(self, self.copy_fmt)
        # We want formats changes to be committed before title/author, as
        # otherwise we could have data loss if the title/author changed and the
        # user was trying to add an extra file from the old books directory.
        self.basic_metadata_widgets.insert(0, self.formats_manager)
        self.formats_manager.metadata_from_format_button.clicked.connect(
                self.metadata_from_format)
        self.formats_manager.cover_from_format_button.clicked.connect(
                self.cover_from_format)
        self.cover = Cover(self)
        self.cover.download_cover.connect(self.download_cover)
        self.basic_metadata_widgets.append(self.cover)

        self.comments = CommentsEdit(self, self.one_line_comments_toolbar)
        self.basic_metadata_widgets.append(self.comments)

        self.rating = RatingEdit(self)
        self.clear_ratings_button = QToolButton(self)
        self.clear_ratings_button.setToolTip(_('Clear rating'))
        self.clear_ratings_button.setIcon(QIcon(I('trash.png')))
        self.clear_ratings_button.clicked.connect(self.rating.zero)

        self.basic_metadata_widgets.append(self.rating)

        self.tags = TagsEdit(self)
        self.tags_editor_button = QToolButton(self)
        self.tags_editor_button.setToolTip(_('Open Tag Editor'))
        self.tags_editor_button.setIcon(QIcon(I('chapters.png')))
        self.tags_editor_button.clicked.connect(self.tags_editor)
        self.clear_tags_button = QToolButton(self)
        self.clear_tags_button.setToolTip(_('Clear all tags'))
        self.clear_tags_button.setIcon(QIcon(I('trash.png')))
        self.clear_tags_button.clicked.connect(self.tags.clear)
        self.basic_metadata_widgets.append(self.tags)

        self.identifiers = IdentifiersEdit(self)
        self.basic_metadata_widgets.append(self.identifiers)
        self.clear_identifiers_button = QToolButton(self)
        self.clear_identifiers_button.setIcon(QIcon(I('trash.png')))
        self.clear_identifiers_button.setToolTip(_('Clear Ids'))
        self.clear_identifiers_button.clicked.connect(self.identifiers.clear)
        self.paste_isbn_button = QToolButton(self)
        self.paste_isbn_button.setToolTip('<p>' +
                    _('Paste the contents of the clipboard into the '
                      'identifiers box prefixed with isbn:') + '</p>')
        self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png')))
        self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn)

        self.publisher = PublisherEdit(self)
        self.basic_metadata_widgets.append(self.publisher)

        self.timestamp = DateEdit(self)
        self.pubdate = PubdateEdit(self)
        self.basic_metadata_widgets.extend([self.timestamp, self.pubdate])

        self.fetch_metadata_button = QPushButton(
                _('&Download metadata'), self)
        self.fetch_metadata_button.clicked.connect(self.fetch_metadata)
        self.download_shortcut.activated.connect(self.fetch_metadata_button.click)
        font = self.fmb_font = QFont()
        font.setBold(True)
        self.fetch_metadata_button.setFont(font)

        if self.use_toolbutton_for_config_metadata:
            self.config_metadata_button = QToolButton(self)
            self.config_metadata_button.setIcon(QIcon(I('config.png')))
        else:
            self.config_metadata_button = QPushButton(self)
            self.config_metadata_button.setText(_('Configure download metadata'))
        self.config_metadata_button.setIcon(QIcon(I('config.png')))
        self.config_metadata_button.clicked.connect(self.configure_metadata)
        self.config_metadata_button.setToolTip(
            _('Change how calibre downloads metadata'))

    # }}}

    def create_custom_metadata_widgets(self):  # {{{
        self.custom_metadata_widgets_parent = w = QWidget(self)
        layout = QGridLayout()
        w.setLayout(layout)
        self.custom_metadata_widgets, self.__cc_spacers = \
            populate_metadata_page(layout, self.db, None, parent=w, bulk=False,
                two_column=self.cc_two_column)
        self.__custom_col_layouts = [layout]
    # }}}

    def set_custom_metadata_tab_order(self, before=None, after=None):  # {{{
        sto = QWidget.setTabOrder
        if getattr(self, 'custom_metadata_widgets', []):
            ans = self.custom_metadata_widgets
            for i in range(len(ans)-1):
                if before is not None and i == 0:
                    pass
                if len(ans[i+1].widgets) == 2:
                    sto(ans[i].widgets[-1], ans[i+1].widgets[1])
                else:
                    sto(ans[i].widgets[-1], ans[i+1].widgets[0])
                for c in range(2, len(ans[i].widgets), 2):
                    sto(ans[i].widgets[c-1], ans[i].widgets[c+1])
            if after is not None:
                pass
    # }}}

    def do_view_format(self, path, fmt):
        if path:
            self.view_format.emit(None, path)
        else:
            self.view_format.emit(self.book_id, fmt)

    def copy_fmt(self, fmt, f):
        self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True)

    def do_layout(self):
        raise NotImplementedError()

    def __call__(self, id_):
        self.book_id = id_
        self.books_to_refresh = set([])
        for widget in self.basic_metadata_widgets:
            widget.initialize(self.db, id_)
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.initialize(id_)
        if callable(self.set_current_callback):
            self.set_current_callback(id_)
        # Commented out as it doesn't play nice with Next, Prev buttons
        # self.fetch_metadata_button.setFocus(Qt.OtherFocusReason)

    # Miscellaneous interaction methods {{{
    def update_window_title(self, *args):
        title = self.title.current_val
        if len(title) > 50:
            title = title[:50] + u'\u2026'
        self.setWindowTitle(BASE_TITLE + ' - ' +
                title + ' - ' +
                _(' [%(num)d of %(tot)d]')%dict(num=
                    self.current_row+1,
                tot=len(self.row_list)))

    def swap_title_author(self, *args):
        title = self.title.current_val
        self.title.current_val = authors_to_string(self.authors.current_val)
        self.authors.current_val = string_to_authors(title)
        self.title_sort.auto_generate()
        self.author_sort.auto_generate()

    def tags_editor(self, *args):
        self.tags.edit(self.db, self.book_id)

    def metadata_from_format(self, *args):
        mi, ext = self.formats_manager.get_selected_format_metadata(self.db,
                self.book_id)
        if mi is not None:
            self.update_from_mi(mi)

    def get_pdf_cover(self):
        pdfpath = self.formats_manager.get_format_path(self.db, self.book_id,
                                                       'pdf')
        from calibre.gui2.metadata.pdf_covers import PDFCovers
        d = PDFCovers(pdfpath, parent=self)
        if d.exec_() == d.Accepted:
            cpath = d.cover_path
            if cpath:
                with open(cpath, 'rb') as f:
                    self.update_cover(f.read(), 'PDF')
        d.cleanup()

    def cover_from_format(self, *args):
        ext = self.formats_manager.get_selected_format()
        if ext is None:
            return
        if ext == 'pdf':
            return self.get_pdf_cover()
        try:
            mi, ext = self.formats_manager.get_selected_format_metadata(self.db,
                    self.book_id)
        except (IOError, OSError) as err:
            if getattr(err, 'errno', None) == errno.EACCES:  # Permission denied
                import traceback
                fname = err.filename if err.filename else 'file'
                error_dialog(self, _('Permission denied'),
                        _('Could not open %s. Is it being used by another'
                        ' program?')%fname, det_msg=traceback.format_exc(),
                        show=True)
                return
            raise
        if mi is None:
            return
        cdata = None
        if mi.cover and os.access(mi.cover, os.R_OK):
            cdata = open(mi.cover).read()
        elif mi.cover_data[1] is not None:
            cdata = mi.cover_data[1]
        if cdata is None:
            error_dialog(self, _('Could not read cover'),
                         _('Could not read cover from %s format')%ext).exec_()
            return
        self.update_cover(cdata, ext)

    def update_cover(self, cdata, fmt):
        orig = self.cover.current_val
        self.cover.current_val = cdata
        if self.cover.current_val is None:
            self.cover.current_val = orig
            return error_dialog(self, _('Could not read cover'),
                         _('The cover in the %s format is invalid')%fmt,
                         show=True)
            return

    def update_from_mi(self, mi, update_sorts=True, merge_tags=True, merge_comments=False):
        if not mi.is_null('title'):
            self.title.current_val = mi.title
            if update_sorts:
                self.title_sort.auto_generate()
        if not mi.is_null('authors'):
            self.authors.current_val = mi.authors
        if not mi.is_null('author_sort'):
            self.author_sort.current_val = mi.author_sort
        elif update_sorts:
            self.author_sort.auto_generate()
        if not mi.is_null('rating'):
            try:
                self.rating.current_val = mi.rating
            except:
                pass
        if not mi.is_null('publisher'):
            self.publisher.current_val = mi.publisher
        if not mi.is_null('tags'):
            old_tags = self.tags.current_val
            tags = mi.tags if mi.tags else []
            if old_tags and merge_tags:
                ltags, lotags = {t.lower() for t in tags}, {t.lower() for t in
                        old_tags}
                tags = [t for t in tags if t.lower() in ltags-lotags] + old_tags
            self.tags.current_val = tags
        if not mi.is_null('identifiers'):
            current = self.identifiers.current_val
            current.update(mi.identifiers)
            self.identifiers.current_val = current
        if not mi.is_null('pubdate'):
            self.pubdate.current_val = mi.pubdate
        if not mi.is_null('series') and mi.series.strip():
            self.series.current_val = mi.series
            if mi.series_index is not None:
                self.series_index.reset_original()
                self.series_index.current_val = float(mi.series_index)
        if not mi.is_null('languages'):
            langs = [canonicalize_lang(x) for x in mi.languages]
            langs = [x for x in langs if x is not None]
            if langs:
                self.languages.current_val = langs
        if mi.comments and mi.comments.strip():
            val = mi.comments
            if val and merge_comments:
                cval = self.comments.current_val
                if cval:
                    val = merge_two_comments(cval, val)
            self.comments.current_val = val

    def fetch_metadata(self, *args):
        d = FullFetch(self.cover.pixmap(), self)
        ret = d.start(title=self.title.current_val, authors=self.authors.current_val,
                identifiers=self.identifiers.current_val)
        if ret == d.Accepted:
            from calibre.ebooks.metadata.sources.prefs import msprefs
            mi = d.book
            dummy = Metadata(_('Unknown'))
            for f in msprefs['ignore_fields']:
                if ':' not in f:
                    setattr(mi, f, getattr(dummy, f))
            if mi is not None:
                pd = mi.pubdate
                if pd is not None:
                    # Put the downloaded published date into the local timezone
                    # as we discard time info and the date is timezone
                    # invariant. This prevents the as_local_timezone() call in
                    # update_from_mi from changing the pubdate
                    mi.pubdate = datetime(pd.year, pd.month, pd.day,
                            tzinfo=local_tz)
                self.update_from_mi(mi, merge_comments=msprefs['append_comments'])
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    def configure_metadata(self):
        from calibre.gui2.preferences import show_config_widget
        gui = self.parent()
        show_config_widget('Sharing', 'Metadata download', parent=self,
                gui=gui, never_shutdown=True)

    def download_cover(self, *args):
        from calibre.gui2.metadata.single_download import CoverFetch
        d = CoverFetch(self.cover.pixmap(), self)
        ret = d.start(self.title.current_val, self.authors.current_val,
                self.identifiers.current_val)
        if ret == d.Accepted:
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    # }}}

    def apply_changes(self):
        self.changed.add(self.book_id)
        if self.db is None:
            # break_cycles has already been called, don't know why this should
            # happen but a user reported it
            return True
        for widget in self.basic_metadata_widgets:
            try:
                if not widget.commit(self.db, self.book_id):
                    return False
                self.books_to_refresh |= getattr(widget, 'books_to_refresh',
                        set([]))
            except (IOError, OSError) as err:
                if getattr(err, 'errno', None) == errno.EACCES:  # Permission denied
                    import traceback
                    fname = getattr(err, 'filename', None)
                    p = 'Locked file: %s\n\n'%fname if fname else ''
                    error_dialog(self, _('Permission denied'),
                            _('Could not change the on disk location of this'
                                ' book. Is it open in another program?'),
                            det_msg=p+traceback.format_exc(), show=True)
                    return False
                raise
        for widget in getattr(self, 'custom_metadata_widgets', []):
            self.books_to_refresh |= widget.commit(self.book_id)

        self.db.commit()
        rows = self.db.refresh_ids(list(self.books_to_refresh))
        if rows:
            self.rows_to_refresh |= set(rows)

        return True

    def accept(self):
        self.save_state()
        if not self.apply_changes():
            return
        ResizableDialog.accept(self)

    def reject(self):
        self.save_state()
        ResizableDialog.reject(self)

    def save_state(self):
        try:
            gprefs['metasingle_window_geometry3'] = bytearray(self.saveGeometry())
        except:
            # Weird failure, see https://bugs.launchpad.net/bugs/995271
            import traceback
            traceback.print_exc()

    # Dialog use methods {{{
    def start(self, row_list, current_row, view_slot=None,
            set_current_callback=None):
        self.row_list = row_list
        self.current_row = current_row
        if view_slot is not None:
            self.view_format.connect(view_slot)
        self.set_current_callback = set_current_callback
        self.do_one(apply_changes=False)
        ret = self.exec_()
        self.break_cycles()
        return ret

    def next_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=1, apply_changes=False)

    def prev_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=-1, apply_changes=False)

    def do_one(self, delta=0, apply_changes=True):
        if apply_changes:
            self.apply_changes()
        self.current_row += delta
        prev = next_ = None
        if self.current_row > 0:
            prev = self.db.title(self.row_list[self.current_row-1])
        if self.current_row < len(self.row_list) - 1:
            next_ = self.db.title(self.row_list[self.current_row+1])

        if next_ is not None:
            tip = (_('Save changes and edit the metadata of %s')+
                    ' [Alt+Right]')%next_
            self.next_button.setToolTip(tip)
        self.next_button.setEnabled(next_ is not None)
        if prev is not None:
            tip = (_('Save changes and edit the metadata of %s')+
                    ' [Alt+Left]')%prev
            self.prev_button.setToolTip(tip)
        self.prev_button.setEnabled(prev is not None)
        self.button_box.button(self.button_box.Ok).setDefault(True)
        self.button_box.button(self.button_box.Ok).setFocus(Qt.OtherFocusReason)
        self(self.db.id(self.row_list[self.current_row]))

    def break_cycles(self):
        # Break any reference cycles that could prevent python
        # from garbage collecting this dialog
        self.set_current_callback = self.db = None
        def disconnect(signal):
            try:
                signal.disconnect()
            except:
                pass  # Fails if view format was never connected
        disconnect(self.view_format)
        for b in ('next_button', 'prev_button'):
            x = getattr(self, b, None)
            if x is not None:
                disconnect(x.clicked)
        for widget in self.basic_metadata_widgets:
            bc = getattr(widget, 'break_cycles', None)
            if bc is not None and callable(bc):
                bc()
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.break_cycles()
コード例 #26
0
    def __init__(self, gui, initial_plugin=None, close_after_initial=False):
        QMainWindow.__init__(self, gui)
        self.gui = gui
        self.must_restart = False
        self.committed = False
        self.close_after_initial = close_after_initial

        self.resize(930, 720)
        nh, nw = min_available_height()-25, available_width()-10
        if nh < 0:
            nh = 800
        if nw < 0:
            nw = 600
        nh = min(self.height(), nh)
        nw = min(self.width(), nw)
        self.resize(nw, nh)
        self.esc_action = QAction(self)
        self.addAction(self.esc_action)
        self.esc_action.setShortcut(QKeySequence(Qt.Key_Escape))
        self.esc_action.triggered.connect(self.esc)

        geom = gprefs.get('preferences_window_geometry', None)
        if geom is not None:
            self.restoreGeometry(geom)

        # Center
        if islinux:
            self.move(gui.rect().center() - self.rect().center())

        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.setWindowIcon(QIcon(I('config.png')))

        self.status_bar = StatusBar(self)
        self.setStatusBar(self.status_bar)

        self.stack = QStackedWidget(self)
        self.cw = QWidget(self)
        self.cw.setLayout(QVBoxLayout())
        self.cw.layout().addWidget(self.stack)
        self.bb = QDialogButtonBox(QDialogButtonBox.Close)
        self.wizard_button = self.bb.addButton(_('Run welcome wizard'),
                self.bb.ActionRole)
        self.wizard_button.setIcon(QIcon(I('wizard.png')))
        self.wizard_button.clicked.connect(self.run_wizard,
                type=Qt.QueuedConnection)
        self.cw.layout().addWidget(self.bb)
        self.bb.button(self.bb.Close).setDefault(True)
        self.bb.rejected.connect(self.close, type=Qt.QueuedConnection)
        self.setCentralWidget(self.cw)
        self.browser = Browser(self)
        self.browser.show_plugin.connect(self.show_plugin)
        self.stack.addWidget(self.browser)
        self.scroll_area = QScrollArea(self)
        self.stack.addWidget(self.scroll_area)
        self.scroll_area.setWidgetResizable(True)

        self.bar = QToolBar(self)
        self.addToolBar(self.bar)
        self.bar.setVisible(False)
        self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        self.bar.setMovable(False)
        self.bar.setFloatable(False)
        self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.apply_action = self.bar.addAction(QIcon(I('ok.png')), _('&Apply'),
                self.commit)
        self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
                _('&Cancel'),                self.cancel)
        self.bar_title = BarTitle(self.bar)
        self.bar.addWidget(self.bar_title)
        self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
                _('Restore &defaults'), self.restore_defaults)
        for ac, tt in [('apply', _('Save changes')),
                ('cancel', _('Cancel and return to overview'))]:
            ac = getattr(self, ac+'_action')
            ac.setToolTip(tt)
            ac.setWhatsThis(tt)
            ac.setStatusTip(tt)

        for ch in self.bar.children():
            if isinstance(ch, QToolButton):
                ch.setCursor(Qt.PointingHandCursor)
                ch.setAutoRaise(True)

        self.stack.setCurrentIndex(0)

        if initial_plugin is not None:
            category, name = initial_plugin
            plugin = get_plugin(category, name)
            if plugin is not None:
                self.show_plugin(plugin)
コード例 #27
0
    def __init__(self, assy, parent):
        """
        Constructor for the part window.

        @param assy: The assembly (part)
        @type  assy: Assembly

        @param parent: The parent widget.
        @type  parent: U{B{QMainWindow}
                       <http://doc.trolltech.com/4/qmainwindow.html>}
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.assy = assy
            # note: to support MDI, self.assy would probably need to be a
            # different assembly for each PartWindow.
            # [bruce 080216 comment]
        self.setWindowIcon(geticon("ui/border/Part.png"))
        self.updateWindowTitle()
        
        # The main layout for the part window is a VBoxLayout <pwVBoxLayout>.
        self.pwVBoxLayout = QVBoxLayout(self)
        pwVBoxLayout = self.pwVBoxLayout
        pwVBoxLayout.setMargin(0)
        pwVBoxLayout.setSpacing(0)

        # ################################################################
        # <pwSplitter> is the horizontal splitter b/w the
        # pwLeftArea (mt and pm) and the glpane.
        self.pwSplitter = QSplitter(Qt.Horizontal)
        pwSplitter = self.pwSplitter
        pwSplitter.setObjectName("pwSplitter")
        pwSplitter.setHandleWidth(3) # 3 pixels wide.
        pwVBoxLayout.addWidget(pwSplitter)

        # ##################################################################
        # <pwLeftArea> is the container holding the pwProjectTabWidget.
        # Note: Making pwLeftArea (and pwRightArea and pwBottomArea) QFrame
        # widgets has the benefit of making it easy to draw a border around
        # each area. One purpose of this would be to help developers understand
        # (visually) how the part window is laid out. I intend to add a debug
        # pref to draw part window area borders and add "What's This" text to
        # them. Mark 2008-01-05.
        self.pwLeftArea = LeftFrame(self)
        pwLeftArea = self.pwLeftArea
        pwLeftArea.setObjectName("pwLeftArea")
        pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)

        # Setting the frame style like this is nice since it clearly
        # defines the splitter at the top-left corner.
        pwLeftArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        # This layout will contain splitter (above) and the pwBottomArea.
        leftChannelVBoxLayout = QVBoxLayout(pwLeftArea)
        leftChannelVBoxLayout.setMargin(0)
        leftChannelVBoxLayout.setSpacing(0)

        pwSplitter.addWidget(pwLeftArea)

        # Makes it so pwLeftArea is not collapsible.
        pwSplitter.setCollapsible (0, False)

        # ##################################################################
        # <pwProjectTabWidget> is a QTabWidget that contains the MT and PM
        # widgets. It lives in the "left area" of the part window.
        self.pwProjectTabWidget = _pwProjectTabWidget()
           # _pwProjectTabWidget subclasses QTabWidget
           # Note [bruce 070829]: to fix bug 2522 I need to intercept
           # self.pwProjectTabWidget.removeTab, so I made it a subclass of
           # QTabWidget. It needs to know the GLPane, but that's not created
           # yet, so we set it later using KLUGE_setGLPane (below).
           # Note: No parent supplied. Could this be the source of the
           # minor vsplitter resizing problem I was trying to resolve a few
           # months ago?  Try supplying a parent later. Mark 2008-01-01
        self.pwProjectTabWidget.setObjectName("pwProjectTabWidget")
        self.pwProjectTabWidget.setCurrentIndex(0)
        self.pwProjectTabWidget.setAutoFillBackground(True)

        # Create the model tree "tab" widget. It will contain the MT GUI widget.
        # Set the tab icon, too.
        self.modelTreeTab = QWidget()
        self.modelTreeTab.setObjectName("modelTreeTab")
        self.pwProjectTabWidget.addTab(
            self.modelTreeTab,
            geticon("ui/modeltree/Model_Tree.png"),
            "")

        modelTreeTabLayout = QVBoxLayout(self.modelTreeTab)
        modelTreeTabLayout.setMargin(0)
        modelTreeTabLayout.setSpacing(0)

        # Create the model tree (GUI) and add it to the tab layout.
        self.modelTree = ModelTree(self.modelTreeTab, parent)
        self.modelTree.modelTreeGui.setObjectName("modelTreeGui")
        modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui)

        # Create the property manager "tab" widget. It will contain the PropMgr
        # scroll area, which will contain the property manager and all its
        # widgets.
        self.propertyManagerTab = QWidget()
        self.propertyManagerTab.setObjectName("propertyManagerTab")

        self.propertyManagerScrollArea = QScrollArea(self.pwProjectTabWidget)
        self.propertyManagerScrollArea.setObjectName("propertyManagerScrollArea")
        self.propertyManagerScrollArea.setWidget(self.propertyManagerTab)
        self.propertyManagerScrollArea.setWidgetResizable(True)
        # Eureka!
        # setWidgetResizable(True) will resize the Property Manager (and its
        # contents) correctly when the scrollbar appears/disappears.
        # It even accounts correctly for collapsed/expanded groupboxes!
        # Mark 2007-05-29

        # Add the property manager scroll area as a "tabbed" widget.
        # Set the tab icon, too.
        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"),
            "")

        # Finally, add the "pwProjectTabWidget" to the left channel layout.
        
        leftChannelVBoxLayout.addWidget(self.pwProjectTabWidget)
       

        # Create the glpane and make it a child of the part splitter.
        self.glpane = GLPane(assy, self, 'glpane name', parent)
            # note: our owner (MWsemantics) assumes
            # there is just this one GLPane for assy, and stores it
            # into assy as assy.o and assy.glpane. [bruce 080216 comment]
        
        # Add what's this text to self.glpane.
        # [bruce 080912 moved this here from part of a method in class GLPane.
        #  In this code's old location, Mark wrote [2007-06-01]: "Problem -
        #  I don't believe this text is processed by fix_whatsthis_text_and_links()
        #  in whatsthis_utilities.py." Now that this code is here, I don't know
        #  whether that's still true. ]
        from ne1_ui.WhatsThisText_for_MainWindow import whats_this_text_for_glpane
        self.glpane.setWhatsThis( whats_this_text_for_glpane() )
        
        # update [re the above comment], bruce 081209:
        # I added the following explicit call of fix_whatsthis_text_and_links,
        # but it doesn't work to replace Ctrl with Cmd on Mac;
        # see today's comment in fix_whatsthis_text_and_links for likely reason.
        # So I will leave this here, but also leave in place the kluges
        # in whats_this_text_for_glpane to do that replacement itself.
        # The wiki help link in this whatsthis text doesn't work,
        # but I guess that is an independent issue, related to lack
        # of use of class QToolBar_WikiHelp or similar code, for GLPane
        # or this class or the main window class.
        from foundation.whatsthis_utilities import fix_whatsthis_text_and_links
        fix_whatsthis_text_and_links(self.glpane) # doesn't yet work
        
        self.pwProjectTabWidget.KLUGE_setGLPane(self.glpane)
            # help fix bug 2522 [bruce 070829]
        qt4warnDestruction(self.glpane, 'GLPane of PartWindow')
        pwSplitter.addWidget(self.glpane)

        # ##################################################################
        # <pwBottomArea> is a container at the bottom of the part window
        # spanning its entire width. It is intended to be used as an extra
        # area for use by Property Managers (or anything else) that needs
        # a landscape oriented layout.
        # An example is the Sequence Editor, which is part of the
        # Strand Properties PM.
        self.pwBottomArea = QFrame()
            # IMHO, self is not a good parent. Mark 2008-01-04.
        pwBottomArea = self.pwBottomArea
        pwBottomArea.setObjectName("pwBottomArea")
        pwBottomArea.setMaximumHeight(50)

        # Add a frame border to see what it looks like.
        pwBottomArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        self.pwVBoxLayout.addWidget(pwBottomArea)

        # Hide the bottom frame for now. Later this might be used for the
        # sequence editor.
        pwBottomArea.hide()
        
        #This widget implementation is subject to heavy revision. The purpose
        #is to implement a NFR that Mark urgently needs : The NFR is: Need a 
        #way to quickly find a node in the MT by entering its name.
        #-- Ninad 2008-11-06
        self.pwSpecialDockWidgetInLeftChannel = SelectNodeByNameDockWidget(self.glpane.win)
        leftChannelVBoxLayout.addWidget(self.pwSpecialDockWidgetInLeftChannel)
                
        # See the resizeEvent() docstring for more information about
        # resizeTimer.
        self.resizeTimer = QTimer(self)
        self.resizeTimer.setSingleShot(True)
        return
コード例 #28
0
    def __init__(self, fm, pref_name, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        if pref_name == 'column_color_rules':
            self.rule_kind = 'color'
            rule_text = _('coloring')
        else:
            self.rule_kind = 'icon'
            rule_text = _('icon')

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(
            _('Create/edit a column {0} rule').format(rule_text))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(
            _('Create a column {0} rule by'
              ' filling in the boxes below'.format(rule_text)))
        l.addWidget(l1, 0, 0, 1, 8)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 8)

        self.l2 = l2 = QLabel(_('Set the'))
        l.addWidget(l2, 2, 0)

        if self.rule_kind == 'color':
            l.addWidget(QLabel(_('color')))
        else:
            self.kind_box = QComboBox(self)
            for tt, t in icon_rule_kinds:
                self.kind_box.addItem(tt, t)
            l.addWidget(self.kind_box, 2, 1)

        self.l3 = l3 = QLabel(_('of the column:'))
        l.addWidget(l3, 2, 2)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 3)

        self.l4 = l4 = QLabel(_('to'))
        l.addWidget(l4, 2, 4)

        if self.rule_kind == 'color':
            self.color_box = QComboBox(self)
            self.color_label = QLabel('Sample text Sample text')
            self.color_label.setTextFormat(Qt.RichText)
            l.addWidget(self.color_box, 2, 5)
            l.addWidget(self.color_label, 2, 6)
            l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
        else:
            self.filename_box = QComboBox()
            self.filename_box.setInsertPolicy(
                self.filename_box.InsertAlphabetically)
            d = os.path.join(config_dir, 'cc_icons')
            self.icon_file_names = []
            if os.path.exists(d):
                for icon_file in os.listdir(d):
                    icon_file = lower(icon_file)
                    if os.path.exists(os.path.join(d, icon_file)):
                        if icon_file.endswith('.png'):
                            self.icon_file_names.append(icon_file)
            self.icon_file_names.sort(key=sort_key)
            self.update_filename_box()

            l.addWidget(self.filename_box, 2, 5)
            self.filename_button = QPushButton(QIcon(I('document_open.png')),
                                               _('&Add icon'))
            l.addWidget(self.filename_button, 2, 6)
            l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7)
            l.setColumnStretch(7, 10)

        self.l5 = l5 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l5, 3, 0, 1, 7)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 8)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                                          _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 8)
        b.clicked.connect(self.add_blank_condition)

        self.l6 = l6 = QLabel(
            _('You can disable a condition by'
              ' blanking all of its boxes'))
        l.addWidget(l6, 6, 0, 1, 8)

        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 8)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        if self.rule_kind == 'color':
            for b in (self.column_box, self.color_box):
                b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
                b.setMinimumContentsLength(15)

        for key in sorted(displayable_columns(fm),
                          key=lambda (k): sort_key(fm[k]['name'])
                          if k != color_row_key else 0):
            if key == color_row_key and self.rule_kind != 'color':
                continue
            name = all_columns_string if key == color_row_key else fm[key][
                'name']
            if name:
                self.column_box.addItem(name, key)
        self.column_box.setCurrentIndex(0)

        if self.rule_kind == 'color':
            self.color_box.addItems(QColor.colorNames())
            self.color_box.setCurrentIndex(0)
            self.update_color_label()
            self.color_box.currentIndexChanged.connect(self.update_color_label)
        else:
            self.filename_button.clicked.connect(self.filename_button_clicked)

        self.resize(self.sizeHint())
コード例 #29
0
class Ui_PartWindow(QWidget):
    """
    The Ui_PartWindow class provides a Part Window UI object composed of three
    primary areas:

    - The "left area" contains the Project TabWidget which contains
    the Model Tree and Property Manager (tabs). Other tabs (widgets)
    can be introduced when needed.

    - The "right area" contains the 3D Graphics Area (i.e. glpane) displaying
    the current part.

    - The "bottom area" lives below the left and right areas, spanning
    the full width of the part window. It can be used whenever a landscape
    layout is needed (i.e. the Sequence Editor). Typically, this area is
    not used.

    A "part window" splitter lives between the left and right areas that
    allow the user to resize the shared area occupied by them. There is no
    splitter between the top and bottom areas.

    This class supports and is limited to a B{Single Document Interface (SDI)}.
    In time, NE1 will migrate to and support a Multiple Document Interface (MDI)
    that will allow multiple project documents (i.e. parts, assemblies,
    simulations, text files, graphs, tables, etc. documents) to be available
    within the common workspace of the NE1 main window.

    @see: U{B{NE1 Main Window Framework}
    <http://www.nanoengineer-1.net/mediawiki/index.php?title=NE1_Main_Window_Framework>}
    """
    widgets = [] # For debugging purposes.

    def __init__(self, assy, parent):
        """
        Constructor for the part window.

        @param assy: The assembly (part)
        @type  assy: Assembly

        @param parent: The parent widget.
        @type  parent: U{B{QMainWindow}
                       <http://doc.trolltech.com/4/qmainwindow.html>}
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.assy = assy
            # note: to support MDI, self.assy would probably need to be a
            # different assembly for each PartWindow.
            # [bruce 080216 comment]
        self.setWindowIcon(geticon("ui/border/Part.png"))
        self.updateWindowTitle()

        # Used in expanding or collapsing the Model Tree/ PM area
        self._previous_pwLeftAreaWidth = PM_DEFAULT_WIDTH

        # The main layout for the part window is a VBoxLayout <pwVBoxLayout>.
        self.pwVBoxLayout = QVBoxLayout(self)
        pwVBoxLayout = self.pwVBoxLayout
        pwVBoxLayout.setMargin(0)
        pwVBoxLayout.setSpacing(0)

        # ################################################################
        # <pwSplitter> is the horizontal splitter b/w the
        # pwProjectTabWidget and the glpane.
        self.pwSplitter = QSplitter(Qt.Horizontal)
        pwSplitter = self.pwSplitter
        pwSplitter.setObjectName("pwSplitter")
        pwSplitter.setHandleWidth(3) # 3 pixels wide.
        pwVBoxLayout.addWidget(pwSplitter)

        # ##################################################################
        # <pwLeftArea> is the container holding the pwProjectTabWidget.
        # Note: Making pwLeftArea (and pwRightArea and pwBottomArea) QFrame
        # widgets has the benefit of making it easy to draw a border around
        # each area. One purpose of this would be to help developers understand
        # (visually) how the part window is laid out. I intend to add a debug
        # pref to draw part window area borders and add "What's This" text to
        # them. Mark 2008-01-05.
        self.pwLeftArea = QFrame()
        pwLeftArea = self.pwLeftArea
        pwLeftArea.setObjectName("pwLeftArea")
        pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)
        pwLeftArea.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy(QSizePolicy.Fixed),
                        QSizePolicy.Policy(QSizePolicy.Expanding)))

        # Setting the frame style like this is nice since it clearly
        # defines the splitter at the top-left corner.
        pwLeftArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        # This layout will contain splitter (above) and the pwBottomArea.
        leftChannelVBoxLayout = QVBoxLayout(pwLeftArea)
        leftChannelVBoxLayout.setMargin(0)
        leftChannelVBoxLayout.setSpacing(0)

        pwSplitter.addWidget(pwLeftArea)

        # Makes it so pwLeftArea is not collapsible.
        pwSplitter.setCollapsible (0, False)

        # ##################################################################
        # <pwProjectTabWidget> is a QTabWidget that contains the MT and PM
        # widgets. It lives in the "left area" of the part window.
        self.pwProjectTabWidget = _pwProjectTabWidget()
           # _pwProjectTabWidget subclasses QTabWidget
           # Note [bruce 070829]: to fix bug 2522 I need to intercept
           # self.pwProjectTabWidget.removeTab, so I made it a subclass of
           # QTabWidget. It needs to know the GLPane, but that's not created
           # yet, so we set it later using KLUGE_setGLPane (below).
           # Note: No parent supplied. Could this be the source of the
           # minor vsplitter resizing problem I was trying to resolve a few
           # months ago?  Try supplying a parent later. Mark 2008-01-01
        self.pwProjectTabWidget.setObjectName("pwProjectTabWidget")
        self.pwProjectTabWidget.setCurrentIndex(0)
        self.pwProjectTabWidget.setAutoFillBackground(True)

        # Create the model tree "tab" widget. It will contain the MT GUI widget.
        # Set the tab icon, too.
        self.modelTreeTab = QWidget()
        self.modelTreeTab.setObjectName("modelTreeTab")
        self.pwProjectTabWidget.addTab(
            self.modelTreeTab,
            geticon("ui/modeltree/Model_Tree.png"),
            "")

        modelTreeTabLayout = QVBoxLayout(self.modelTreeTab)
        modelTreeTabLayout.setMargin(0)
        modelTreeTabLayout.setSpacing(0)

        # Create the model tree (GUI) and add it to the tab layout.
        self.modelTree = modelTree(self.modelTreeTab, parent)
        self.modelTree.modelTreeGui.setObjectName("modelTreeGui")
        modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui)

        # Create the property manager "tab" widget. It will contain the PropMgr
        # scroll area, which will contain the property manager and all its
        # widgets.
        self.propertyManagerTab = QWidget()
        self.propertyManagerTab.setObjectName("propertyManagerTab")

        self.propertyManagerScrollArea = QScrollArea(self.pwProjectTabWidget)
        self.propertyManagerScrollArea.setObjectName("propertyManagerScrollArea")
        self.propertyManagerScrollArea.setWidget(self.propertyManagerTab)
        self.propertyManagerScrollArea.setWidgetResizable(True)
        # Eureka!
        # setWidgetResizable(True) will resize the Property Manager (and its
        # contents) correctly when the scrollbar appears/disappears.
        # It even accounts correctly for collapsed/expanded groupboxes!
        # Mark 2007-05-29

        # Add the property manager scroll area as a "tabbed" widget.
        # Set the tab icon, too.
        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"),
            "")

        # Finally, add the "pwProjectTabWidget" to the left channel layout.
        leftChannelVBoxLayout.addWidget(self.pwProjectTabWidget)

        # Create the glpane and make it a child of the part splitter.
        self.glpane = GLPane(assy, self, 'glpane name', parent)
            # note: our owner (MWsemantics) assumes
            # there is just this one GLPane for assy, and stores it
            # into assy as assy.o and assy.glpane. [bruce 080216 comment]
        self.pwProjectTabWidget.KLUGE_setGLPane(self.glpane)
            # help fix bug 2522 [bruce 070829]
        qt4warnDestruction(self.glpane, 'GLPane of PartWindow')
        pwSplitter.addWidget(self.glpane)

        # ##################################################################
        # <pwBottomArea> is a container at the bottom of the part window
        # spanning its entire width. It is intended to be used as an extra
        # area for use by Property Managers (or anything else) that needs
        # a landscape oriented layout.
        # An example is the Sequence Editor, which is part of the
        # Strand Properties PM.
        self.pwBottomArea = QFrame()
            # IMHO, self is not a good parent. Mark 2008-01-04.
        pwBottomArea = self.pwBottomArea
        pwBottomArea.setObjectName("pwBottomArea")
        pwBottomArea.setMaximumHeight(50)
        pwBottomArea.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy(QSizePolicy.Expanding),
                        QSizePolicy.Policy(QSizePolicy.Fixed)))

        # Add a frame border to see what it looks like.
        pwBottomArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        self.pwVBoxLayout.addWidget(pwBottomArea)

        # Hide the bottom frame for now. Later this might be used for the
        # sequence editor.
        pwBottomArea.hide()

    def updateWindowTitle(self, changed = False):
        #by mark; bruce 050810 revised this in several ways, fixed bug 785
        """
        Update the window title (caption) at the top of the part window.
        Example:  "partname.mmp"

        This implements the standard way most applications indicate that a
        document has unsaved changes. On Mac OS X the close button will have
        a modified look; on other platforms the window title will have
        an '*' (asterisk).

        @note: We'll want to experiment with this to make sure it

        @param changed: If True, the document has unsaved changes.
        @type  changed: boolean

        @see: U{B{windowTitle}<http://doc.trolltech.com/4/qwidget.html#windowTitle-prop>},
              U{B{windowModified}<http://doc.trolltech.com/4/qwidget.html#windowModified-prop>}
        """
        caption_fullpath = env.prefs[captionFullPath_prefs_key]

        try:
            # self.assy.filename is always an empty string, even after a
            # file has been opened with a complete name. Need to ask Bruce
            # about this problem, resulting in a bug (i.e. the window title
            # is always "Untitled". Mark 2008-01-02.
            junk, basename = os.path.split(self.assy.filename)
            assert basename
                # it's normal for this to fail, when there is no file yet

            if caption_fullpath:
                partname = os.path.normpath(self.assy.filename)
                    #fixed bug 453-1 ninad060721
            else:
                partname = basename

        except:
            partname = 'Untitled'

        # If you're wondering about the "[*]" placeholder below, see:
        # http://doc.trolltech.com/4/qwidget.html#windowModified-prop
        self.setWindowTitle(self.trUtf8(partname + '[*]'))
        self.setWindowModified(changed)
        return

    def collapseLeftArea(self):
        """
        Collapse the left area.
        """
        self._previous_pwLeftAreaWidth = self.pwLeftArea.width()
        self.pwLeftArea.setFixedWidth(0)
        self.pwSplitter.setMaximumWidth(self.pwLeftArea.width())

    def expandLeftArea(self):
        """
        Expand the left area.

        @see: L{MWsemantics._showFullScreenCommonCode()} for an example
        showing how it is used.
        """
        if self._previous_pwLeftAreaWidth == 0:
            self._previous_pwLeftAreaWidth = PM_DEFAULT_WIDTH
        self.pwLeftArea.setMaximumWidth(
            self._previous_pwLeftAreaWidth)
        self.pwSplitter.setMaximumWidth(self.pwLeftArea.width())

    def updatePropertyManagerTab(self, tab): #Ninad 061207
        "Update the Properties Manager tab with 'tab' "

        self.parent.glpane.gl_update_confcorner()
            #bruce 070627, since PM affects confcorner appearance

        if self.propertyManagerScrollArea.widget():
            # The following is necessary to get rid of those C object
            # deleted errors (and the resulting bugs)
            lastwidgetobject = self.propertyManagerScrollArea.takeWidget()
            if lastwidgetobject:
                # bruce 071018 revised this code; see my comment on same
                # code in PM_Dialog
                try:
                    lastwidgetobject.update_props_if_needed_before_closing
                except AttributeError:
                    if 1 or debug_flags.atom_debug:
                        msg1 = "Last PropMgr %r doesn't have method" % lastwidgetobject
                        msg2 =" update_props_if_needed_before_closing. That's"
                        msg3 = " OK (for now, only implemented for Plane PM). "
                        msg4 = "Ignoring Exception: "
                        print_compact_traceback(msg1 + msg2 + msg3 + msg4)
                else:
                    lastwidgetobject.update_props_if_needed_before_closing()

            lastwidgetobject.hide()
            # @ ninad 061212 perhaps hiding the widget is not needed

        self.pwProjectTabWidget.removeTab(
            self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea))

        # Set the PropertyManager tab scroll area to the appropriate widget.
        self.propertyManagerScrollArea.setWidget(tab)

        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"),
            "")

        self.pwProjectTabWidget.setCurrentIndex(
            self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea))

    def KLUGE_current_PropertyManager(self):
        #bruce 070627; revised 070829 as part of fixing bug 2523
        """
        Return the current Property Manager widget (whether or not its tab is
        chosen, but only if it has a tab), or None if there is not one.

        @warning: This method's existence (not only its implementation)
        is a kluge, since the right way to access that would be by asking
        the "command sequencer";
        but that's not yet implemented, so this is the best we can do for now.
        Also, it would be better to get the top command and talk to it, not
        its PM (a QWidget). Also, whatever calls this will be making
        assumptions about that PM which are really only the command's business.
        So in short, every call of this is in need of cleanup once we have a
        working "command sequencer". (That's true of many things related to
        PMs, not only this method.)

        @warning: The return values are (presumably) widgets, but they can
        also be mode objects and generator objects, due to excessive use of
        multiple inheritance in the current PM code. So be careful what you
        do with them -- they might have lots of extra methods/attrs,
        and setting your own attrs in them might mess things up.
        """
        res = self.propertyManagerScrollArea.widget()
        if not hasattr(res, 'done_btn'):
            # not sure what widget this is otherwise, but it is a widget
            # (not None) for the default mode, at least on startup, so just
            # return None in this case
            return None
        # Sometimes this PM remains present from a prior command, even when
        # there is no longer a tab for the PM. As part of fixing bug 2523
        # we have to avoid returning it in that case. How we do that is a kluge,
        # but hopefully this entire kluge function can be dispensed with soon.
        # This change also fixes bug 2522 on the Mac (but not on Windows --
        # for that, we needed to intercept removeTab in separate code above).
        index = self.pwProjectTabWidget.indexOf(
            self.propertyManagerScrollArea)
        if index == -1:
            return None
        # Due to bugs in other code, sometimes the PM tab is left in place,
        # though the PM itself is hidden. To avoid finding the PM in that case,
        # also check whether it's hidden. This will fix the CC part of a new bug
        # just reported by Keith in email (when hitting Ok in DNA Gen).
        if res.isHidden(): # probably a QWidget method [bruce 080205 comment]
            return None
        return res

    def dismiss(self):
        self.parent.removePartWindow(self)
    
    def setSplitterPosition(self, leftAreaWidth = PM_DEFAULT_WIDTH): 
        """
        Set the position of the splitter between the left area and graphics area
        so that the starting width of the model tree/property manager is 
        I{leftAreaWdith} pixels wide.
        """
        # This code fixes bug 2424. Mark 2007-06-27.
        #
        # Bug 2424 was difficult to fix for many reasons:
        #
        # - QSplitter.sizes() does not return valid values until the main window
        #   is displayed. Specifically, the value of the second index (the glpane
        #   width) is always zero until the main window is displayed. I suspect 
        #   that the initial size of the glpane (in its constructor) is not set
        #   (or set to 0) and may be contributing to the confusion. This is only
        #   a theory.
        #
        # - QSplitter.setSizes() only works if width1 (the PropMgr width) and
        #   width2 (the glpane width) equal a "magic combined width". See 
        #   more about this in the Method description below.
        #
        # - Qt's QSplitter.moveSplitter() function doesn't work.
        #   
        # Method for bug fix:
        #
        # Get the widths of the left area and the glpane using QSplitter.sizes().
        # These (2) widths add up and equal a total width. You can only
        # feed QSplitter.setSizes() two values that add up to the total width
        # or it will do nothing.
    
        pw = self
        w1, w2 = pw.pwSplitter.sizes()
        total_combined_width = w1 + w2
        new_glpane_width = total_combined_width - leftAreaWidth
        pw.pwSplitter.setSizes([leftAreaWidth, new_glpane_width])
        return
コード例 #30
0
class Ui_PartWindow(QWidget):
    """
    The Ui_PartWindow class provides a Part Window UI object composed of three
    primary areas:

    - The "left area" contains the Project TabWidget which contains
    the Model Tree and Property Manager (tabs). Other tabs (widgets)
    can be added as needed.

    - The "right area" contains the Graphics Area (i.e. glpane) displaying
    the current model.

    - The "bottom area" lives below the left and right areas, spanning
    the full width of the part window. It can be used whenever a landscape
    layout is needed (i.e. the Sequence Editor). Typically, this area is
    not used and is hidden by default.

    A "part window" splitter lives between the left and right areas that
    allow the user to resize the shared area occupied by them. There is no
    splitter between the top and bottom areas.

    This class supports and is limited to a B{Single Document Interface (SDI)}.
    In time, NE1 will migrate to and support a Multiple Document Interface (MDI)
    to allow multiple project documents (i.e. parts, assemblies,
    simulations, text files, graphs, tables, etc. documents) to be available
    within the common workspace of the NE1 main window.

    @see: U{B{NE1 Main Window Framework}
    <http://www.nanoengineer-1.net/mediawiki/index.php?title=NE1_Main_Window_Framework>}
    """
    widgets = []  # For debugging purposes.
    splitterPosition = PM_DEFAULT_WIDTH
    _previous_splitterPosition = PM_DEFAULT_WIDTH

    # Used for restoring the splitter position when collapsing/expanding
    # the left area.

    def __init__(self, assy, parent):
        """
        Constructor for the part window.

        @param assy: The assembly (part)
        @type  assy: Assembly

        @param parent: The parent widget.
        @type  parent: U{B{QMainWindow}
                       <http://doc.trolltech.com/4/qmainwindow.html>}
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.assy = assy
        # note: to support MDI, self.assy would probably need to be a
        # different assembly for each PartWindow.
        # [bruce 080216 comment]
        self.setWindowIcon(geticon("ui/border/Part.png"))
        self.updateWindowTitle()

        # The main layout for the part window is a VBoxLayout <pwVBoxLayout>.
        self.pwVBoxLayout = QVBoxLayout(self)
        pwVBoxLayout = self.pwVBoxLayout
        pwVBoxLayout.setMargin(0)
        pwVBoxLayout.setSpacing(0)

        # ################################################################
        # <pwSplitter> is the horizontal splitter b/w the
        # pwLeftArea (mt and pm) and the glpane.
        self.pwSplitter = QSplitter(Qt.Horizontal)
        pwSplitter = self.pwSplitter
        pwSplitter.setObjectName("pwSplitter")
        pwSplitter.setHandleWidth(3)  # 3 pixels wide.
        pwVBoxLayout.addWidget(pwSplitter)

        # ##################################################################
        # <pwLeftArea> is the container holding the pwProjectTabWidget.
        # Note: Making pwLeftArea (and pwRightArea and pwBottomArea) QFrame
        # widgets has the benefit of making it easy to draw a border around
        # each area. One purpose of this would be to help developers understand
        # (visually) how the part window is laid out. I intend to add a debug
        # pref to draw part window area borders and add "What's This" text to
        # them. Mark 2008-01-05.
        self.pwLeftArea = LeftFrame(self)
        pwLeftArea = self.pwLeftArea
        pwLeftArea.setObjectName("pwLeftArea")
        pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)

        # Setting the frame style like this is nice since it clearly
        # defines the splitter at the top-left corner.
        pwLeftArea.setFrameStyle(QFrame.Panel | QFrame.Sunken)

        # This layout will contain splitter (above) and the pwBottomArea.
        leftChannelVBoxLayout = QVBoxLayout(pwLeftArea)
        leftChannelVBoxLayout.setMargin(0)
        leftChannelVBoxLayout.setSpacing(0)

        pwSplitter.addWidget(pwLeftArea)

        # Makes it so pwLeftArea is not collapsible.
        pwSplitter.setCollapsible(0, False)

        # ##################################################################
        # <pwProjectTabWidget> is a QTabWidget that contains the MT and PM
        # widgets. It lives in the "left area" of the part window.
        self.pwProjectTabWidget = _pwProjectTabWidget()
        # _pwProjectTabWidget subclasses QTabWidget
        # Note [bruce 070829]: to fix bug 2522 I need to intercept
        # self.pwProjectTabWidget.removeTab, so I made it a subclass of
        # QTabWidget. It needs to know the GLPane, but that's not created
        # yet, so we set it later using KLUGE_setGLPane (below).
        # Note: No parent supplied. Could this be the source of the
        # minor vsplitter resizing problem I was trying to resolve a few
        # months ago?  Try supplying a parent later. Mark 2008-01-01
        self.pwProjectTabWidget.setObjectName("pwProjectTabWidget")
        self.pwProjectTabWidget.setCurrentIndex(0)
        self.pwProjectTabWidget.setAutoFillBackground(True)

        # Create the model tree "tab" widget. It will contain the MT GUI widget.
        # Set the tab icon, too.
        self.modelTreeTab = QWidget()
        self.modelTreeTab.setObjectName("modelTreeTab")
        self.pwProjectTabWidget.addTab(self.modelTreeTab,
                                       geticon("ui/modeltree/Model_Tree.png"),
                                       "")

        modelTreeTabLayout = QVBoxLayout(self.modelTreeTab)
        modelTreeTabLayout.setMargin(0)
        modelTreeTabLayout.setSpacing(0)

        # Create the model tree (GUI) and add it to the tab layout.
        self.modelTree = ModelTree(self.modelTreeTab, parent)
        self.modelTree.modelTreeGui.setObjectName("modelTreeGui")
        modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui)

        # Create the property manager "tab" widget. It will contain the PropMgr
        # scroll area, which will contain the property manager and all its
        # widgets.
        self.propertyManagerTab = QWidget()
        self.propertyManagerTab.setObjectName("propertyManagerTab")

        self.propertyManagerScrollArea = QScrollArea(self.pwProjectTabWidget)
        self.propertyManagerScrollArea.setObjectName(
            "propertyManagerScrollArea")
        self.propertyManagerScrollArea.setWidget(self.propertyManagerTab)
        self.propertyManagerScrollArea.setWidgetResizable(True)
        # Eureka!
        # setWidgetResizable(True) will resize the Property Manager (and its
        # contents) correctly when the scrollbar appears/disappears.
        # It even accounts correctly for collapsed/expanded groupboxes!
        # Mark 2007-05-29

        # Add the property manager scroll area as a "tabbed" widget.
        # Set the tab icon, too.
        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"), "")

        # Finally, add the "pwProjectTabWidget" to the left channel layout.

        leftChannelVBoxLayout.addWidget(self.pwProjectTabWidget)

        # Create the glpane and make it a child of the part splitter.
        self.glpane = GLPane(assy, self, 'glpane name', parent)
        # note: our owner (MWsemantics) assumes
        # there is just this one GLPane for assy, and stores it
        # into assy as assy.o and assy.glpane. [bruce 080216 comment]

        # Add what's this text to self.glpane.
        # [bruce 080912 moved this here from part of a method in class GLPane.
        #  In this code's old location, Mark wrote [2007-06-01]: "Problem -
        #  I don't believe this text is processed by fix_whatsthis_text_and_links()
        #  in whatsthis_utilities.py." Now that this code is here, I don't know
        #  whether that's still true. ]
        from ne1_ui.WhatsThisText_for_MainWindow import whats_this_text_for_glpane
        self.glpane.setWhatsThis(whats_this_text_for_glpane())

        # update [re the above comment], bruce 081209:
        # I added the following explicit call of fix_whatsthis_text_and_links,
        # but it doesn't work to replace Ctrl with Cmd on Mac;
        # see today's comment in fix_whatsthis_text_and_links for likely reason.
        # So I will leave this here, but also leave in place the kluges
        # in whats_this_text_for_glpane to do that replacement itself.
        # The wiki help link in this whatsthis text doesn't work,
        # but I guess that is an independent issue, related to lack
        # of use of class QToolBar_WikiHelp or similar code, for GLPane
        # or this class or the main window class.
        from foundation.whatsthis_utilities import fix_whatsthis_text_and_links
        fix_whatsthis_text_and_links(self.glpane)  # doesn't yet work

        self.pwProjectTabWidget.KLUGE_setGLPane(self.glpane)
        # help fix bug 2522 [bruce 070829]
        qt4warnDestruction(self.glpane, 'GLPane of PartWindow')
        pwSplitter.addWidget(self.glpane)

        # ##################################################################
        # <pwBottomArea> is a container at the bottom of the part window
        # spanning its entire width. It is intended to be used as an extra
        # area for use by Property Managers (or anything else) that needs
        # a landscape oriented layout.
        # An example is the Sequence Editor, which is part of the
        # Strand Properties PM.
        self.pwBottomArea = QFrame()
        # IMHO, self is not a good parent. Mark 2008-01-04.
        pwBottomArea = self.pwBottomArea
        pwBottomArea.setObjectName("pwBottomArea")
        pwBottomArea.setMaximumHeight(50)

        # Add a frame border to see what it looks like.
        pwBottomArea.setFrameStyle(QFrame.Panel | QFrame.Sunken)

        self.pwVBoxLayout.addWidget(pwBottomArea)

        # Hide the bottom frame for now. Later this might be used for the
        # sequence editor.
        pwBottomArea.hide()

        #This widget implementation is subject to heavy revision. The purpose
        #is to implement a NFR that Mark urgently needs : The NFR is: Need a
        #way to quickly find a node in the MT by entering its name.
        #-- Ninad 2008-11-06
        self.pwSpecialDockWidgetInLeftChannel = SelectNodeByNameDockWidget(
            self.glpane.win)
        leftChannelVBoxLayout.addWidget(self.pwSpecialDockWidgetInLeftChannel)

        # See the resizeEvent() docstring for more information about
        # resizeTimer.
        self.resizeTimer = QTimer(self)
        self.resizeTimer.setSingleShot(True)
        return

    def getLeftChannelDockWidget(self):
        return self.pwSpecialDockWidgetInLeftChannel

    def updateWindowTitle(self, changed=False):
        #by mark; bruce 050810 revised this in several ways, fixed bug 785
        """
        Update the window title (caption) at the top of the part window.
        Example:  "partname.mmp"

        This implements the standard way most applications indicate that a
        document has unsaved changes. On Mac OS X the close button will have
        a modified look; on other platforms the window title will have
        an '*' (asterisk).

        @note: We'll want to experiment with this to make sure it

        @param changed: If True, the document has unsaved changes.
        @type  changed: boolean

        @see: U{B{windowTitle}<http://doc.trolltech.com/4/qwidget.html#windowTitle-prop>},
              U{B{windowModified}<http://doc.trolltech.com/4/qwidget.html#windowModified-prop>}
        """
        # WARNING: there is mostly-duplicated code in this method and in
        # MWsemantics.update_mainwindow_caption. See the comment in that
        # method for more info and todos. [bruce 081227 comment]
        caption_fullpath = env.prefs[captionFullPath_prefs_key]

        partname = "Untitled"  # fallback value if no file yet
        if self.assy.filename:  #bruce 081227 cleanup: try -> if, etc
            # self.assy.filename is always an empty string, even after a
            # file has been opened with a complete name. Need to ask Bruce
            # about this problem, resulting in a bug (i.e. the window title
            # is always "Untitled". Mark 2008-01-02.
            junk, basename = os.path.split(self.assy.filename)
            if basename:
                if caption_fullpath:
                    partname = os.path.normpath(self.assy.filename)
                    #fixed bug 453-1 ninad060721
                else:
                    partname = basename

        # WARNING: the following code differs in the two versions
        # of this routine.
        # The "[*]" placeholder below is modified or removed by Qt; see:
        # http://doc.trolltech.com/4/qwidget.html#windowModified-prop
        self.setWindowTitle(self.trUtf8(partname + '[*]'))
        self.setWindowModified(changed)  # replaces '[*]' by '' or '*'
        return

    def collapseLeftArea(self, hideLeftArea=True):
        """
        Make the left area collapsible (via the splitter). The left area
        will be hidden (collapsed,actually) if I{hideLeftArea} is True 
        (the default).
        """
        self._previous_splitterPosition = self.pwLeftArea.width()
        if hideLeftArea:
            self.pwSplitter.setCollapsible(0, True)
            self.setSplitterPosition(pos=0)
        return

    def expandLeftArea(self):
        """
        Expand the left area.

        @see: L{MWsemantics._showFullScreenCommonCode()} for an example
        showing how it is used.
        """
        self.setSplitterPosition(pos=self._previous_splitterPosition)
        self.pwSplitter.setCollapsible(0, False)
        self.pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        self.pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)
        return

    def updatePropertyManagerTab(self, tab):  #Ninad 061207
        "Update the Properties Manager tab with 'tab' "

        self.parent.glpane.gl_update_confcorner()
        #bruce 070627, since PM affects confcorner appearance

        if self.propertyManagerScrollArea.widget():
            # The following is necessary to get rid of those C object
            # deleted errors (and the resulting bugs)
            lastwidgetobject = self.propertyManagerScrollArea.takeWidget()
            if lastwidgetobject:
                # bruce 071018 revised this code; see my comment on same
                # code in PM_Dialog
                try:
                    lastwidgetobject.update_props_if_needed_before_closing
                except AttributeError:
                    if 1 or debug_flags.atom_debug:
                        msg1 = "Last PropMgr %r doesn't have method" % lastwidgetobject
                        msg2 = " update_props_if_needed_before_closing. That's"
                        msg3 = " OK (for now, only implemented for Plane PM). "
                        msg4 = "Ignoring Exception: "
                        print_compact_traceback(msg1 + msg2 + msg3 + msg4)
                else:
                    lastwidgetobject.update_props_if_needed_before_closing()

            lastwidgetobject.hide()
            # @ ninad 061212 perhaps hiding the widget is not needed

        self.pwProjectTabWidget.removeTab(
            self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea))

        # Set the PropertyManager tab scroll area to the appropriate widget.
        self.propertyManagerScrollArea.setWidget(tab)

        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"), "")

        self.pwProjectTabWidget.setCurrentIndex(
            self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea))
        return

    def KLUGE_current_PropertyManager(self):
        #bruce 070627; revised 070829 as part of fixing bug 2523
        """
        Return the current Property Manager widget (whether or not its tab is
        chosen, but only if it has a tab), or None if there is not one.

        @warning: This method's existence (not only its implementation)
        is a kluge, since the right way to access that would be by asking
        the "command sequencer";
        but that's not yet implemented, so this is the best we can do for now.
        Also, it would be better to get the top command and talk to it, not
        its PM (a QWidget). Also, whatever calls this will be making
        assumptions about that PM which are really only the command's business.
        So in short, every call of this is in need of cleanup once we have a
        working "command sequencer". (That's true of many things related to
        PMs, not only this method.)

        @warning: The return values are (presumably) widgets, but they can
        also be mode objects and generator objects, due to excessive use of
        multiple inheritance in the current PM code. So be careful what you
        do with them -- they might have lots of extra methods/attrs,
        and setting your own attrs in them might mess things up.
        """
        res = self.propertyManagerScrollArea.widget()
        if not hasattr(res, 'done_btn'):
            # not sure what widget this is otherwise, but it is a widget
            # (not None) for the default mode, at least on startup, so just
            # return None in this case
            return None
        # Sometimes this PM remains present from a prior command, even when
        # there is no longer a tab for the PM. As part of fixing bug 2523
        # we have to avoid returning it in that case. How we do that is a kluge,
        # but hopefully this entire kluge function can be dispensed with soon.
        # This change also fixes bug 2522 on the Mac (but not on Windows --
        # for that, we needed to intercept removeTab in separate code above).
        index = self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea)
        if index == -1:
            return None
        # Due to bugs in other code, sometimes the PM tab is left in place,
        # though the PM itself is hidden. To avoid finding the PM in that case,
        # also check whether it's hidden. This will fix the CC part of a new bug
        # just reported by Keith in email (when hitting Ok in DNA Gen).
        if res.isHidden():  # probably a QWidget method [bruce 080205 comment]
            return None
        return res

    def dismiss(self):
        self.parent.removePartWindow(self)
        return

    def setSplitterPosition(self, pos=PM_DEFAULT_WIDTH, setDefault=True):
        """
        Set the position of the splitter between the left area and graphics area
        so that the width of the container holding the model tree (and 
        property manager) is I{pos} pixels wide.
        
        @param pos: The splitter position (in pixel units).
        @type  pos: int
        
        @param setDefault: If True (the default), I{pos} becomes the new default
                           position.
        @type  setDefault: boolean
        """
        self.pwSplitter.moveSplitter(pos, 1)
        if _DEBUG:
            print "New Splitter Position: %d (setDefault=%d)" \
                  % (pos, setDefault)
        if setDefault:
            self.splitterPosition = pos
        return

    def resizeEvent(self, event):
        """
        This reimplementation of QWidget.resizeEvent is here to deal with the
        undesired behavior of the splitter while resizing the part window.
        Normally, the splitter will drift back and forth while resizing
        the part window. This forces the splitter to stay fixed during
        resize operations.
        """
        # When self.resizeTimer.isActive() = True, the partwindow is being
        # resized. This is checked by the resizeEvent handler in LeftFrame
        # to determine if the splitter is being moved by the user or
        # programmably by self's resizeEvent.
        if self.resizeTimer.isActive():
            self.resizeTimer.stop()  # Stop the timer.
        self.resizeTimer.start(500)  # (Re)strand a .5 second singleshot timer.
        self.setSplitterPosition(self.splitterPosition, setDefault=False)
        QWidget.resizeEvent(self, event)
        return
コード例 #31
0
ファイル: single.py プロジェクト: siebert/calibre
class MetadataSingleDialogBase(ResizableDialog):

    view_format = pyqtSignal(object, object)
    cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields']
    one_line_comments_toolbar = False
    use_toolbutton_for_config_metadata = True

    def __init__(self, db, parent=None):
        self.db = db
        self.changed = set()
        self.books_to_refresh = set()
        self.rows_to_refresh = set()
        ResizableDialog.__init__(self, parent)

    def setupUi(self, *args):  # {{{
        self.resize(990, 670)

        self.download_shortcut = QShortcut(self)
        self.download_shortcut.setKey(
            QKeySequence('Ctrl+D', QKeySequence.PortableText))
        p = self.parent()
        if hasattr(p, 'keyboard'):
            kname = u'Interface Action: Edit Metadata (Edit Metadata) : menu action : download'
            sc = p.keyboard.keys_map.get(kname, None)
            if sc:
                self.download_shortcut.setKey(sc[0])

        self.button_box = bb = QDialogButtonBox(self)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        self.next_button = QPushButton(QIcon(I('forward.png')), _('Next'),
                                       self)
        self.next_button.setShortcut(QKeySequence('Alt+Right'))
        self.next_button.clicked.connect(self.next_clicked)
        self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'),
                                       self)
        self.prev_button.setShortcut(QKeySequence('Alt+Left'))

        self.button_box.addButton(self.prev_button, bb.ActionRole)
        self.button_box.addButton(self.next_button, bb.ActionRole)
        self.prev_button.clicked.connect(self.prev_clicked)
        bb.setStandardButtons(bb.Ok | bb.Cancel)
        bb.button(bb.Ok).setDefault(True)

        self.scroll_area = QScrollArea(self)
        self.scroll_area.setFrameShape(QScrollArea.NoFrame)
        self.scroll_area.setWidgetResizable(True)
        self.central_widget = QTabWidget(self)
        self.scroll_area.setWidget(self.central_widget)

        self.l = QVBoxLayout(self)
        self.setLayout(self.l)
        self.l.addWidget(self.scroll_area)
        ll = self.button_box_layout = QHBoxLayout()
        self.l.addLayout(ll)
        ll.addSpacing(10)
        ll.addWidget(self.button_box)

        self.setWindowIcon(QIcon(I('edit_input.png')))
        self.setWindowTitle(BASE_TITLE)

        self.create_basic_metadata_widgets()

        if len(self.db.custom_column_label_map):
            self.create_custom_metadata_widgets()

        self.do_layout()
        geom = gprefs.get('metasingle_window_geometry3', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))

    # }}}

    def create_basic_metadata_widgets(self):  # {{{
        self.basic_metadata_widgets = []

        self.languages = LanguagesEdit(self)
        self.basic_metadata_widgets.append(self.languages)

        self.title = TitleEdit(self)
        self.title.textChanged.connect(self.update_window_title)
        self.deduce_title_sort_button = QToolButton(self)
        self.deduce_title_sort_button.setToolTip(
            _('Automatically create the title sort entry based on the current '
              'title entry.\nUsing this button to create title sort will '
              'change title sort from red to green.'))
        self.deduce_title_sort_button.setWhatsThis(
            self.deduce_title_sort_button.toolTip())
        self.title_sort = TitleSortEdit(self, self.title,
                                        self.deduce_title_sort_button,
                                        self.languages)
        self.basic_metadata_widgets.extend([self.title, self.title_sort])

        self.deduce_author_sort_button = b = QToolButton(self)
        b.setToolTip('<p>' + _(
            'Automatically create the author sort entry based on the current '
            'author entry. Using this button to create author sort will '
            'change author sort from red to green.  There is a menu of '
            'functions available under this button. Click and hold '
            'on the button to see it.') + '</p>')
        b.m = m = QMenu()
        ac = m.addAction(QIcon(I('forward.png')),
                         _('Set author sort from author'))
        ac2 = m.addAction(QIcon(I('back.png')),
                          _('Set author from author sort'))
        ac3 = m.addAction(QIcon(I('user_profile.png')), _('Manage authors'))
        ac4 = m.addAction(QIcon(I('next.png')),
                          _('Copy author to author sort'))
        ac5 = m.addAction(QIcon(I('previous.png')),
                          _('Copy author sort to author'))

        b.setMenu(m)
        self.authors = AuthorsEdit(self, ac3)
        self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac,
                                          ac2, ac4, ac5)
        self.basic_metadata_widgets.extend([self.authors, self.author_sort])

        self.swap_title_author_button = QToolButton(self)
        self.swap_title_author_button.setIcon(QIcon(I('swap.png')))
        self.swap_title_author_button.setToolTip(
            _('Swap the author and title'))
        self.swap_title_author_button.clicked.connect(self.swap_title_author)

        self.manage_authors_button = QToolButton(self)
        self.manage_authors_button.setIcon(QIcon(I('user_profile.png')))
        self.manage_authors_button.setToolTip(
            '<p>' + _('Manage authors. Use to rename authors and correct '
                      'individual author\'s sort values') + '</p>')
        self.manage_authors_button.clicked.connect(self.authors.manage_authors)

        self.series = SeriesEdit(self)
        self.clear_series_button = QToolButton(self)
        self.clear_series_button.setToolTip(_('Clear series'))
        self.clear_series_button.clicked.connect(self.series.clear)
        self.series_index = SeriesIndexEdit(self, self.series)
        self.basic_metadata_widgets.extend([self.series, self.series_index])

        self.formats_manager = FormatsManager(self, self.copy_fmt)
        # We want formats changes to be committed before title/author, as
        # otherwise we could have data loss if the title/author changed and the
        # user was trying to add an extra file from the old books directory.
        self.basic_metadata_widgets.insert(0, self.formats_manager)
        self.formats_manager.metadata_from_format_button.clicked.connect(
            self.metadata_from_format)
        self.formats_manager.cover_from_format_button.clicked.connect(
            self.cover_from_format)
        self.cover = Cover(self)
        self.cover.download_cover.connect(self.download_cover)
        self.basic_metadata_widgets.append(self.cover)

        self.comments = CommentsEdit(self, self.one_line_comments_toolbar)
        self.basic_metadata_widgets.append(self.comments)

        self.rating = RatingEdit(self)
        self.clear_ratings_button = QToolButton(self)
        self.clear_ratings_button.setToolTip(_('Clear rating'))
        self.clear_ratings_button.setIcon(QIcon(I('trash.png')))
        self.clear_ratings_button.clicked.connect(self.rating.zero)

        self.basic_metadata_widgets.append(self.rating)

        self.tags = TagsEdit(self)
        self.tags_editor_button = QToolButton(self)
        self.tags_editor_button.setToolTip(_('Open Tag Editor'))
        self.tags_editor_button.setIcon(QIcon(I('chapters.png')))
        self.tags_editor_button.clicked.connect(self.tags_editor)
        self.clear_tags_button = QToolButton(self)
        self.clear_tags_button.setToolTip(_('Clear all tags'))
        self.clear_tags_button.setIcon(QIcon(I('trash.png')))
        self.clear_tags_button.clicked.connect(self.tags.clear)
        self.basic_metadata_widgets.append(self.tags)

        self.identifiers = IdentifiersEdit(self)
        self.basic_metadata_widgets.append(self.identifiers)
        self.clear_identifiers_button = QToolButton(self)
        self.clear_identifiers_button.setIcon(QIcon(I('trash.png')))
        self.clear_identifiers_button.setToolTip(_('Clear Ids'))
        self.clear_identifiers_button.clicked.connect(self.identifiers.clear)
        self.paste_isbn_button = QToolButton(self)
        self.paste_isbn_button.setToolTip(
            '<p>' + _('Paste the contents of the clipboard into the '
                      'identifiers box prefixed with isbn:') + '</p>')
        self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png')))
        self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn)

        self.publisher = PublisherEdit(self)
        self.basic_metadata_widgets.append(self.publisher)

        self.timestamp = DateEdit(self)
        self.pubdate = PubdateEdit(self)
        self.basic_metadata_widgets.extend([self.timestamp, self.pubdate])

        self.fetch_metadata_button = QPushButton(_('&Download metadata'), self)
        self.fetch_metadata_button.clicked.connect(self.fetch_metadata)
        self.download_shortcut.activated.connect(
            self.fetch_metadata_button.click)
        font = self.fmb_font = QFont()
        font.setBold(True)
        self.fetch_metadata_button.setFont(font)

        if self.use_toolbutton_for_config_metadata:
            self.config_metadata_button = QToolButton(self)
            self.config_metadata_button.setIcon(QIcon(I('config.png')))
        else:
            self.config_metadata_button = QPushButton(self)
            self.config_metadata_button.setText(
                _('Configure download metadata'))
        self.config_metadata_button.setIcon(QIcon(I('config.png')))
        self.config_metadata_button.clicked.connect(self.configure_metadata)
        self.config_metadata_button.setToolTip(
            _('Change how calibre downloads metadata'))

    # }}}

    def create_custom_metadata_widgets(self):  # {{{
        self.custom_metadata_widgets_parent = w = QWidget(self)
        layout = QGridLayout()
        w.setLayout(layout)
        self.custom_metadata_widgets, self.__cc_spacers = \
            populate_metadata_page(layout, self.db, None, parent=w, bulk=False,
                two_column=self.cc_two_column)
        self.__custom_col_layouts = [layout]

    # }}}

    def set_custom_metadata_tab_order(self, before=None, after=None):  # {{{
        sto = QWidget.setTabOrder
        if getattr(self, 'custom_metadata_widgets', []):
            ans = self.custom_metadata_widgets
            for i in range(len(ans) - 1):
                if before is not None and i == 0:
                    pass
                if len(ans[i + 1].widgets) == 2:
                    sto(ans[i].widgets[-1], ans[i + 1].widgets[1])
                else:
                    sto(ans[i].widgets[-1], ans[i + 1].widgets[0])
                for c in range(2, len(ans[i].widgets), 2):
                    sto(ans[i].widgets[c - 1], ans[i].widgets[c + 1])
            if after is not None:
                pass

    # }}}

    def do_view_format(self, path, fmt):
        if path:
            self.view_format.emit(None, path)
        else:
            self.view_format.emit(self.book_id, fmt)

    def copy_fmt(self, fmt, f):
        self.db.copy_format_to(self.book_id, fmt, f, index_is_id=True)

    def do_layout(self):
        raise NotImplementedError()

    def __call__(self, id_):
        self.book_id = id_
        self.books_to_refresh = set([])
        for widget in self.basic_metadata_widgets:
            widget.initialize(self.db, id_)
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.initialize(id_)
        if callable(self.set_current_callback):
            self.set_current_callback(id_)
        # Commented out as it doesn't play nice with Next, Prev buttons
        # self.fetch_metadata_button.setFocus(Qt.OtherFocusReason)

    # Miscellaneous interaction methods {{{
    def update_window_title(self, *args):
        title = self.title.current_val
        if len(title) > 50:
            title = title[:50] + u'\u2026'
        self.setWindowTitle(
            BASE_TITLE + ' - ' + title + ' - ' + _(' [%(num)d of %(tot)d]') %
            dict(num=self.current_row + 1, tot=len(self.row_list)))

    def swap_title_author(self, *args):
        title = self.title.current_val
        self.title.current_val = authors_to_string(self.authors.current_val)
        self.authors.current_val = string_to_authors(title)
        self.title_sort.auto_generate()
        self.author_sort.auto_generate()

    def tags_editor(self, *args):
        self.tags.edit(self.db, self.book_id)

    def metadata_from_format(self, *args):
        mi, ext = self.formats_manager.get_selected_format_metadata(
            self.db, self.book_id)
        if mi is not None:
            self.update_from_mi(mi)

    def get_pdf_cover(self):
        pdfpath = self.formats_manager.get_format_path(self.db, self.book_id,
                                                       'pdf')
        from calibre.gui2.metadata.pdf_covers import PDFCovers
        d = PDFCovers(pdfpath, parent=self)
        if d.exec_() == d.Accepted:
            cpath = d.cover_path
            if cpath:
                with open(cpath, 'rb') as f:
                    self.update_cover(f.read(), 'PDF')
        d.cleanup()

    def cover_from_format(self, *args):
        ext = self.formats_manager.get_selected_format()
        if ext is None:
            return
        if ext == 'pdf':
            return self.get_pdf_cover()
        try:
            mi, ext = self.formats_manager.get_selected_format_metadata(
                self.db, self.book_id)
        except (IOError, OSError) as err:
            if getattr(err, 'errno',
                       None) == errno.EACCES:  # Permission denied
                import traceback
                fname = err.filename if err.filename else 'file'
                error_dialog(self,
                             _('Permission denied'),
                             _('Could not open %s. Is it being used by another'
                               ' program?') % fname,
                             det_msg=traceback.format_exc(),
                             show=True)
                return
            raise
        if mi is None:
            return
        cdata = None
        if mi.cover and os.access(mi.cover, os.R_OK):
            cdata = open(mi.cover).read()
        elif mi.cover_data[1] is not None:
            cdata = mi.cover_data[1]
        if cdata is None:
            error_dialog(self, _('Could not read cover'),
                         _('Could not read cover from %s format') %
                         ext).exec_()
            return
        self.update_cover(cdata, ext)

    def update_cover(self, cdata, fmt):
        orig = self.cover.current_val
        self.cover.current_val = cdata
        if self.cover.current_val is None:
            self.cover.current_val = orig
            return error_dialog(self,
                                _('Could not read cover'),
                                _('The cover in the %s format is invalid') %
                                fmt,
                                show=True)
            return

    def update_from_mi(self,
                       mi,
                       update_sorts=True,
                       merge_tags=True,
                       merge_comments=False):
        if not mi.is_null('title'):
            self.title.current_val = mi.title
            if update_sorts:
                self.title_sort.auto_generate()
        if not mi.is_null('authors'):
            self.authors.current_val = mi.authors
        if not mi.is_null('author_sort'):
            self.author_sort.current_val = mi.author_sort
        elif update_sorts:
            self.author_sort.auto_generate()
        if not mi.is_null('rating'):
            try:
                self.rating.current_val = mi.rating
            except:
                pass
        if not mi.is_null('publisher'):
            self.publisher.current_val = mi.publisher
        if not mi.is_null('tags'):
            old_tags = self.tags.current_val
            tags = mi.tags if mi.tags else []
            if old_tags and merge_tags:
                ltags, lotags = {t.lower()
                                 for t in tags}, {t.lower()
                                                  for t in old_tags}
                tags = [t for t in tags if t.lower() in ltags - lotags
                        ] + old_tags
            self.tags.current_val = tags
        if not mi.is_null('identifiers'):
            current = self.identifiers.current_val
            current.update(mi.identifiers)
            self.identifiers.current_val = current
        if not mi.is_null('pubdate'):
            self.pubdate.current_val = mi.pubdate
        if not mi.is_null('series') and mi.series.strip():
            self.series.current_val = mi.series
            if mi.series_index is not None:
                self.series_index.reset_original()
                self.series_index.current_val = float(mi.series_index)
        if not mi.is_null('languages'):
            langs = [canonicalize_lang(x) for x in mi.languages]
            langs = [x for x in langs if x is not None]
            if langs:
                self.languages.current_val = langs
        if mi.comments and mi.comments.strip():
            val = mi.comments
            if val and merge_comments:
                cval = self.comments.current_val
                if cval:
                    val = merge_two_comments(cval, val)
            self.comments.current_val = val

    def fetch_metadata(self, *args):
        d = FullFetch(self.cover.pixmap(), self)
        ret = d.start(title=self.title.current_val,
                      authors=self.authors.current_val,
                      identifiers=self.identifiers.current_val)
        if ret == d.Accepted:
            from calibre.ebooks.metadata.sources.prefs import msprefs
            mi = d.book
            dummy = Metadata(_('Unknown'))
            for f in msprefs['ignore_fields']:
                if ':' not in f:
                    setattr(mi, f, getattr(dummy, f))
            if mi is not None:
                pd = mi.pubdate
                if pd is not None:
                    # Put the downloaded published date into the local timezone
                    # as we discard time info and the date is timezone
                    # invariant. This prevents the as_local_timezone() call in
                    # update_from_mi from changing the pubdate
                    mi.pubdate = datetime(pd.year,
                                          pd.month,
                                          pd.day,
                                          tzinfo=local_tz)
                self.update_from_mi(mi,
                                    merge_comments=msprefs['append_comments'])
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    def configure_metadata(self):
        from calibre.gui2.preferences import show_config_widget
        gui = self.parent()
        show_config_widget('Sharing',
                           'Metadata download',
                           parent=self,
                           gui=gui,
                           never_shutdown=True)

    def download_cover(self, *args):
        from calibre.gui2.metadata.single_download import CoverFetch
        d = CoverFetch(self.cover.pixmap(), self)
        ret = d.start(self.title.current_val, self.authors.current_val,
                      self.identifiers.current_val)
        if ret == d.Accepted:
            if d.cover_pixmap is not None:
                self.cover.current_val = pixmap_to_data(d.cover_pixmap)

    # }}}

    def apply_changes(self):
        self.changed.add(self.book_id)
        if self.db is None:
            # break_cycles has already been called, don't know why this should
            # happen but a user reported it
            return True
        for widget in self.basic_metadata_widgets:
            try:
                if not widget.commit(self.db, self.book_id):
                    return False
                self.books_to_refresh |= getattr(widget, 'books_to_refresh',
                                                 set([]))
            except (IOError, OSError) as err:
                if getattr(err, 'errno',
                           None) == errno.EACCES:  # Permission denied
                    import traceback
                    fname = getattr(err, 'filename', None)
                    p = 'Locked file: %s\n\n' % fname if fname else ''
                    error_dialog(
                        self,
                        _('Permission denied'),
                        _('Could not change the on disk location of this'
                          ' book. Is it open in another program?'),
                        det_msg=p + traceback.format_exc(),
                        show=True)
                    return False
                raise
        for widget in getattr(self, 'custom_metadata_widgets', []):
            self.books_to_refresh |= widget.commit(self.book_id)

        self.db.commit()
        rows = self.db.refresh_ids(list(self.books_to_refresh))
        if rows:
            self.rows_to_refresh |= set(rows)

        return True

    def accept(self):
        self.save_state()
        if not self.apply_changes():
            return
        ResizableDialog.accept(self)

    def reject(self):
        self.save_state()
        ResizableDialog.reject(self)

    def save_state(self):
        try:
            gprefs['metasingle_window_geometry3'] = bytearray(
                self.saveGeometry())
        except:
            # Weird failure, see https://bugs.launchpad.net/bugs/995271
            import traceback
            traceback.print_exc()

    # Dialog use methods {{{
    def start(self,
              row_list,
              current_row,
              view_slot=None,
              set_current_callback=None):
        self.row_list = row_list
        self.current_row = current_row
        if view_slot is not None:
            self.view_format.connect(view_slot)
        self.set_current_callback = set_current_callback
        self.do_one(apply_changes=False)
        ret = self.exec_()
        self.break_cycles()
        return ret

    def next_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=1, apply_changes=False)

    def prev_clicked(self):
        if not self.apply_changes():
            return
        self.do_one(delta=-1, apply_changes=False)

    def do_one(self, delta=0, apply_changes=True):
        if apply_changes:
            self.apply_changes()
        self.current_row += delta
        prev = next_ = None
        if self.current_row > 0:
            prev = self.db.title(self.row_list[self.current_row - 1])
        if self.current_row < len(self.row_list) - 1:
            next_ = self.db.title(self.row_list[self.current_row + 1])

        if next_ is not None:
            tip = (_('Save changes and edit the metadata of %s') +
                   ' [Alt+Right]') % next_
            self.next_button.setToolTip(tip)
        self.next_button.setEnabled(next_ is not None)
        if prev is not None:
            tip = (_('Save changes and edit the metadata of %s') +
                   ' [Alt+Left]') % prev
            self.prev_button.setToolTip(tip)
        self.prev_button.setEnabled(prev is not None)
        self.button_box.button(self.button_box.Ok).setDefault(True)
        self.button_box.button(self.button_box.Ok).setFocus(
            Qt.OtherFocusReason)
        self(self.db.id(self.row_list[self.current_row]))

    def break_cycles(self):
        # Break any reference cycles that could prevent python
        # from garbage collecting this dialog
        self.set_current_callback = self.db = None

        def disconnect(signal):
            try:
                signal.disconnect()
            except:
                pass  # Fails if view format was never connected

        disconnect(self.view_format)
        for b in ('next_button', 'prev_button'):
            x = getattr(self, b, None)
            if x is not None:
                disconnect(x.clicked)
        for widget in self.basic_metadata_widgets:
            bc = getattr(widget, 'break_cycles', None)
            if bc is not None and callable(bc):
                bc()
        for widget in getattr(self, 'custom_metadata_widgets', []):
            widget.break_cycles()
コード例 #32
0
    def __init__(self, assy, parent):
        """
        Constructor for the part window.

        @param assy: The assembly (part)
        @type  assy: Assembly

        @param parent: The parent widget.
        @type  parent: U{B{QMainWindow}
                       <http://doc.trolltech.com/4/qmainwindow.html>}
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.assy = assy
        # note: to support MDI, self.assy would probably need to be a
        # different assembly for each PartWindow.
        # [bruce 080216 comment]
        self.setWindowIcon(geticon("ui/border/Part.png"))
        self.updateWindowTitle()

        # The main layout for the part window is a VBoxLayout <pwVBoxLayout>.
        self.pwVBoxLayout = QVBoxLayout(self)
        pwVBoxLayout = self.pwVBoxLayout
        pwVBoxLayout.setMargin(0)
        pwVBoxLayout.setSpacing(0)

        # ################################################################
        # <pwSplitter> is the horizontal splitter b/w the
        # pwLeftArea (mt and pm) and the glpane.
        self.pwSplitter = QSplitter(Qt.Horizontal)
        pwSplitter = self.pwSplitter
        pwSplitter.setObjectName("pwSplitter")
        pwSplitter.setHandleWidth(3)  # 3 pixels wide.
        pwVBoxLayout.addWidget(pwSplitter)

        # ##################################################################
        # <pwLeftArea> is the container holding the pwProjectTabWidget.
        # Note: Making pwLeftArea (and pwRightArea and pwBottomArea) QFrame
        # widgets has the benefit of making it easy to draw a border around
        # each area. One purpose of this would be to help developers understand
        # (visually) how the part window is laid out. I intend to add a debug
        # pref to draw part window area borders and add "What's This" text to
        # them. Mark 2008-01-05.
        self.pwLeftArea = LeftFrame(self)
        pwLeftArea = self.pwLeftArea
        pwLeftArea.setObjectName("pwLeftArea")
        pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)

        # Setting the frame style like this is nice since it clearly
        # defines the splitter at the top-left corner.
        pwLeftArea.setFrameStyle(QFrame.Panel | QFrame.Sunken)

        # This layout will contain splitter (above) and the pwBottomArea.
        leftChannelVBoxLayout = QVBoxLayout(pwLeftArea)
        leftChannelVBoxLayout.setMargin(0)
        leftChannelVBoxLayout.setSpacing(0)

        pwSplitter.addWidget(pwLeftArea)

        # Makes it so pwLeftArea is not collapsible.
        pwSplitter.setCollapsible(0, False)

        # ##################################################################
        # <pwProjectTabWidget> is a QTabWidget that contains the MT and PM
        # widgets. It lives in the "left area" of the part window.
        self.pwProjectTabWidget = _pwProjectTabWidget()
        # _pwProjectTabWidget subclasses QTabWidget
        # Note [bruce 070829]: to fix bug 2522 I need to intercept
        # self.pwProjectTabWidget.removeTab, so I made it a subclass of
        # QTabWidget. It needs to know the GLPane, but that's not created
        # yet, so we set it later using KLUGE_setGLPane (below).
        # Note: No parent supplied. Could this be the source of the
        # minor vsplitter resizing problem I was trying to resolve a few
        # months ago?  Try supplying a parent later. Mark 2008-01-01
        self.pwProjectTabWidget.setObjectName("pwProjectTabWidget")
        self.pwProjectTabWidget.setCurrentIndex(0)
        self.pwProjectTabWidget.setAutoFillBackground(True)

        # Create the model tree "tab" widget. It will contain the MT GUI widget.
        # Set the tab icon, too.
        self.modelTreeTab = QWidget()
        self.modelTreeTab.setObjectName("modelTreeTab")
        self.pwProjectTabWidget.addTab(self.modelTreeTab,
                                       geticon("ui/modeltree/Model_Tree.png"),
                                       "")

        modelTreeTabLayout = QVBoxLayout(self.modelTreeTab)
        modelTreeTabLayout.setMargin(0)
        modelTreeTabLayout.setSpacing(0)

        # Create the model tree (GUI) and add it to the tab layout.
        self.modelTree = ModelTree(self.modelTreeTab, parent)
        self.modelTree.modelTreeGui.setObjectName("modelTreeGui")
        modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui)

        # Create the property manager "tab" widget. It will contain the PropMgr
        # scroll area, which will contain the property manager and all its
        # widgets.
        self.propertyManagerTab = QWidget()
        self.propertyManagerTab.setObjectName("propertyManagerTab")

        self.propertyManagerScrollArea = QScrollArea(self.pwProjectTabWidget)
        self.propertyManagerScrollArea.setObjectName(
            "propertyManagerScrollArea")
        self.propertyManagerScrollArea.setWidget(self.propertyManagerTab)
        self.propertyManagerScrollArea.setWidgetResizable(True)
        # Eureka!
        # setWidgetResizable(True) will resize the Property Manager (and its
        # contents) correctly when the scrollbar appears/disappears.
        # It even accounts correctly for collapsed/expanded groupboxes!
        # Mark 2007-05-29

        # Add the property manager scroll area as a "tabbed" widget.
        # Set the tab icon, too.
        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"), "")

        # Finally, add the "pwProjectTabWidget" to the left channel layout.

        leftChannelVBoxLayout.addWidget(self.pwProjectTabWidget)

        # Create the glpane and make it a child of the part splitter.
        self.glpane = GLPane(assy, self, 'glpane name', parent)
        # note: our owner (MWsemantics) assumes
        # there is just this one GLPane for assy, and stores it
        # into assy as assy.o and assy.glpane. [bruce 080216 comment]

        # Add what's this text to self.glpane.
        # [bruce 080912 moved this here from part of a method in class GLPane.
        #  In this code's old location, Mark wrote [2007-06-01]: "Problem -
        #  I don't believe this text is processed by fix_whatsthis_text_and_links()
        #  in whatsthis_utilities.py." Now that this code is here, I don't know
        #  whether that's still true. ]
        from ne1_ui.WhatsThisText_for_MainWindow import whats_this_text_for_glpane
        self.glpane.setWhatsThis(whats_this_text_for_glpane())

        # update [re the above comment], bruce 081209:
        # I added the following explicit call of fix_whatsthis_text_and_links,
        # but it doesn't work to replace Ctrl with Cmd on Mac;
        # see today's comment in fix_whatsthis_text_and_links for likely reason.
        # So I will leave this here, but also leave in place the kluges
        # in whats_this_text_for_glpane to do that replacement itself.
        # The wiki help link in this whatsthis text doesn't work,
        # but I guess that is an independent issue, related to lack
        # of use of class QToolBar_WikiHelp or similar code, for GLPane
        # or this class or the main window class.
        from foundation.whatsthis_utilities import fix_whatsthis_text_and_links
        fix_whatsthis_text_and_links(self.glpane)  # doesn't yet work

        self.pwProjectTabWidget.KLUGE_setGLPane(self.glpane)
        # help fix bug 2522 [bruce 070829]
        qt4warnDestruction(self.glpane, 'GLPane of PartWindow')
        pwSplitter.addWidget(self.glpane)

        # ##################################################################
        # <pwBottomArea> is a container at the bottom of the part window
        # spanning its entire width. It is intended to be used as an extra
        # area for use by Property Managers (or anything else) that needs
        # a landscape oriented layout.
        # An example is the Sequence Editor, which is part of the
        # Strand Properties PM.
        self.pwBottomArea = QFrame()
        # IMHO, self is not a good parent. Mark 2008-01-04.
        pwBottomArea = self.pwBottomArea
        pwBottomArea.setObjectName("pwBottomArea")
        pwBottomArea.setMaximumHeight(50)

        # Add a frame border to see what it looks like.
        pwBottomArea.setFrameStyle(QFrame.Panel | QFrame.Sunken)

        self.pwVBoxLayout.addWidget(pwBottomArea)

        # Hide the bottom frame for now. Later this might be used for the
        # sequence editor.
        pwBottomArea.hide()

        #This widget implementation is subject to heavy revision. The purpose
        #is to implement a NFR that Mark urgently needs : The NFR is: Need a
        #way to quickly find a node in the MT by entering its name.
        #-- Ninad 2008-11-06
        self.pwSpecialDockWidgetInLeftChannel = SelectNodeByNameDockWidget(
            self.glpane.win)
        leftChannelVBoxLayout.addWidget(self.pwSpecialDockWidgetInLeftChannel)

        # See the resizeEvent() docstring for more information about
        # resizeTimer.
        self.resizeTimer = QTimer(self)
        self.resizeTimer.setSingleShot(True)
        return
コード例 #33
0
class Preferences(QMainWindow):

    run_wizard_requested = pyqtSignal()

    def __init__(self, gui, initial_plugin=None, close_after_initial=False):
        QMainWindow.__init__(self, gui)
        self.gui = gui
        self.must_restart = False
        self.committed = False
        self.close_after_initial = close_after_initial

        self.resize(930, 720)
        nh, nw = min_available_height()-25, available_width()-10
        if nh < 0:
            nh = 800
        if nw < 0:
            nw = 600
        nh = min(self.height(), nh)
        nw = min(self.width(), nw)
        self.resize(nw, nh)
        self.esc_action = QAction(self)
        self.addAction(self.esc_action)
        self.esc_action.setShortcut(QKeySequence(Qt.Key_Escape))
        self.esc_action.triggered.connect(self.esc)

        geom = gprefs.get('preferences_window_geometry', None)
        if geom is not None:
            self.restoreGeometry(geom)

        # Center
        if islinux:
            self.move(gui.rect().center() - self.rect().center())

        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.setWindowIcon(QIcon(I('config.png')))

        self.status_bar = StatusBar(self)
        self.setStatusBar(self.status_bar)

        self.stack = QStackedWidget(self)
        self.cw = QWidget(self)
        self.cw.setLayout(QVBoxLayout())
        self.cw.layout().addWidget(self.stack)
        self.bb = QDialogButtonBox(QDialogButtonBox.Close)
        self.wizard_button = self.bb.addButton(_('Run welcome wizard'),
                self.bb.ActionRole)
        self.wizard_button.setIcon(QIcon(I('wizard.png')))
        self.wizard_button.clicked.connect(self.run_wizard,
                type=Qt.QueuedConnection)
        self.cw.layout().addWidget(self.bb)
        self.bb.button(self.bb.Close).setDefault(True)
        self.bb.rejected.connect(self.close, type=Qt.QueuedConnection)
        self.setCentralWidget(self.cw)
        self.browser = Browser(self)
        self.browser.show_plugin.connect(self.show_plugin)
        self.stack.addWidget(self.browser)
        self.scroll_area = QScrollArea(self)
        self.stack.addWidget(self.scroll_area)
        self.scroll_area.setWidgetResizable(True)

        self.bar = QToolBar(self)
        self.addToolBar(self.bar)
        self.bar.setVisible(False)
        self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        self.bar.setMovable(False)
        self.bar.setFloatable(False)
        self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.apply_action = self.bar.addAction(QIcon(I('ok.png')), _('&Apply'),
                self.commit)
        self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
                _('&Cancel'),                self.cancel)
        self.bar_title = BarTitle(self.bar)
        self.bar.addWidget(self.bar_title)
        self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
                _('Restore &defaults'), self.restore_defaults)
        for ac, tt in [('apply', _('Save changes')),
                ('cancel', _('Cancel and return to overview'))]:
            ac = getattr(self, ac+'_action')
            ac.setToolTip(tt)
            ac.setWhatsThis(tt)
            ac.setStatusTip(tt)

        for ch in self.bar.children():
            if isinstance(ch, QToolButton):
                ch.setCursor(Qt.PointingHandCursor)
                ch.setAutoRaise(True)

        self.stack.setCurrentIndex(0)

        if initial_plugin is not None:
            category, name = initial_plugin
            plugin = get_plugin(category, name)
            if plugin is not None:
                self.show_plugin(plugin)

    def run_wizard(self):
        self.close()
        self.run_wizard_requested.emit()

    def set_tooltips_for_labels(self):

        def process_child(child):
            for g in child.children():
                if isinstance(g, QLabel):
                    buddy = g.buddy()
                    if buddy is not None and hasattr(buddy, 'toolTip'):
                        htext = unicode(buddy.toolTip()).strip()
                        etext = unicode(g.toolTip()).strip()
                        if htext and not etext:
                            g.setToolTip(htext)
                            g.setWhatsThis(htext)
                else:
                    process_child(g)

        process_child(self.showing_widget)

    def show_plugin(self, plugin):
        self.showing_widget = plugin.create_widget(self.scroll_area)
        self.showing_widget.genesis(self.gui)
        self.showing_widget.initialize()
        self.set_tooltips_for_labels()
        self.scroll_area.setWidget(self.showing_widget)
        self.stack.setCurrentIndex(1)
        self.showing_widget.show()
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences') + ' - ' +
                plugin.gui_name)
        self.apply_action.setEnabled(False)
        self.showing_widget.changed_signal.connect(lambda :
                self.apply_action.setEnabled(True))
        self.restore_action.setEnabled(self.showing_widget.supports_restoring_to_defaults)
        tt = self.showing_widget.restore_defaults_desc
        if not self.restore_action.isEnabled():
            tt = _('Restoring to defaults not supported for') + ' ' + \
                plugin.gui_name
        self.restore_action.setToolTip(textwrap.fill(tt))
        self.restore_action.setWhatsThis(textwrap.fill(tt))
        self.restore_action.setStatusTip(tt)
        self.bar_title.show_plugin(plugin)
        self.setWindowIcon(QIcon(plugin.icon))
        self.bar.setVisible(True)
        self.bb.setVisible(False)


    def hide_plugin(self):
        self.showing_widget = QWidget(self.scroll_area)
        self.scroll_area.setWidget(self.showing_widget)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.bar.setVisible(False)
        self.stack.setCurrentIndex(0)
        self.setWindowIcon(QIcon(I('config.png')))
        self.bb.setVisible(True)

    def esc(self, *args):
        if self.stack.currentIndex() == 1:
            self.cancel()
        elif self.stack.currentIndex() == 0:
            self.close()

    def commit(self, *args):
        try:
            must_restart = self.showing_widget.commit()
        except AbortCommit:
            return
        rc = self.showing_widget.restart_critical
        self.committed = True
        do_restart = False
        if must_restart:
            self.must_restart = True
            msg = _('Some of the changes you made require a restart.'
                    ' Please restart calibre as soon as possible.')
            if rc:
                msg = _('The changes you have made require calibre be '
                        'restarted immediately. You will not be allowed to '
                        'set any more preferences, until you restart.')


            d = warning_dialog(self, _('Restart needed'), msg,
                    show_copy_button=False)
            b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole)
            b.setIcon(QIcon(I('lt.png')))
            d.do_restart = False
            def rf():
                d.do_restart = True
            b.clicked.connect(rf)
            d.set_details('')
            d.exec_()
            b.clicked.disconnect()
            do_restart = d.do_restart
        self.showing_widget.refresh_gui(self.gui)
        self.hide_plugin()
        if self.close_after_initial or (must_restart and rc) or do_restart:
            self.close()
        if do_restart:
            self.gui.quit(restart=True)


    def cancel(self, *args):
        if self.close_after_initial:
            self.close()
        else:
            self.hide_plugin()

    def restore_defaults(self, *args):
        self.showing_widget.restore_defaults()

    def closeEvent(self, *args):
        gprefs.set('preferences_window_geometry',
                bytearray(self.saveGeometry()))
        if self.committed:
            self.gui.must_restart_before_config = self.must_restart
            self.gui.tags_view.recount()
            self.gui.create_device_menu()
            self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
            self.gui.bars_manager.apply_settings()
            self.gui.bars_manager.update_bars()
            self.gui.build_context_menus()

        return QMainWindow.closeEvent(self, *args)
コード例 #34
0
class PluginObject(object):

   tabName = 'Exchange Rates'
   maxVersion = '0.92'
   
   #############################################################################
   def __init__(self, main):

      self.main = main

      ##########################################################################
      ##### Display the conversion values based on the Coinbase API
      self.lastPriceFetch = 0
      self.lblHeader    = QRichLabel(tr("""<b>Tracking buy and sell prices on 
         Coinbase every 60 seconds</b>"""), doWrap=False)
      self.lblLastTime  = QRichLabel('', doWrap=False)
      self.lblSellLabel = QRichLabel(tr('Coinbase <b>Sell</b> Price (USD):'), doWrap=False)
      self.lblBuyLabel  = QRichLabel(tr('Coinbase <b>Buy</b> Price (USD):'),  doWrap=False)
      self.lblSellPrice = QRichLabel('<Not Available>')
      self.lblBuyPrice  = QRichLabel('<Not Available>')

      self.lastSellStr = ''
      self.lastBuyStr = ''

      self.btnUpdate = QPushButton(tr('Check Now'))
      self.main.connect(self.btnUpdate, SIGNAL('clicked()'), self.checkUpdatePrice)

      ##########################################################################
      ##### A calculator for converting prices between USD and BTC
      lblCalcTitle = QRichLabel(tr("""Convert between USD and BTC using 
         Coinbase sell price"""), hAlign=Qt.AlignHCenter, doWrap=False)
      self.edtEnterUSD = QLineEdit()
      self.edtEnterBTC = QLineEdit()
      self.lblEnterUSD1 = QRichLabel('$')
      self.lblEnterUSD2 = QRichLabel('USD')
      self.lblEnterBTC = QRichLabel('BTC')
      btnClear = QPushButton('Clear')

      self.main.connect(self.edtEnterUSD, SIGNAL('textEdited(QString)'), self.updateCalcBTC)
      self.main.connect(self.edtEnterBTC, SIGNAL('textEdited(QString)'), self.updateCalcUSD)

      def clearCalc():
         self.edtEnterUSD.setText('')
         self.edtEnterBTC.setText('')

      self.main.connect(btnClear, SIGNAL('clicked()'), clearCalc)

      frmCalcMid = makeHorizFrame( [self.lblEnterUSD1,
                                    self.edtEnterUSD,
                                    self.lblEnterUSD2,
                                    'Stretch',
                                    self.edtEnterBTC,
                                    self.lblEnterBTC])

      frmCalcClear = makeHorizFrame(['Stretch', btnClear, 'Stretch'])
      frmCalc = makeVertFrame([lblCalcTitle, frmCalcMid, frmCalcClear], STYLE_PLAIN)

      ##########################################################################
      ##### A table showing you the total balance of each wallet in USD and BTC
      lblWltTableTitle = QRichLabel(tr("Wallet balances converted to USD"), 
                                            doWrap=False, hAlign=Qt.AlignHCenter)
      numWallets = len(self.main.walletMap)
      self.wltTable = QTableWidget(self.main)
      self.wltTable.setRowCount(numWallets)
      self.wltTable.setColumnCount(4)
      self.wltTable.horizontalHeader().setStretchLastSection(True)
      self.wltTable.setMinimumWidth(600)


      ##########################################################################
      ##### Setup the main layout for the tab
      mainLayout = QGridLayout()
      i=0
      mainLayout.addWidget(self.lblHeader,      i,0,  1,3)
      i+=1
      mainLayout.addItem(QSpacerItem(15,15),    i,0)
      mainLayout.addWidget(self.lblSellLabel,   i,1)
      mainLayout.addWidget(self.lblSellPrice,   i,2)
      i+=1
      mainLayout.addItem(QSpacerItem(15,15),    i,0)
      mainLayout.addWidget(self.lblBuyLabel,    i,1)
      mainLayout.addWidget(self.lblBuyPrice,    i,2)
      i+=1
      mainLayout.addWidget(self.lblLastTime,    i,0,  1,2)
      mainLayout.addWidget(self.btnUpdate,      i,2)
      i+=1
      mainLayout.addItem(QSpacerItem(20,20),    i,0)
      i+=1
      mainLayout.addWidget(frmCalc,             i,0,  1,3)
      i+=1
      mainLayout.addItem(QSpacerItem(30,30),    i,0)
      i+=1
      mainLayout.addWidget(lblWltTableTitle,    i,0,  1,3)
      i+=1
      mainLayout.addWidget(self.wltTable,       i,0,  1,3)

      mainLayout.setColumnStretch(0,0)
      mainLayout.setColumnStretch(1,1)
      mainLayout.setColumnStretch(2,1)
      tabWidget = QWidget()
      tabWidget.setLayout(mainLayout)

      frmH = makeHorizFrame(['Stretch', tabWidget, 'Stretch'])
      frm  = makeVertFrame(['Space(20)', frmH, 'Stretch'])


      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(frm)


   #############################################################################
   def getTabToDisplay(self):
      return self.tabToDisplay


   #############################################################################
   def addCommasToPrice(self, pstr):
      dispStr = pstr.strip().split('.')[0] 
      dispStr = ','.join([dispStr[::-1][3*i:3*(i+1)][::-1] \
                            for i in range((len(dispStr)-1)/3+1)][::-1])
      if '.' in pstr:
         dispStr = dispStr + '.' + pstr.split('.')[1]
      return dispStr


   #############################################################################
   def fetchFormattedPrice(self, url):
      sock = urllib2.urlopen(url)
      value = ast.literal_eval(sock.read())['subtotal']['amount']
      return self.addCommasToPrice(value)



   #############################################################################
   def checkUpdatePrice(self):

      urlBase = 'http://coinbase.com/api/v1/prices/'
      urlSell = urlBase + 'sell'
      urlBuy  = urlBase + 'buy'

      try:
         self.lastSellStr = self.fetchFormattedPrice(urlSell)
         self.lastBuyStr  = self.fetchFormattedPrice(urlBuy)
         
         self.lblSellPrice.setText('<b><font color="%s">$%s</font> / BTC</b>' % \
                                           (htmlColor('TextBlue'), self.lastSellStr))
         self.lblBuyPrice.setText( '<b><font color="%s">$%s</font> / BTC</b>' % \
                                           (htmlColor('TextBlue'), self.lastBuyStr))
      
         self.lastPriceFetch = RightNow()

         self.updateLastTimeStr()
         self.updateWalletTable()
         self.updateCalcUSD(self.edtEnterBTC.text())
      except:
         #LOGEXCEPT('Failed to fetch price data from %s' % urlBase)
         pass

   

   #############################################################################
   def updateLastTimeStr(self):
      secs = RightNow() - self.lastPriceFetch
      tstr = 'Less than 1 min'
      if secs > 60:
         tstr = secondsToHumanTime(secs)

      self.lblLastTime.setText(tr("""<font color="%s">Last updated:  
         %s ago</font>""") % (htmlColor('DisableFG'), tstr))

   #############################################################################
   def injectGoOnlineFunc(self, topBlock):
      self.checkUpdatePrice()

   #############################################################################
   def injectHeartbeatAlwaysFunc(self):
      # Check the price every 60 seconds, update widgets
      self.updateLastTimeStr()
      if RightNow() < self.lastPriceFetch+60:
         return

      self.lastPriceFetch = RightNow()
      self.checkUpdatePrice() 


   #############################################################################
   def updateCalcUSD(self, newBTCVal):
      try:
         convertVal = float(self.lastSellStr.replace(',',''))
         usdVal = convertVal * float(newBTCVal.replace(',',''))
         self.edtEnterUSD.setText(self.addCommasToPrice('%0.2f' % usdVal))
      except:
         self.edtEnterUSD.setText('')
         
   #############################################################################
   def updateCalcBTC(self, newUSDVal):
      try:
         convertVal = float(self.lastSellStr.replace(',',''))
         btcVal = float(newUSDVal.replace(',','')) / convertVal
         self.edtEnterBTC.setText(self.addCommasToPrice('%0.8f' % btcVal))
      except:
         self.edtEnterBTC.setText('')
      
      
   #############################################################################
   def updateWalletTable(self):
      numWallets = len(self.main.walletMap)
      self.wltTable.setRowCount(numWallets)
      self.wltTable.setColumnCount(4)

      row = 0
      for wltID,wltObj in self.main.walletMap.iteritems():
         wltValueBTC = '(...)'
         wltValueUSD = '(...)'
         if TheBDM.getBDMState()=='BlockchainReady':
            convertVal = float(self.lastSellStr.replace(',',''))
            wltBal = wltObj.getBalance('Total')
            wltValueBTC = coin2str(wltBal, maxZeros=2)
            wltValueUSD = '$' + self.addCommasToPrice('%0.2f' % (wltBal*convertVal/1e8))

         rowItems = []
         rowItems.append(QTableWidgetItem(wltID))
         rowItems.append(QTableWidgetItem(wltObj.labelName))
         rowItems.append(QTableWidgetItem(wltValueBTC))
         rowItems.append(QTableWidgetItem(wltValueUSD))

         rowItems[-2].setTextAlignment(Qt.AlignRight)
         rowItems[-1].setTextAlignment(Qt.AlignRight)
         rowItems[-2].setFont(GETFONT('Fixed', 10))
         rowItems[-1].setFont(GETFONT('Fixed', 10))

         for i,item in enumerate(rowItems):
            self.wltTable.setItem(row, i, item)
            item.setFlags(Qt.NoItemFlags)

         self.wltTable.setHorizontalHeaderItem(0, QTableWidgetItem(tr('Wallet ID')))
         self.wltTable.setHorizontalHeaderItem(1, QTableWidgetItem(tr('Wallet Name')))
         self.wltTable.setHorizontalHeaderItem(2, QTableWidgetItem(tr('BTC Balance')))
         self.wltTable.setHorizontalHeaderItem(3, QTableWidgetItem(tr('USD ($) Value')))
         self.wltTable.verticalHeader().hide()

         row += 1
コード例 #35
0
ファイル: diff.py プロジェクト: sss/calibre
    def __init__(self,
                 ids,
                 get_metadata,
                 field_metadata,
                 parent=None,
                 window_title=None,
                 reject_button_tooltip=None,
                 accept_all_tooltip=None,
                 reject_all_tooltip=None,
                 revert_tooltip=None,
                 intro_msg=None,
                 action_button=None,
                 **kwargs):
        QDialog.__init__(self, parent)
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        self.setWindowIcon(QIcon(I('auto_author_sort.png')))
        self.get_metadata = get_metadata
        self.ids = list(ids)
        self.total = len(self.ids)
        self.accepted = OrderedDict()
        self.window_title = window_title or _('Compare metadata')

        if intro_msg:
            self.la = la = QLabel(intro_msg)
            la.setWordWrap(True)
            l.addWidget(la)

        self.compare_widget = CompareSingle(field_metadata,
                                            parent=parent,
                                            revert_tooltip=revert_tooltip,
                                            **kwargs)
        self.sa = sa = QScrollArea()
        l.addWidget(sa)
        sa.setWidget(self.compare_widget)
        sa.setWidgetResizable(True)

        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Cancel)
        bb.rejected.connect(self.reject)
        if self.total > 1:
            self.aarb = b = bb.addButton(_('&Accept all remaining'),
                                         bb.YesRole)
            b.setIcon(QIcon(I('ok.png')))
            if accept_all_tooltip:
                b.setToolTip(accept_all_tooltip)
            b.clicked.connect(self.accept_all_remaining)
            self.rarb = b = bb.addButton(_('Re&ject all remaining'), bb.NoRole)
            b.setIcon(QIcon(I('minus.png')))
            if reject_all_tooltip:
                b.setToolTip(reject_all_tooltip)
            b.clicked.connect(self.reject_all_remaining)
            self.sb = b = bb.addButton(_('&Reject'), bb.ActionRole)
            b.clicked.connect(partial(self.next_item, False))
            b.setIcon(QIcon(I('minus.png')))
            if reject_button_tooltip:
                b.setToolTip(reject_button_tooltip)
        if action_button is not None:
            self.acb = b = bb.addButton(action_button[0], bb.ActionRole)
            b.setIcon(QIcon(action_button[1]))
            self.action_button_action = action_button[2]
            b.clicked.connect(self.action_button_clicked)
        self.nb = b = bb.addButton(
            _('&Next') if self.total > 1 else _('&OK'), bb.ActionRole)
        b.setIcon(QIcon(I('forward.png' if self.total > 1 else 'ok.png')))
        b.clicked.connect(partial(self.next_item, True))
        b.setDefault(True)
        l.addWidget(bb)

        self.next_item(True)

        desktop = QApplication.instance().desktop()
        geom = desktop.availableGeometry(parent or self)
        width = max(700, min(950, geom.width() - 50))
        height = max(650, min(1000, geom.height() - 100))
        self.resize(QSize(width, height))
        geom = gprefs.get('diff_dialog_geom', None)
        if geom is not None:
            self.restoreGeometry(geom)
        b.setFocus(Qt.OtherFocusReason)
コード例 #36
0
class Ui_PartWindow(QWidget):
    """
    The Ui_PartWindow class provides a Part Window UI object composed of three
    primary areas:

    - The "left area" contains the Project TabWidget which contains
    the Model Tree and Property Manager (tabs). Other tabs (widgets)
    can be added as needed.

    - The "right area" contains the Graphics Area (i.e. glpane) displaying
    the current model.

    - The "bottom area" lives below the left and right areas, spanning
    the full width of the part window. It can be used whenever a landscape
    layout is needed (i.e. the Sequence Editor). Typically, this area is
    not used and is hidden by default.

    A "part window" splitter lives between the left and right areas that
    allow the user to resize the shared area occupied by them. There is no
    splitter between the top and bottom areas.

    This class supports and is limited to a B{Single Document Interface (SDI)}.
    In time, NE1 will migrate to and support a Multiple Document Interface (MDI)
    to allow multiple project documents (i.e. parts, assemblies,
    simulations, text files, graphs, tables, etc. documents) to be available
    within the common workspace of the NE1 main window.

    @see: U{B{NE1 Main Window Framework}
    <http://www.nanoengineer-1.net/mediawiki/index.php?title=NE1_Main_Window_Framework>}
    """
    widgets = [] # For debugging purposes.
    splitterPosition = PM_DEFAULT_WIDTH
    _previous_splitterPosition = PM_DEFAULT_WIDTH
        # Used for restoring the splitter position when collapsing/expanding
        # the left area.

    def __init__(self, assy, parent):
        """
        Constructor for the part window.

        @param assy: The assembly (part)
        @type  assy: Assembly

        @param parent: The parent widget.
        @type  parent: U{B{QMainWindow}
                       <http://doc.trolltech.com/4/qmainwindow.html>}
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.assy = assy
            # note: to support MDI, self.assy would probably need to be a
            # different assembly for each PartWindow.
            # [bruce 080216 comment]
        self.setWindowIcon(geticon("ui/border/Part.png"))
        self.updateWindowTitle()
        
        # The main layout for the part window is a VBoxLayout <pwVBoxLayout>.
        self.pwVBoxLayout = QVBoxLayout(self)
        pwVBoxLayout = self.pwVBoxLayout
        pwVBoxLayout.setMargin(0)
        pwVBoxLayout.setSpacing(0)

        # ################################################################
        # <pwSplitter> is the horizontal splitter b/w the
        # pwLeftArea (mt and pm) and the glpane.
        self.pwSplitter = QSplitter(Qt.Horizontal)
        pwSplitter = self.pwSplitter
        pwSplitter.setObjectName("pwSplitter")
        pwSplitter.setHandleWidth(3) # 3 pixels wide.
        pwVBoxLayout.addWidget(pwSplitter)

        # ##################################################################
        # <pwLeftArea> is the container holding the pwProjectTabWidget.
        # Note: Making pwLeftArea (and pwRightArea and pwBottomArea) QFrame
        # widgets has the benefit of making it easy to draw a border around
        # each area. One purpose of this would be to help developers understand
        # (visually) how the part window is laid out. I intend to add a debug
        # pref to draw part window area borders and add "What's This" text to
        # them. Mark 2008-01-05.
        self.pwLeftArea = LeftFrame(self)
        pwLeftArea = self.pwLeftArea
        pwLeftArea.setObjectName("pwLeftArea")
        pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)

        # Setting the frame style like this is nice since it clearly
        # defines the splitter at the top-left corner.
        pwLeftArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        # This layout will contain splitter (above) and the pwBottomArea.
        leftChannelVBoxLayout = QVBoxLayout(pwLeftArea)
        leftChannelVBoxLayout.setMargin(0)
        leftChannelVBoxLayout.setSpacing(0)

        pwSplitter.addWidget(pwLeftArea)

        # Makes it so pwLeftArea is not collapsible.
        pwSplitter.setCollapsible (0, False)

        # ##################################################################
        # <pwProjectTabWidget> is a QTabWidget that contains the MT and PM
        # widgets. It lives in the "left area" of the part window.
        self.pwProjectTabWidget = _pwProjectTabWidget()
           # _pwProjectTabWidget subclasses QTabWidget
           # Note [bruce 070829]: to fix bug 2522 I need to intercept
           # self.pwProjectTabWidget.removeTab, so I made it a subclass of
           # QTabWidget. It needs to know the GLPane, but that's not created
           # yet, so we set it later using KLUGE_setGLPane (below).
           # Note: No parent supplied. Could this be the source of the
           # minor vsplitter resizing problem I was trying to resolve a few
           # months ago?  Try supplying a parent later. Mark 2008-01-01
        self.pwProjectTabWidget.setObjectName("pwProjectTabWidget")
        self.pwProjectTabWidget.setCurrentIndex(0)
        self.pwProjectTabWidget.setAutoFillBackground(True)

        # Create the model tree "tab" widget. It will contain the MT GUI widget.
        # Set the tab icon, too.
        self.modelTreeTab = QWidget()
        self.modelTreeTab.setObjectName("modelTreeTab")
        self.pwProjectTabWidget.addTab(
            self.modelTreeTab,
            geticon("ui/modeltree/Model_Tree.png"),
            "")

        modelTreeTabLayout = QVBoxLayout(self.modelTreeTab)
        modelTreeTabLayout.setMargin(0)
        modelTreeTabLayout.setSpacing(0)

        # Create the model tree (GUI) and add it to the tab layout.
        self.modelTree = ModelTree(self.modelTreeTab, parent)
        self.modelTree.modelTreeGui.setObjectName("modelTreeGui")
        modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui)

        # Create the property manager "tab" widget. It will contain the PropMgr
        # scroll area, which will contain the property manager and all its
        # widgets.
        self.propertyManagerTab = QWidget()
        self.propertyManagerTab.setObjectName("propertyManagerTab")

        self.propertyManagerScrollArea = QScrollArea(self.pwProjectTabWidget)
        self.propertyManagerScrollArea.setObjectName("propertyManagerScrollArea")
        self.propertyManagerScrollArea.setWidget(self.propertyManagerTab)
        self.propertyManagerScrollArea.setWidgetResizable(True)
        # Eureka!
        # setWidgetResizable(True) will resize the Property Manager (and its
        # contents) correctly when the scrollbar appears/disappears.
        # It even accounts correctly for collapsed/expanded groupboxes!
        # Mark 2007-05-29

        # Add the property manager scroll area as a "tabbed" widget.
        # Set the tab icon, too.
        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"),
            "")

        # Finally, add the "pwProjectTabWidget" to the left channel layout.
        
        leftChannelVBoxLayout.addWidget(self.pwProjectTabWidget)
       

        # Create the glpane and make it a child of the part splitter.
        self.glpane = GLPane(assy, self, 'glpane name', parent)
            # note: our owner (MWsemantics) assumes
            # there is just this one GLPane for assy, and stores it
            # into assy as assy.o and assy.glpane. [bruce 080216 comment]
        
        # Add what's this text to self.glpane.
        # [bruce 080912 moved this here from part of a method in class GLPane.
        #  In this code's old location, Mark wrote [2007-06-01]: "Problem -
        #  I don't believe this text is processed by fix_whatsthis_text_and_links()
        #  in whatsthis_utilities.py." Now that this code is here, I don't know
        #  whether that's still true. ]
        from ne1_ui.WhatsThisText_for_MainWindow import whats_this_text_for_glpane
        self.glpane.setWhatsThis( whats_this_text_for_glpane() )
        
        # update [re the above comment], bruce 081209:
        # I added the following explicit call of fix_whatsthis_text_and_links,
        # but it doesn't work to replace Ctrl with Cmd on Mac;
        # see today's comment in fix_whatsthis_text_and_links for likely reason.
        # So I will leave this here, but also leave in place the kluges
        # in whats_this_text_for_glpane to do that replacement itself.
        # The wiki help link in this whatsthis text doesn't work,
        # but I guess that is an independent issue, related to lack
        # of use of class QToolBar_WikiHelp or similar code, for GLPane
        # or this class or the main window class.
        from foundation.whatsthis_utilities import fix_whatsthis_text_and_links
        fix_whatsthis_text_and_links(self.glpane) # doesn't yet work
        
        self.pwProjectTabWidget.KLUGE_setGLPane(self.glpane)
            # help fix bug 2522 [bruce 070829]
        qt4warnDestruction(self.glpane, 'GLPane of PartWindow')
        pwSplitter.addWidget(self.glpane)

        # ##################################################################
        # <pwBottomArea> is a container at the bottom of the part window
        # spanning its entire width. It is intended to be used as an extra
        # area for use by Property Managers (or anything else) that needs
        # a landscape oriented layout.
        # An example is the Sequence Editor, which is part of the
        # Strand Properties PM.
        self.pwBottomArea = QFrame()
            # IMHO, self is not a good parent. Mark 2008-01-04.
        pwBottomArea = self.pwBottomArea
        pwBottomArea.setObjectName("pwBottomArea")
        pwBottomArea.setMaximumHeight(50)

        # Add a frame border to see what it looks like.
        pwBottomArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        self.pwVBoxLayout.addWidget(pwBottomArea)

        # Hide the bottom frame for now. Later this might be used for the
        # sequence editor.
        pwBottomArea.hide()
        
        #This widget implementation is subject to heavy revision. The purpose
        #is to implement a NFR that Mark urgently needs : The NFR is: Need a 
        #way to quickly find a node in the MT by entering its name.
        #-- Ninad 2008-11-06
        self.pwSpecialDockWidgetInLeftChannel = SelectNodeByNameDockWidget(self.glpane.win)
        leftChannelVBoxLayout.addWidget(self.pwSpecialDockWidgetInLeftChannel)
                
        # See the resizeEvent() docstring for more information about
        # resizeTimer.
        self.resizeTimer = QTimer(self)
        self.resizeTimer.setSingleShot(True)
        return
    
    def getLeftChannelDockWidget(self):
        return self.pwSpecialDockWidgetInLeftChannel

    def updateWindowTitle(self, changed = False):
        #by mark; bruce 050810 revised this in several ways, fixed bug 785
        """
        Update the window title (caption) at the top of the part window.
        Example:  "partname.mmp"

        This implements the standard way most applications indicate that a
        document has unsaved changes. On Mac OS X the close button will have
        a modified look; on other platforms the window title will have
        an '*' (asterisk).

        @note: We'll want to experiment with this to make sure it

        @param changed: If True, the document has unsaved changes.
        @type  changed: boolean

        @see: U{B{windowTitle}<http://doc.trolltech.com/4/qwidget.html#windowTitle-prop>},
              U{B{windowModified}<http://doc.trolltech.com/4/qwidget.html#windowModified-prop>}
        """
        # WARNING: there is mostly-duplicated code in this method and in
        # MWsemantics.update_mainwindow_caption. See the comment in that
        # method for more info and todos. [bruce 081227 comment]
        caption_fullpath = env.prefs[captionFullPath_prefs_key]

        partname = "Untitled" # fallback value if no file yet
        if self.assy.filename: #bruce 081227 cleanup: try -> if, etc
            # self.assy.filename is always an empty string, even after a
            # file has been opened with a complete name. Need to ask Bruce
            # about this problem, resulting in a bug (i.e. the window title
            # is always "Untitled". Mark 2008-01-02.
            junk, basename = os.path.split(self.assy.filename)
            if basename:
                if caption_fullpath:
                    partname = os.path.normpath(self.assy.filename)
                        #fixed bug 453-1 ninad060721
                else:
                    partname = basename

        # WARNING: the following code differs in the two versions
        # of this routine.
        # The "[*]" placeholder below is modified or removed by Qt; see:
        # http://doc.trolltech.com/4/qwidget.html#windowModified-prop
        self.setWindowTitle(self.trUtf8(partname + '[*]'))
        self.setWindowModified(changed) # replaces '[*]' by '' or '*'
        return

    def collapseLeftArea(self, hideLeftArea = True):
        """
        Make the left area collapsible (via the splitter). The left area
        will be hidden (collapsed,actually) if I{hideLeftArea} is True 
        (the default).
        """
        self._previous_splitterPosition = self.pwLeftArea.width()
        if hideLeftArea:
            self.pwSplitter.setCollapsible(0, True)
            self.setSplitterPosition(pos = 0)
        return

    def expandLeftArea(self):
        """
        Expand the left area.

        @see: L{MWsemantics._showFullScreenCommonCode()} for an example
        showing how it is used.
        """
        self.setSplitterPosition(pos = self._previous_splitterPosition)
        self.pwSplitter.setCollapsible(0, False)
        self.pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        self.pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)
        return

    def updatePropertyManagerTab(self, tab): #Ninad 061207
        "Update the Properties Manager tab with 'tab' "

        self.parent.glpane.gl_update_confcorner()
            #bruce 070627, since PM affects confcorner appearance

        if self.propertyManagerScrollArea.widget():
            # The following is necessary to get rid of those C object
            # deleted errors (and the resulting bugs)
            lastwidgetobject = self.propertyManagerScrollArea.takeWidget()
            if lastwidgetobject:
                # bruce 071018 revised this code; see my comment on same
                # code in PM_Dialog
                try:
                    lastwidgetobject.update_props_if_needed_before_closing
                except AttributeError:
                    if 1 or debug_flags.atom_debug:
                        msg1 = "Last PropMgr %r doesn't have method" % lastwidgetobject
                        msg2 =" update_props_if_needed_before_closing. That's"
                        msg3 = " OK (for now, only implemented for Plane PM). "
                        msg4 = "Ignoring Exception: "
                        print_compact_traceback(msg1 + msg2 + msg3 + msg4)
                else:
                    lastwidgetobject.update_props_if_needed_before_closing()

            lastwidgetobject.hide()
            # @ ninad 061212 perhaps hiding the widget is not needed

        self.pwProjectTabWidget.removeTab(
            self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea))

        # Set the PropertyManager tab scroll area to the appropriate widget.
        self.propertyManagerScrollArea.setWidget(tab)

        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"),
            "")

        self.pwProjectTabWidget.setCurrentIndex(
            self.pwProjectTabWidget.indexOf(self.propertyManagerScrollArea))
        return

    def KLUGE_current_PropertyManager(self):
        #bruce 070627; revised 070829 as part of fixing bug 2523
        """
        Return the current Property Manager widget (whether or not its tab is
        chosen, but only if it has a tab), or None if there is not one.

        @warning: This method's existence (not only its implementation)
        is a kluge, since the right way to access that would be by asking
        the "command sequencer";
        but that's not yet implemented, so this is the best we can do for now.
        Also, it would be better to get the top command and talk to it, not
        its PM (a QWidget). Also, whatever calls this will be making
        assumptions about that PM which are really only the command's business.
        So in short, every call of this is in need of cleanup once we have a
        working "command sequencer". (That's true of many things related to
        PMs, not only this method.)

        @warning: The return values are (presumably) widgets, but they can
        also be mode objects and generator objects, due to excessive use of
        multiple inheritance in the current PM code. So be careful what you
        do with them -- they might have lots of extra methods/attrs,
        and setting your own attrs in them might mess things up.
        """
        res = self.propertyManagerScrollArea.widget()
        if not hasattr(res, 'done_btn'):
            # not sure what widget this is otherwise, but it is a widget
            # (not None) for the default mode, at least on startup, so just
            # return None in this case
            return None
        # Sometimes this PM remains present from a prior command, even when
        # there is no longer a tab for the PM. As part of fixing bug 2523
        # we have to avoid returning it in that case. How we do that is a kluge,
        # but hopefully this entire kluge function can be dispensed with soon.
        # This change also fixes bug 2522 on the Mac (but not on Windows --
        # for that, we needed to intercept removeTab in separate code above).
        index = self.pwProjectTabWidget.indexOf(
            self.propertyManagerScrollArea)
        if index == -1:
            return None
        # Due to bugs in other code, sometimes the PM tab is left in place,
        # though the PM itself is hidden. To avoid finding the PM in that case,
        # also check whether it's hidden. This will fix the CC part of a new bug
        # just reported by Keith in email (when hitting Ok in DNA Gen).
        if res.isHidden(): # probably a QWidget method [bruce 080205 comment]
            return None
        return res

    def dismiss(self):
        self.parent.removePartWindow(self)
        return
    
    def setSplitterPosition(self, pos = PM_DEFAULT_WIDTH, setDefault = True): 
        """
        Set the position of the splitter between the left area and graphics area
        so that the width of the container holding the model tree (and 
        property manager) is I{pos} pixels wide.
        
        @param pos: The splitter position (in pixel units).
        @type  pos: int
        
        @param setDefault: If True (the default), I{pos} becomes the new default
                           position.
        @type  setDefault: boolean
        """
        self.pwSplitter.moveSplitter(pos, 1)
        if _DEBUG:
            print "New Splitter Position: %d (setDefault=%d)" \
                  % (pos, setDefault)
        if setDefault:
            self.splitterPosition = pos
        return
    
    def resizeEvent(self, event):
        """
        This reimplementation of QWidget.resizeEvent is here to deal with the
        undesired behavior of the splitter while resizing the part window.
        Normally, the splitter will drift back and forth while resizing
        the part window. This forces the splitter to stay fixed during
        resize operations.
        """
        # When self.resizeTimer.isActive() = True, the partwindow is being
        # resized. This is checked by the resizeEvent handler in LeftFrame
        # to determine if the splitter is being moved by the user or 
        # programmably by self's resizeEvent.
        if self.resizeTimer.isActive():
            self.resizeTimer.stop() # Stop the timer.
        self.resizeTimer.start( 500 )  # (Re)strand a .5 second singleshot timer.
        self.setSplitterPosition(self.splitterPosition, setDefault = False)
        QWidget.resizeEvent(self, event)
        return
コード例 #37
0
ファイル: single.py プロジェクト: siebert/calibre
    def do_layout(self):
        self.central_widget.clear()
        self.tabs = []
        self.labels = []
        sto = QWidget.setTabOrder

        self.on_drag_enter.connect(self.handle_drag_enter)
        self.tabs.append(DragTrackingWidget(self, self.on_drag_enter))
        self.central_widget.addTab(self.tabs[0], _("&Metadata"))
        self.tabs[0].l = QGridLayout()
        self.tabs[0].setLayout(self.tabs[0].l)

        self.tabs.append(QWidget(self))
        self.central_widget.addTab(self.tabs[1], _("&Cover and formats"))
        self.tabs[1].l = QGridLayout()
        self.tabs[1].setLayout(self.tabs[1].l)

        # accept drop events so we can automatically switch to the second tab to
        # drop covers and formats
        self.tabs[0].setAcceptDrops(True)

        # Tab 0
        tab0 = self.tabs[0]

        tl = QGridLayout()
        gb = QGroupBox(_('&Basic metadata'), self.tabs[0])
        self.tabs[0].l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0,
                   self.title,
                   self.title_sort,
                   button=self.deduce_title_sort_button,
                   span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2,
                   self.authors,
                   self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2,
                   icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4,
                   self.series,
                   self.series_index,
                   button=self.clear_series_button,
                   icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7,
                   self.rating,
                   self.pubdate,
                   button=self.clear_ratings_button)
        create_row(8,
                   self.pubdate,
                   self.publisher,
                   button=self.pubdate.clear_button,
                   icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11,
                   self.timestamp,
                   self.identifiers,
                   button=self.timestamp.clear_button,
                   icon='trash.png')
        create_row(12,
                   self.identifiers,
                   self.comments,
                   button=self.clear_identifiers_button,
                   icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1, 1)

        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('C&ustom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(tab0)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            self.tabs[0].l.addWidget(gb, 0, 1, 1, 1)
            sto(self.identifiers, gb)

        w = QGroupBox(_('&Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        l = QHBoxLayout()
        w.setLayout(l)
        l.addWidget(self.comments)
        tab0.l.addWidget(w, 1, 0, 1, 2)

        # Tab 1
        tab1 = self.tabs[1]

        wsp = QWidget(tab1)
        wgl = QVBoxLayout()
        wsp.setLayout(wgl)

        # right-hand side of splitter
        gb = QGroupBox(_('Change cover'), tab1)
        l = QGridLayout()
        gb.setLayout(l)
        for i, b in enumerate(self.cover.buttons[:3]):
            l.addWidget(b, 0, i, 1, 1)
            sto(b, self.cover.buttons[i + 1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        l.addLayout(hl, 1, 0, 1, 3)
        wgl.addWidget(gb)
        wgl.addItem(
            QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Expanding))
        wgl.addItem(
            QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Expanding))
        wgl.addWidget(self.formats_manager)

        self.splitter = QSplitter(Qt.Horizontal, tab1)
        tab1.l.addWidget(self.splitter)
        self.splitter.addWidget(self.cover)
        self.splitter.addWidget(wsp)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(64, 64))
コード例 #38
0
ファイル: single.py プロジェクト: siebert/calibre
    def do_layout(self):
        self.central_widget.clear()
        self.labels = []
        sto = QWidget.setTabOrder

        self.central_widget.tabBar().setVisible(False)
        tab0 = QWidget(self)
        self.central_widget.addTab(tab0, _("&Metadata"))
        l = QGridLayout()
        tab0.setLayout(l)

        # Basic metadata in col 0
        tl = QGridLayout()
        gb = QGroupBox(_('Basic metadata'), tab0)
        l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0,
                   self.title,
                   self.title_sort,
                   button=self.deduce_title_sort_button,
                   span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2,
                   self.authors,
                   self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2,
                   icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4,
                   self.series,
                   self.series_index,
                   button=self.clear_series_button,
                   icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7,
                   self.rating,
                   self.pubdate,
                   button=self.clear_ratings_button)
        create_row(8,
                   self.pubdate,
                   self.publisher,
                   button=self.pubdate.clear_button,
                   icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11,
                   self.timestamp,
                   self.identifiers,
                   button=self.timestamp.clear_button,
                   icon='trash.png')
        create_row(12,
                   self.identifiers,
                   self.comments,
                   button=self.clear_identifiers_button,
                   icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1, 1)

        # Custom metadata in col 1
        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('Custom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(gb)
            sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            l.addWidget(gb, 0, 1, 1, 1)
            sp = QSizePolicy()
            sp.setVerticalStretch(10)
            sp.setHorizontalPolicy(QSizePolicy.Minimum)
            sp.setVerticalPolicy(QSizePolicy.Expanding)
            gb.setSizePolicy(sp)
            self.set_custom_metadata_tab_order()

        # comments span col 0 & 1
        w = QGroupBox(_('Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        lb = QHBoxLayout()
        w.setLayout(lb)
        lb.addWidget(self.comments)
        l.addWidget(w, 1, 0, 1, 2)

        # Cover & formats in col 3
        gb = QGroupBox(_('Cover'), tab0)
        lb = QGridLayout()
        gb.setLayout(lb)
        lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
        sto(self.manage_authors_button, self.cover.buttons[0])
        for i, b in enumerate(self.cover.buttons[:3]):
            lb.addWidget(b, 1, i, 1, 1)
            sto(b, self.cover.buttons[i + 1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        lb.addLayout(hl, 2, 0, 1, 3)
        l.addWidget(gb, 0, 2, 1, 1)
        l.addWidget(self.formats_manager, 1, 2, 1, 1)
        sto(self.cover.buttons[-1], self.formats_manager)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(32, 32))
コード例 #39
0
    def __init__(self, assy, parent):
        """
        Constructor for the part window.

        @param assy: The assembly (part)
        @type  assy: Assembly

        @param parent: The parent widget.
        @type  parent: U{B{QMainWindow}
                       <http://doc.trolltech.com/4/qmainwindow.html>}
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.assy = assy
            # note: to support MDI, self.assy would probably need to be a
            # different assembly for each PartWindow.
            # [bruce 080216 comment]
        self.setWindowIcon(geticon("ui/border/Part.png"))
        self.updateWindowTitle()

        # Used in expanding or collapsing the Model Tree/ PM area
        self._previous_pwLeftAreaWidth = PM_DEFAULT_WIDTH

        # The main layout for the part window is a VBoxLayout <pwVBoxLayout>.
        self.pwVBoxLayout = QVBoxLayout(self)
        pwVBoxLayout = self.pwVBoxLayout
        pwVBoxLayout.setMargin(0)
        pwVBoxLayout.setSpacing(0)

        # ################################################################
        # <pwSplitter> is the horizontal splitter b/w the
        # pwProjectTabWidget and the glpane.
        self.pwSplitter = QSplitter(Qt.Horizontal)
        pwSplitter = self.pwSplitter
        pwSplitter.setObjectName("pwSplitter")
        pwSplitter.setHandleWidth(3) # 3 pixels wide.
        pwVBoxLayout.addWidget(pwSplitter)

        # ##################################################################
        # <pwLeftArea> is the container holding the pwProjectTabWidget.
        # Note: Making pwLeftArea (and pwRightArea and pwBottomArea) QFrame
        # widgets has the benefit of making it easy to draw a border around
        # each area. One purpose of this would be to help developers understand
        # (visually) how the part window is laid out. I intend to add a debug
        # pref to draw part window area borders and add "What's This" text to
        # them. Mark 2008-01-05.
        self.pwLeftArea = QFrame()
        pwLeftArea = self.pwLeftArea
        pwLeftArea.setObjectName("pwLeftArea")
        pwLeftArea.setMinimumWidth(PM_MINIMUM_WIDTH)
        pwLeftArea.setMaximumWidth(PM_MAXIMUM_WIDTH)
        pwLeftArea.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy(QSizePolicy.Fixed),
                        QSizePolicy.Policy(QSizePolicy.Expanding)))

        # Setting the frame style like this is nice since it clearly
        # defines the splitter at the top-left corner.
        pwLeftArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        # This layout will contain splitter (above) and the pwBottomArea.
        leftChannelVBoxLayout = QVBoxLayout(pwLeftArea)
        leftChannelVBoxLayout.setMargin(0)
        leftChannelVBoxLayout.setSpacing(0)

        pwSplitter.addWidget(pwLeftArea)

        # Makes it so pwLeftArea is not collapsible.
        pwSplitter.setCollapsible (0, False)

        # ##################################################################
        # <pwProjectTabWidget> is a QTabWidget that contains the MT and PM
        # widgets. It lives in the "left area" of the part window.
        self.pwProjectTabWidget = _pwProjectTabWidget()
           # _pwProjectTabWidget subclasses QTabWidget
           # Note [bruce 070829]: to fix bug 2522 I need to intercept
           # self.pwProjectTabWidget.removeTab, so I made it a subclass of
           # QTabWidget. It needs to know the GLPane, but that's not created
           # yet, so we set it later using KLUGE_setGLPane (below).
           # Note: No parent supplied. Could this be the source of the
           # minor vsplitter resizing problem I was trying to resolve a few
           # months ago?  Try supplying a parent later. Mark 2008-01-01
        self.pwProjectTabWidget.setObjectName("pwProjectTabWidget")
        self.pwProjectTabWidget.setCurrentIndex(0)
        self.pwProjectTabWidget.setAutoFillBackground(True)

        # Create the model tree "tab" widget. It will contain the MT GUI widget.
        # Set the tab icon, too.
        self.modelTreeTab = QWidget()
        self.modelTreeTab.setObjectName("modelTreeTab")
        self.pwProjectTabWidget.addTab(
            self.modelTreeTab,
            geticon("ui/modeltree/Model_Tree.png"),
            "")

        modelTreeTabLayout = QVBoxLayout(self.modelTreeTab)
        modelTreeTabLayout.setMargin(0)
        modelTreeTabLayout.setSpacing(0)

        # Create the model tree (GUI) and add it to the tab layout.
        self.modelTree = modelTree(self.modelTreeTab, parent)
        self.modelTree.modelTreeGui.setObjectName("modelTreeGui")
        modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui)

        # Create the property manager "tab" widget. It will contain the PropMgr
        # scroll area, which will contain the property manager and all its
        # widgets.
        self.propertyManagerTab = QWidget()
        self.propertyManagerTab.setObjectName("propertyManagerTab")

        self.propertyManagerScrollArea = QScrollArea(self.pwProjectTabWidget)
        self.propertyManagerScrollArea.setObjectName("propertyManagerScrollArea")
        self.propertyManagerScrollArea.setWidget(self.propertyManagerTab)
        self.propertyManagerScrollArea.setWidgetResizable(True)
        # Eureka!
        # setWidgetResizable(True) will resize the Property Manager (and its
        # contents) correctly when the scrollbar appears/disappears.
        # It even accounts correctly for collapsed/expanded groupboxes!
        # Mark 2007-05-29

        # Add the property manager scroll area as a "tabbed" widget.
        # Set the tab icon, too.
        self.pwProjectTabWidget.addTab(
            self.propertyManagerScrollArea,
            geticon("ui/modeltree/Property_Manager.png"),
            "")

        # Finally, add the "pwProjectTabWidget" to the left channel layout.
        leftChannelVBoxLayout.addWidget(self.pwProjectTabWidget)

        # Create the glpane and make it a child of the part splitter.
        self.glpane = GLPane(assy, self, 'glpane name', parent)
            # note: our owner (MWsemantics) assumes
            # there is just this one GLPane for assy, and stores it
            # into assy as assy.o and assy.glpane. [bruce 080216 comment]
        self.pwProjectTabWidget.KLUGE_setGLPane(self.glpane)
            # help fix bug 2522 [bruce 070829]
        qt4warnDestruction(self.glpane, 'GLPane of PartWindow')
        pwSplitter.addWidget(self.glpane)

        # ##################################################################
        # <pwBottomArea> is a container at the bottom of the part window
        # spanning its entire width. It is intended to be used as an extra
        # area for use by Property Managers (or anything else) that needs
        # a landscape oriented layout.
        # An example is the Sequence Editor, which is part of the
        # Strand Properties PM.
        self.pwBottomArea = QFrame()
            # IMHO, self is not a good parent. Mark 2008-01-04.
        pwBottomArea = self.pwBottomArea
        pwBottomArea.setObjectName("pwBottomArea")
        pwBottomArea.setMaximumHeight(50)
        pwBottomArea.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy(QSizePolicy.Expanding),
                        QSizePolicy.Policy(QSizePolicy.Fixed)))

        # Add a frame border to see what it looks like.
        pwBottomArea.setFrameStyle( QFrame.Panel | QFrame.Sunken )

        self.pwVBoxLayout.addWidget(pwBottomArea)

        # Hide the bottom frame for now. Later this might be used for the
        # sequence editor.
        pwBottomArea.hide()
コード例 #40
0
    def __init__(self, fm, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(_('Create/edit a column coloring rule'))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(_('Create a coloring rule by'
            ' filling in the boxes below'))
        l.addWidget(l1, 0, 0, 1, 5)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 5)

        self.l2 = l2 = QLabel(_('Set the color of the column:'))
        l.addWidget(l2, 2, 0)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 1)

        self.l3 = l3 = QLabel(_('to'))
        l.addWidget(l3, 2, 2)

        self.color_box = QComboBox(self)
        self.color_label = QLabel('Sample text Sample text')
        self.color_label.setTextFormat(Qt.RichText)
        l.addWidget(self.color_box, 2, 3)
        l.addWidget(self.color_label, 2, 4)
        l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 5)

        self.l4 = l4 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l4, 3, 0, 1, 6)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 6)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 6)
        b.clicked.connect(self.add_blank_condition)

        self.l5 = l5 = QLabel(_('You can disable a condition by'
            ' blanking all of its boxes'))
        l.addWidget(l5, 6, 0, 1, 6)

        self.bb = bb = QDialogButtonBox(
                QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 6)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        for b in (self.column_box, self.color_box):
            b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
            b.setMinimumContentsLength(15)

        for key in sorted(
                displayable_columns(fm),
                key=sort_key):
            name = fm[key]['name']
            if name:
                self.column_box.addItem(key, key)
        self.column_box.setCurrentIndex(0)

        self.color_box.addItems(QColor.colorNames())
        self.color_box.setCurrentIndex(0)

        self.update_color_label()
        self.color_box.currentIndexChanged.connect(self.update_color_label)
        self.resize(self.sizeHint())