mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
multi-deck sync and related improvements
- deck open & browser refresh done after splash screen hidden now - splash reduced to 3 steps - new options sync on program load/close - per-deck auto sync disabled on upgrade - plugins are now always loaded before a deck has been opened - don't prompt for sync params in auto sync, for both all and single deck - refresh deck browser after multi sync - wait on sync thread until syncFinished called, could fix crashes - after a full sync, ensure interface is still disabled - sync menu option now available in deck browser - new option to tell a progress window it should appear immediately
This commit is contained in:
parent
77ede46ebb
commit
f93910128f
8 changed files with 173 additions and 86 deletions
|
@ -142,7 +142,7 @@ def run():
|
|||
import forms
|
||||
import ui
|
||||
|
||||
ui.splash = SplashScreen(5)
|
||||
ui.splash = SplashScreen(3)
|
||||
|
||||
import anki
|
||||
if anki.version != appVersion:
|
||||
|
|
|
@ -99,12 +99,18 @@ class Config(dict):
|
|||
'suppressEstimates': False,
|
||||
'suppressUpdate': False,
|
||||
'syncInMsgBox': False,
|
||||
'syncOnClose': True,
|
||||
'syncOnLoad': True,
|
||||
'syncOnClose': False,
|
||||
'syncOnLoad': False,
|
||||
'syncOnProgramClose': True,
|
||||
'syncOnProgramOpen': True,
|
||||
'syncPassword': "",
|
||||
'syncUsername': "",
|
||||
'typeAnswerFontSize': 20,
|
||||
}
|
||||
# disable sync on deck load when upgrading
|
||||
if not self.has_key("syncOnProgramOpen"):
|
||||
self['syncOnLoad'] = False
|
||||
self['syncOnClose'] = False
|
||||
for (k,v) in fields.items():
|
||||
if not self.has_key(k):
|
||||
self[k] = v
|
||||
|
|
|
@ -42,6 +42,7 @@ class AnkiQt(QMainWindow):
|
|||
self.state = "initial"
|
||||
self.hideWelcome = False
|
||||
self.views = []
|
||||
signal.signal(signal.SIGINT, self.onSigInt)
|
||||
self.setLang()
|
||||
self.setupStyle()
|
||||
self.setupFonts()
|
||||
|
@ -69,37 +70,40 @@ class AnkiQt(QMainWindow):
|
|||
self.resize(500, 500)
|
||||
# load deck
|
||||
ui.splash.update()
|
||||
if (args or self.config['loadLastDeck'] or
|
||||
len(self.config['recentDeckPaths']) == 1) and \
|
||||
not self.maybeLoadLastDeck(args):
|
||||
self.setEnabled(True)
|
||||
self.moveToState("auto")
|
||||
# check for updates
|
||||
ui.splash.update()
|
||||
self.setupErrorHandler()
|
||||
self.setupMisc()
|
||||
# activate & raise is useful when run from the command line on osx
|
||||
self.activateWindow()
|
||||
self.raise_()
|
||||
# plugins might be looking at this
|
||||
self.state = "noDeck"
|
||||
self.loadPlugins()
|
||||
self.setupAutoUpdate()
|
||||
self.rebuildPluginsMenu()
|
||||
# run after-init hook
|
||||
# plugins loaded, now show interface
|
||||
ui.splash.finish(self)
|
||||
self.show()
|
||||
# program open sync
|
||||
if self.config['syncOnProgramOpen']:
|
||||
self.syncDeck(interactive=False)
|
||||
if (args or self.config['loadLastDeck'] or
|
||||
len(self.config['recentDeckPaths']) == 1):
|
||||
# open the last deck
|
||||
self.maybeLoadLastDeck(args)
|
||||
if self.deck:
|
||||
# deck open sync?
|
||||
if self.config['syncOnLoad'] and self.deck.syncName:
|
||||
self.syncDeck(interactive=False)
|
||||
elif not self.config['syncOnProgramOpen'] or not self.browserDecks:
|
||||
# sync disabled or no user/pass, so draw deck browser manually
|
||||
self.moveToState("noDeck")
|
||||
# all setup is done, run after-init hook
|
||||
try:
|
||||
runHook('init')
|
||||
except:
|
||||
ui.utils.showWarning(
|
||||
_("Broken plugin:\n\n%s") %
|
||||
unicode(traceback.format_exc(), "utf-8", "replace"))
|
||||
ui.splash.update()
|
||||
ui.splash.finish(self)
|
||||
# ensure actions are updated after plugins loaded
|
||||
self.moveToState("auto")
|
||||
self.show()
|
||||
if (self.deck and self.config['syncOnLoad'] and
|
||||
self.deck.syncName):
|
||||
self.syncDeck(interactive=False)
|
||||
signal.signal(signal.SIGINT, self.onSigInt)
|
||||
except:
|
||||
ui.utils.showInfo("Error during startup:\n%s" %
|
||||
traceback.format_exc())
|
||||
|
@ -1117,8 +1121,8 @@ your deck."""))
|
|||
if not self.config['recentDeckPaths']:
|
||||
return
|
||||
toRemove = []
|
||||
if ui.splash.finished:
|
||||
self.startProgress(max=len(self.config['recentDeckPaths']))
|
||||
self.startProgress(max=len(self.config['recentDeckPaths']),
|
||||
immediate=True)
|
||||
for c, d in enumerate(self.config['recentDeckPaths']):
|
||||
if ui.splash.finished:
|
||||
self.updateProgress(_("Checking deck %(x)d of %(y)d...") % {
|
||||
|
@ -1378,6 +1382,8 @@ later by using File>Close.
|
|||
if not self.saveAndClose(hideWelcome=True):
|
||||
event.ignore()
|
||||
else:
|
||||
if self.config['syncOnProgramClose']:
|
||||
self.syncDeck(interactive=False)
|
||||
self.prepareForExit()
|
||||
event.accept()
|
||||
self.app.quit()
|
||||
|
@ -2095,7 +2101,8 @@ it to your friends.
|
|||
if not self.inMainWindow() and interactive: return
|
||||
self.setNotice()
|
||||
# vet input
|
||||
self.ensureSyncParams()
|
||||
if interactive:
|
||||
self.ensureSyncParams()
|
||||
u=self.config['syncUsername']
|
||||
p=self.config['syncPassword']
|
||||
if not u or not p:
|
||||
|
@ -2110,28 +2117,34 @@ it to your friends.
|
|||
self.deckProperties.dialog.qtabwidget.setCurrentIndex(1)
|
||||
self.showToolTip(_("Enable syncing, choose a name, then sync again."))
|
||||
return
|
||||
if self.deck is None and self.deckPath is None:
|
||||
# qt on linux incorrectly accepts shortcuts for disabled actions
|
||||
return
|
||||
# hide all deck-associated dialogs
|
||||
self.closeAllDeckWindows()
|
||||
if self.deck:
|
||||
# save first, so we can rollback on failure
|
||||
self.deck.save()
|
||||
# store data we need before closing the deck
|
||||
self.deckPath = self.deck.path
|
||||
self.syncName = self.deck.syncName or self.deck.name()
|
||||
self.lastSync = self.deck.lastSync
|
||||
if checkSources:
|
||||
self.sourcesToCheck = self.deck.s.column0(
|
||||
"select id from sources where syncPeriod != -1 "
|
||||
"and syncPeriod = 0 or :t - lastSync > syncPeriod",
|
||||
t=time.time())
|
||||
else:
|
||||
self.sourcesToCheck = []
|
||||
self.deck.close()
|
||||
self.deck = None
|
||||
self.loadAfterSync = reload
|
||||
if self.deck is None and getattr(self, 'deckPath', None) is None:
|
||||
# sync all decks
|
||||
self.loadAfterSync = -1
|
||||
self.syncName = None
|
||||
self.sourcesToCheck = []
|
||||
self.syncDecks = self.decksToSync()
|
||||
else:
|
||||
# sync one deck
|
||||
# hide all deck-associated dialogs
|
||||
self.closeAllDeckWindows()
|
||||
|
||||
if self.deck:
|
||||
# save first, so we can rollback on failure
|
||||
self.deck.save()
|
||||
# store data we need before closing the deck
|
||||
self.deckPath = self.deck.path
|
||||
self.syncName = self.deck.syncName or self.deck.name()
|
||||
self.lastSync = self.deck.lastSync
|
||||
if checkSources:
|
||||
self.sourcesToCheck = self.deck.s.column0(
|
||||
"select id from sources where syncPeriod != -1 "
|
||||
"and syncPeriod = 0 or :t - lastSync > syncPeriod",
|
||||
t=time.time())
|
||||
else:
|
||||
self.sourcesToCheck = []
|
||||
self.deck.close()
|
||||
self.deck = None
|
||||
self.loadAfterSync = reload
|
||||
# bug triggered by preferences dialog - underlying c++ widgets are not
|
||||
# garbage collected until the middle of the child thread
|
||||
self.state = "nostate"
|
||||
|
@ -2146,7 +2159,7 @@ it to your friends.
|
|||
self.connect(self.syncThread, SIGNAL("noMatchingDeck"), self.selectSyncDeck)
|
||||
self.connect(self.syncThread, SIGNAL("syncClockOff"), self.syncClockOff)
|
||||
self.connect(self.syncThread, SIGNAL("cleanNewDeck"), self.cleanNewDeck)
|
||||
self.connect(self.syncThread, SIGNAL("syncFinished"), self.syncFinished)
|
||||
self.connect(self.syncThread, SIGNAL("syncFinished"), self.onSyncFinished)
|
||||
self.connect(self.syncThread, SIGNAL("openSyncProgress"), self.openSyncProgress)
|
||||
self.connect(self.syncThread, SIGNAL("closeSyncProgress"), self.closeSyncProgress)
|
||||
self.connect(self.syncThread, SIGNAL("updateSyncProgress"), self.updateSyncProgress)
|
||||
|
@ -2158,16 +2171,27 @@ it to your friends.
|
|||
self.syncThread.start()
|
||||
self.switchToWelcomeScreen()
|
||||
self.setEnabled(False)
|
||||
while not self.syncThread.isFinished():
|
||||
self.syncFinished = False
|
||||
while not self.syncFinished:
|
||||
self.app.processEvents()
|
||||
self.syncThread.wait(100)
|
||||
self.setEnabled(True)
|
||||
return self.syncThread.ok
|
||||
|
||||
def syncFinished(self):
|
||||
def decksToSync(self):
|
||||
ok = []
|
||||
for d in self.config['recentDeckPaths']:
|
||||
if os.path.exists(d):
|
||||
ok.append(d)
|
||||
return ok
|
||||
|
||||
def onSyncFinished(self):
|
||||
"Reopen after sync finished."
|
||||
self.mainWin.buttonStack.show()
|
||||
if self.loadAfterSync:
|
||||
if self.loadAfterSync == -1:
|
||||
# after sync all, so refresh browser list
|
||||
self.moveToState("noDeck")
|
||||
elif self.loadAfterSync:
|
||||
if self.loadAfterSync == 2:
|
||||
name = re.sub("[<>]", "", self.syncName)
|
||||
p = os.path.join(self.documentDir, name + ".anki")
|
||||
|
@ -2183,6 +2207,7 @@ it to your friends.
|
|||
elif not self.hideWelcome:
|
||||
self.moveToState("noDeck")
|
||||
self.deckPath = None
|
||||
self.syncFinished = True
|
||||
|
||||
def selectSyncDeck(self, decks, create=True):
|
||||
name = ui.sync.DeckChooser(self, decks, create).getName()
|
||||
|
@ -2195,7 +2220,7 @@ it to your friends.
|
|||
if not create:
|
||||
self.cleanNewDeck()
|
||||
else:
|
||||
self.syncFinished()
|
||||
self.onSyncFinished()
|
||||
|
||||
def cleanNewDeck(self):
|
||||
"Unload a new deck if an initial sync failed."
|
||||
|
@ -2213,7 +2238,7 @@ it to your friends.
|
|||
_("Since this can cause many problems with syncing,\n"
|
||||
"syncing is disabled until you fix the problem.")
|
||||
)
|
||||
self.syncFinished()
|
||||
self.onSyncFinished()
|
||||
|
||||
def showSyncWarning(self, text):
|
||||
ui.utils.showWarning(text, self)
|
||||
|
@ -2259,6 +2284,8 @@ it to your friends.
|
|||
|
||||
def fullSyncFinished(self):
|
||||
self.finishProgress()
|
||||
# need to deactivate interface again
|
||||
self.setEnabled(False)
|
||||
|
||||
def fullSyncProgress(self, type, val):
|
||||
if type == "fromLocal":
|
||||
|
@ -2277,7 +2304,6 @@ it to your friends.
|
|||
"Close",
|
||||
"Addcards",
|
||||
"Editdeck",
|
||||
"Syncdeck",
|
||||
"DisplayProperties",
|
||||
"DeckProperties",
|
||||
"Undo",
|
||||
|
@ -2654,13 +2680,13 @@ it to your friends.
|
|||
def setProgressParent(self, parent):
|
||||
self.progressParent = parent
|
||||
|
||||
def startProgress(self, max=0, min=0, title=None):
|
||||
def startProgress(self, max=0, min=0, title=None, immediate=False):
|
||||
if self.mainThread != QThread.currentThread():
|
||||
return
|
||||
self.setBusy()
|
||||
if not self.progressWins:
|
||||
parent = self.progressParent or self.app.activeWindow() or self
|
||||
p = ui.utils.ProgressWin(parent, max, min, title)
|
||||
p = ui.utils.ProgressWin(parent, max, min, title, immediate)
|
||||
else:
|
||||
p = None
|
||||
self.progressWins.append(p)
|
||||
|
|
|
@ -101,6 +101,8 @@ class Preferences(QDialog):
|
|||
def setupNetwork(self):
|
||||
self.dialog.syncOnOpen.setChecked(self.config['syncOnLoad'])
|
||||
self.dialog.syncOnClose.setChecked(self.config['syncOnClose'])
|
||||
self.dialog.syncOnProgramOpen.setChecked(self.config['syncOnProgramOpen'])
|
||||
self.dialog.syncOnProgramClose.setChecked(self.config['syncOnProgramClose'])
|
||||
self.dialog.syncUser.setText(self.config['syncUsername'])
|
||||
self.dialog.syncPass.setText(self.config['syncPassword'])
|
||||
self.dialog.proxyHost.setText(self.config['proxyHost'])
|
||||
|
@ -113,6 +115,8 @@ class Preferences(QDialog):
|
|||
def updateNetwork(self):
|
||||
self.config['syncOnLoad'] = self.dialog.syncOnOpen.isChecked()
|
||||
self.config['syncOnClose'] = self.dialog.syncOnClose.isChecked()
|
||||
self.config['syncOnProgramOpen'] = self.dialog.syncOnProgramOpen.isChecked()
|
||||
self.config['syncOnProgramClose'] = self.dialog.syncOnProgramClose.isChecked()
|
||||
self.config['syncUsername'] = unicode(self.dialog.syncUser.text())
|
||||
self.config['syncPassword'] = unicode(self.dialog.syncPass.text())
|
||||
self.config['proxyHost'] = unicode(self.dialog.proxyHost.text())
|
||||
|
|
|
@ -10,6 +10,7 @@ from anki.sync import SyncClient, HttpSyncServerProxy, copyLocalMedia
|
|||
from anki.sync import SYNC_HOST, SYNC_PORT
|
||||
from anki.errors import *
|
||||
from anki import DeckStorage
|
||||
from anki.db import sqlite
|
||||
import ankiqt.forms
|
||||
from anki.hooks import addHook, removeHook
|
||||
|
||||
|
@ -29,6 +30,7 @@ class Sync(QThread):
|
|||
self.ok = True
|
||||
self.onlyMerge = onlyMerge
|
||||
self.sourcesToCheck = sourcesToCheck
|
||||
self.proxy = None
|
||||
addHook('fullSyncStarted', self.fullSyncStarted)
|
||||
addHook('fullSyncFinished', self.fullSyncFinished)
|
||||
addHook('fullSyncProgress', self.fullSyncProgress)
|
||||
|
@ -37,7 +39,10 @@ class Sync(QThread):
|
|||
self.emit(SIGNAL("setStatus"), msg, timeout)
|
||||
|
||||
def run(self):
|
||||
self.syncDeck()
|
||||
if self.parent.syncName:
|
||||
self.syncDeck()
|
||||
else:
|
||||
self.syncAllDecks()
|
||||
removeHook('fullSyncStarted', self.fullSyncStarted)
|
||||
removeHook('fullSyncFinished', self.fullSyncFinished)
|
||||
removeHook('fullSyncProgress', self.fullSyncProgress)
|
||||
|
@ -77,22 +82,44 @@ class Sync(QThread):
|
|||
|
||||
def connect(self, *args):
|
||||
# connect, check auth
|
||||
proxy = HttpSyncServerProxy(self.user, self.pwd)
|
||||
proxy.sourcesToCheck = self.sourcesToCheck
|
||||
proxy.connect("ankiqt-" + ankiqt.appVersion)
|
||||
return proxy
|
||||
if not self.proxy:
|
||||
self.setStatus(_("Connecting..."), 0)
|
||||
proxy = HttpSyncServerProxy(self.user, self.pwd)
|
||||
proxy.sourcesToCheck = self.sourcesToCheck
|
||||
proxy.connect("ankiqt-" + ankiqt.appVersion)
|
||||
self.proxy = proxy
|
||||
return self.proxy
|
||||
|
||||
def syncDeck(self):
|
||||
self.setStatus(_("Connecting..."), 0)
|
||||
def syncAllDecks(self):
|
||||
decks = self.parent.syncDecks
|
||||
for d in decks:
|
||||
self.syncDeck(deck=d)
|
||||
self.emit(SIGNAL("syncFinished"))
|
||||
|
||||
def syncDeck(self, deck=None):
|
||||
# multi-mode setup
|
||||
if deck:
|
||||
c = sqlite.connect(deck)
|
||||
syncName = c.execute("select syncName from decks").fetchone()[0]
|
||||
c.close()
|
||||
if not syncName:
|
||||
return
|
||||
path = deck
|
||||
else:
|
||||
syncName = self.parent.syncName
|
||||
path = self.parent.deckPath
|
||||
# ensure deck mods cached
|
||||
try:
|
||||
proxy = self.connect()
|
||||
except SyncError, e:
|
||||
return self.error(e)
|
||||
# exists on server?
|
||||
if not proxy.hasDeck(self.parent.syncName):
|
||||
if not proxy.hasDeck(syncName):
|
||||
if deck:
|
||||
return
|
||||
if self.create:
|
||||
try:
|
||||
proxy.createDeck(self.parent.syncName)
|
||||
proxy.createDeck(syncName)
|
||||
except SyncError, e:
|
||||
return self.error(e)
|
||||
else:
|
||||
|
@ -100,17 +127,18 @@ class Sync(QThread):
|
|||
self.emit(SIGNAL("noMatchingDeck"), keys, not self.onlyMerge)
|
||||
self.setStatus("")
|
||||
return
|
||||
self.setStatus(_("Syncing <b>%s</b>...") % syncName, 0)
|
||||
timediff = abs(proxy.timestamp - time.time())
|
||||
if timediff > 300:
|
||||
self.emit(SIGNAL("syncClockOff"), timediff)
|
||||
return
|
||||
# reconnect
|
||||
# reopen
|
||||
self.deck = None
|
||||
try:
|
||||
self.deck = DeckStorage.Deck(self.parent.deckPath)
|
||||
self.deck = DeckStorage.Deck(path)
|
||||
client = SyncClient(self.deck)
|
||||
client.setServer(proxy)
|
||||
proxy.deckName = self.parent.syncName
|
||||
proxy.deckName = syncName
|
||||
# need to do anything?
|
||||
start = time.time()
|
||||
if client.prepareSync():
|
||||
|
@ -129,15 +157,16 @@ class Sync(QThread):
|
|||
client.fullSyncFromServer(ret[1], ret[2])
|
||||
self.setStatus(_("Sync complete."), 0)
|
||||
# reopen the deck in case we have sources
|
||||
self.deck = DeckStorage.Deck(self.parent.deckPath)
|
||||
self.deck = DeckStorage.Deck(path)
|
||||
client.deck = self.deck
|
||||
else:
|
||||
# diff
|
||||
self.setStatus(_("Determining differences..."), 0)
|
||||
payload = client.genPayload(sums)
|
||||
# send payload
|
||||
pr = client.payloadChangeReport(payload)
|
||||
self.setStatus("<br>" + pr + "<br>", 0)
|
||||
if not deck:
|
||||
pr = client.payloadChangeReport(payload)
|
||||
self.setStatus("<br>" + pr + "<br>", 0)
|
||||
self.setStatus(_("Transferring payload..."), 0)
|
||||
res = client.server.applyPayload(payload)
|
||||
# apply reply
|
||||
|
@ -150,7 +179,8 @@ class Sync(QThread):
|
|||
self.deck.s.commit()
|
||||
else:
|
||||
changes = False
|
||||
self.setStatus(_("No changes found."))
|
||||
if not deck:
|
||||
self.setStatus(_("No changes found."))
|
||||
# check sources
|
||||
srcChanged = False
|
||||
if self.sourcesToCheck:
|
||||
|
@ -177,12 +207,13 @@ class Sync(QThread):
|
|||
self.deck.s.commit()
|
||||
# close and send signal to main thread
|
||||
self.deck.close()
|
||||
taken = time.time() - start
|
||||
if (changes or srcChanged) and taken < 2.5:
|
||||
time.sleep(2.5 - taken)
|
||||
else:
|
||||
time.sleep(0.25)
|
||||
self.emit(SIGNAL("syncFinished"))
|
||||
if not deck:
|
||||
taken = time.time() - start
|
||||
if (changes or srcChanged) and taken < 2.5:
|
||||
time.sleep(2.5 - taken)
|
||||
else:
|
||||
time.sleep(0.25)
|
||||
self.emit(SIGNAL("syncFinished"))
|
||||
except Exception, e:
|
||||
self.ok = False
|
||||
#traceback.print_exc()
|
||||
|
@ -192,7 +223,8 @@ class Sync(QThread):
|
|||
err = `getattr(e, 'data', None) or e`
|
||||
self.setStatus(_("Syncing failed: %(a)s") % {
|
||||
'a': err})
|
||||
self.error(e)
|
||||
if not deck:
|
||||
self.error(e)
|
||||
|
||||
# Choosing a deck to sync to
|
||||
##########################################################################
|
||||
|
|
|
@ -235,7 +235,7 @@ def getBase(deck, card):
|
|||
|
||||
class ProgressWin(object):
|
||||
|
||||
def __init__(self, parent, max=0, min=0, title=None):
|
||||
def __init__(self, parent, max=0, min=0, title=None, immediate=False):
|
||||
if not title:
|
||||
title = "Anki"
|
||||
self.diag = QProgressDialog("", "", min, max, parent)
|
||||
|
@ -245,7 +245,10 @@ class ProgressWin(object):
|
|||
self.diag.setAutoReset(False)
|
||||
# qt doesn't seem to honour this consistently, and it's not triggered
|
||||
# by the db progress handler, so we set it high and use maybeShow() below
|
||||
self.diag.setMinimumDuration(100000)
|
||||
if immediate:
|
||||
self.diag.show()
|
||||
else:
|
||||
self.diag.setMinimumDuration(100000)
|
||||
self.counter = min
|
||||
self.min = min
|
||||
self.max = max
|
||||
|
|
|
@ -2972,7 +2972,7 @@
|
|||
<string>S&ync</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Synchronize this deck with Anki Online</string>
|
||||
<string>Synchronize with Anki Online</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+Y</string>
|
||||
|
|
|
@ -204,7 +204,7 @@
|
|||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="syncOnClose">
|
||||
<property name="text">
|
||||
<string>Sync on close</string>
|
||||
<string>Sync on deck close</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
|
@ -221,13 +221,27 @@
|
|||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="syncOnOpen">
|
||||
<property name="text">
|
||||
<string>Sync on open</string>
|
||||
<string>Sync on deck open</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="syncOnProgramOpen">
|
||||
<property name="text">
|
||||
<string>Sync on program open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="syncOnProgramClose">
|
||||
<property name="text">
|
||||
<string>Sync on program close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -689,6 +703,8 @@
|
|||
<tabstop>syncPass</tabstop>
|
||||
<tabstop>syncOnOpen</tabstop>
|
||||
<tabstop>syncOnClose</tabstop>
|
||||
<tabstop>syncOnProgramOpen</tabstop>
|
||||
<tabstop>syncOnProgramClose</tabstop>
|
||||
<tabstop>proxyHost</tabstop>
|
||||
<tabstop>proxyPort</tabstop>
|
||||
<tabstop>proxyUser</tabstop>
|
||||
|
@ -720,8 +736,8 @@
|
|||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>270</x>
|
||||
<y>412</y>
|
||||
<x>279</x>
|
||||
<y>457</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
|
@ -736,8 +752,8 @@
|
|||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>317</x>
|
||||
<y>412</y>
|
||||
<x>326</x>
|
||||
<y>457</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
|
|
Loading…
Reference in a new issue