在PyQt4中,我在滚动区域中构建一个大型列表,这需要10秒钟。目前应用程序在构建时挂起,从我的理解,我不能在主线程以外的线程中构建gui对象。有没有办法让这个过程更优雅?我可以绕过'主要线程之外的'no gui建筑'吗?或者也许存储这些gui对象并在我再次启动应用程序后读取它们?我最初使用uic.loadUi()来构建主窗口。
这是MainWindow和buildMatchHistory代码:
class MainWindow(QMainWindow):
def __init__(self):
global summonerName, summonerNameFull, summonerId, summonerRegion, summonerRank, apiKey
super(QMainWindow, self).__init__()
# Load UI
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\MainWindow.ui'
self.ui = uic.loadUi(fileLocation, self)
# Connect the Refresh match history button to the appropriate method and set the window icon
self.refreshMatchHistoryButton.clicked.connect(self.refreshMatchHistory)
self.setWindowIcon(QIcon('app_icon.png'))
self.ui.show()
self.matchHistoryBuilder = MatchHistoryBuilder()
# If match_history.txt exists, call buildMatchHistory. If not, initialize match history files, then call buildMatchHistory.
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
isFile = os.path.isfile(fileLocation)
if isFile:
self.buildMatchHistory()
pass
else:
# Pull match history and store in match_history.txt
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
with open(fileLocation, 'w') as newFile:
matchHistoryData = self.matchHistoryBuilder.getMatchHistory(summonerId)
json.dump(matchHistoryData, newFile)
numberOfMatches = matchHistoryData["totalGames"]
# Create a progress bar to show progress of pulling match history data into files
self.initMatchHistoryProgressDialog = QProgressDialog(self)
self.initMatchHistoryProgressDialog.setMinimum(1)
self.initMatchHistoryProgressDialog.setMaximum(numberOfMatches)
self.initMatchHistoryProgressDialog.setFixedSize(600, 300)
self.initMatchHistoryProgressDialog.setWindowTitle("Getting match history data")
self.initMatchHistoryProgressDialog.show()
# Spawn a new thread that initializes the match history files. When the thread is finished, call buildMatchHistory().
self.initMatchHistoryWorkerThread = QThread(self)
self.initMatchHistory = InitMatchHistory()
self.initMatchHistory.moveToThread(self.initMatchHistoryWorkerThread)
self.initMatchHistory.matchDetailsPulled.connect(self.incrementProgressBar)
QObject.connect(self.initMatchHistoryWorkerThread, SIGNAL('started()'), self.initMatchHistory.run)
QObject.connect(self.initMatchHistory, SIGNAL('finished()'), self.initMatchHistoryWorkerThread.quit)
QObject.connect(self.initMatchHistory, SIGNAL('finished()'), self.initMatchHistory.deleteLater)
QObject.connect(self.initMatchHistory, SIGNAL('finished()'), self.buildMatchHistory)
QObject.connect(self.initMatchHistoryWorkerThread, SIGNAL('finished()'), self.initMatchHistoryWorkerThread.deleteLater)
self.initMatchHistoryWorkerThread.start()
def buildMatchHistory(self):
# This method takes whatever matches are in match_history.txt, calls MatchHistoryBuilder.buildMatch() on each,
# and builds the GUI objects for the match history into the matchHistoryScrollArea.
# Globals: self.matchHistoryBuilder
print "Entered buildMatchHistory"
# Open match_history.txt and read json data into matchHistoryData
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
with open(fileLocation, 'r') as f:
matchHistoryData = json.load(f)
matchHistoryData = matchHistoryData["matches"]
# Scroll Area Properties
self.matchHistory = self.ui.matchHistoryScrollArea
self.matchHistory.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.matchHistory.setWidgetResizable(True)
# Container Widget
widget = QWidget()
# Layout of Container Widget
layout = QVBoxLayout()
for matchIndex, matchInstance in enumerate(matchHistoryData):
matchId = matchInstance["matchId"]
match = self.matchHistoryBuilder.buildMatch(summonerId, matchIndex, matchId)
layout.addWidget(match)
widget.setLayout(layout)
self.matchHistory.setWidget(widget)
MatchHistoryBuilder部分:
class MatchHistoryBuilder(QObject):
def __init__(self):
super(QObject, self).__init__()
#self.mainWindow = mainWindow
config = SafeConfigParser()
configFileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + "\config.ini"
config.read(configFileLocation)
global apiKey
apiKey = str(config.get('main', 'apiKey'))
if not apiKey:
# Pull api_key from internal file
print "Was forced to use api_key.txt in MatchHistoryBuilder"
apiKeyFileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + "\\api_key.txt"
with open(apiKeyFileLocation, 'r') as f:
apiKey = f.read()
if not apiKey:
print "no API Key available"
def buildMatch(self, summonerId, matchIndex, matchId):
# This method takes the matchIndex and matchId as an input, builds a match object, and returns it.
# Globals: none
# Open match_history.txt and load json data to matchHistoryData
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
with open(fileLocation, 'r') as f:
matchHistoryData = json.load(f)
# If match_history_details.txt isn't yet a file, create it
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history_details.txt'
isFile = os.path.isfile(fileLocation)
if not isFile:
with open(fileLocation, 'w') as newFile:
print "Created match_history_details.txt"
matchHistoryDetails = {}
json.dump(matchHistoryDetails, newFile)
# Open match_history_details and check whether we have match data for the matchID in question. If we do,
# continue. If not, call getMatchDetails for the match and store that data in match_history_details.txt.
with open(fileLocation, 'r') as f:
matchHistoryDetails = json.load(f)
if str(matchId) not in matchHistoryDetails.keys():
matchDetails = self.getMatchDetails(summonerId, matchId)
while not matchDetails:
time.sleep(1)
matchDetails = self.getMatchDetails(summonerId, matchId)
matchHistoryDetails[matchId] = matchDetails
with open(fileLocation, 'w') as f:
json.dump(matchHistoryDetails, f)
# Load champion name from getChampionName() and lane from matchHistoryData
championId = matchHistoryData["matches"][matchIndex]["champion"]
championName = self.getChampionName(championId)
lane = matchHistoryData["matches"][matchIndex]["lane"].lower().capitalize()
# Build the match GroupBox itself with the champion name as the title
match = QGroupBox(championName)
match.setFixedHeight(170)
match.setFixedWidth(940)
# For each match, build GroupBox's for statistics and set their sizes, positions, and object names
scoreBox = QGroupBox(match)
scoreBox.setFixedWidth(130)
scoreBox.setFixedHeight(60)
scoreBox.setGeometry(QRect(280, 20, 130, 60))
scoreBox.setAlignment(Qt.AlignCenter)
scoreBox.setObjectName("scoreBox")
killParticipationPercentBox = QGroupBox(match)
killParticipationPercentBox.setFixedWidth(130)
killParticipationPercentBox.setFixedHeight(60)
killParticipationPercentBox.setGeometry(QRect(440, 20, 130, 60))
killParticipationPercentBox.setAlignment(Qt.AlignCenter)
killParticipationPercentBox.setObjectName("killParticipationPercentBox")
goldPerMinBox = QGroupBox(match)
goldPerMinBox.setFixedWidth(130)
goldPerMinBox.setFixedHeight(60)
goldPerMinBox.setGeometry(QRect(440, 90, 130, 60))
goldPerMinBox.setAlignment(Qt.AlignCenter)
goldPerMinBox.setObjectName("goldPerMinBox")
kdaBox = QGroupBox(match)
kdaBox.setFixedWidth(130)
kdaBox.setFixedHeight(60)
kdaBox.setGeometry(QRect(280, 90, 130, 60))
kdaBox.setAlignment(Qt.AlignCenter)
kdaBox.setObjectName("kdaBox")
wardScoreBox = QGroupBox(match)
wardScoreBox.setFixedWidth(130)
wardScoreBox.setFixedHeight(60)
wardScoreBox.setGeometry(QRect(600, 20, 130, 60))
wardScoreBox.setAlignment(Qt.AlignCenter)
wardScoreBox.setObjectName("wardScoreBox")
csPerMinBox = QGroupBox(match)
csPerMinBox.setFixedWidth(130)
csPerMinBox.setFixedHeight(60)
csPerMinBox.setGeometry(QRect(600, 90, 130, 60))
csPerMinBox.setAlignment(Qt.AlignCenter)
csPerMinBox.setObjectName("groupBox_7")
changeInLPLabel = QLabel(match)
changeInLPLabel.setGeometry(QRect(820, 90, 101, 61))
changeInLPLabel.setObjectName("changeInLP")
laneLabel = QLabel(match)
laneLabel.setGeometry(QRect(20, 100, 141, 51))
laneLabel.setStyleSheet("font: 10pt \"Verdana\";")
laneLabel.setObjectName("lane")
# Set titles of each item
scoreBox.setTitle("score")
killParticipationPercentBox.setTitle("Kill part. %")
goldPerMinBox.setTitle("gold/min")
kdaBox.setTitle("KDA")
wardScoreBox.setTitle("ward score")
csPerMinBox.setTitle("cs/min")
changeInLPLabel.setText("+lp")
laneLabel.setText(lane)
return match
工人主题:
class InitMatchHistory(QObject):
matchDetailsPulled = pyqtSignal(object)
def __init__(self):
super(QObject, self).__init__()
def run(self):
configFileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + "\config.ini"
config = SafeConfigParser()
config.read(configFileLocation)
self.summonerId = config.get('main', 'summonerId')
self.matchHistoryBuilder = MatchHistoryBuilder()
# If match_history_details.txt isn't yet a file, create it
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history_details.txt'
isFile = os.path.isfile(fileLocation)
if not isFile:
with open(fileLocation, 'w') as newFile:
print "Created match_history_details.txt"
matchHistoryDetails = {}
json.dump(matchHistoryDetails, newFile)
# Open match_history.txt and read json data into matchHistoryData
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
with open(fileLocation, 'r') as f:
matchHistoryData = json.load(f)
matchHistoryData = matchHistoryData["matches"]
# For each match in match history, open match_history_details and check whether we have match data for the
# matchID in question. If not, call getMatchDetails for the match and store that data in match_history_details.txt.
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history_details.txt'
with open(fileLocation, 'r') as f:
matchHistoryDetails = json.load(f)
for matchIndex, matchInstance in enumerate(matchHistoryData):
matchId = matchInstance["matchId"]
if str(matchId) not in matchHistoryDetails.keys():
matchDetails = self.matchHistoryBuilder.getMatchDetails(self.summonerId, matchId)
if not matchDetails:
time.sleep(10)
matchDetails = self.matchHistoryBuilder.getMatchDetails(self.summonerId, matchId)
matchHistoryDetails[matchId] = matchDetails
self.matchDetailsPulled.emit(str(matchIndex))
with open(fileLocation, 'w') as f:
json.dump(matchHistoryDetails, f)
class RefreshMatchHistory(QObject):
def __init__(self):
super(QObject, self).__init__()
def run(self):
configFileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + "\config.ini"
config = SafeConfigParser()
config.read(configFileLocation)
self.summonerId = config.get('main', 'summonerId')
self.matchHistoryBuilder = MatchHistoryBuilder()
try:
# Open match_history.txt and read json data into matchHistoryData
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
with open(fileLocation, 'r') as f:
oldMatchHistoryData = json.load(f)
oldMatches = oldMatchHistoryData["matches"]
# Call getMatchHistory and for any new matches, call getMatchDetails and update match_history_details.txt
newMatchHistoryData = self.matchHistoryBuilder.getMatchHistory(self.summonerId)
newMatches = newMatchHistoryData["matches"]
numberOfNewMatches = newMatchHistoryData["totalGames"] - oldMatchHistoryData["totalGames"]
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history_details.txt'
with open(fileLocation, 'r') as f:
matchHistoryDetails = json.load(f)
while numberOfNewMatches > 0:
matchId = newMatches[numberOfNewMatches-1]["matchId"]
if str(matchId) not in matchHistoryDetails.keys():
matchDetails = self.getMatchDetails(self.summonerId, matchId)
if not matchDetails:
time.sleep(10)
matchDetails = self.getMatchDetails(self.summonerId, matchId)
matchHistoryDetails[matchId] = matchDetails
numberOfNewMatches -= 1
with open(fileLocation, 'w') as f:
json.dump(matchHistoryDetails, f)
# When done, store the new match history in match_history.txt
fileLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + '\match_history.txt'
with open(fileLocation, 'w') as f:
json.dump(newMatchHistoryData, f)
except IOError:
print "One or both of the match history files are missing, from refreshMatchHistory"