예제 #1
0
    def findRange(self, button):
        if not self.checkLongAndLat():
            return

        r = self.rangeEdit.text()

        if r == "":
            QMessageBox.warning(self, "警告", "请输入range值", QMessageBox.Ok)
            return

        button.setEnabled(False)

        r = int(r)

        self.kEdit.clear()
        self.keyWordEdit.clear()

        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(
            target=drawRangeMap,
            args=(self.csv_file,
                  self.longitude,
                  self.latitude,
                  r,
                  'html/rangeMap.html', '距离range图'))
        type = self.FINDRANGE
        self.range.append(r)
        self.t.endTrigger.connect(
            lambda: self.showInWebEngineView(button, '/html/rangeMap.html', type))
        self.t.start()
예제 #2
0
 def showTime(self, button):
     data = [
         (self.rangeTime, self.range, 'range查找'),
         (self.topKTimeWithoutKeyWord, self.topKWithoutKeyWord, 'topK无关键字'),
         (self.topKTimeWithKeyWord, self.topKWithKeyWord, 'topK有关键字')
     ]
     self.t = DrawThread(target=drawLineChart, args=(data, 'html/showTime.html'))
     self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/showTime.html'))
     self.t.start()
예제 #3
0
 def drawColorMap(self, button):
     button.setEnabled(False)
     self.loadUrl('/config/loadingHtml/loading.html')
     self.t = DrawThread(
         target=drawColorMaps,
         args=(self.csv_file['Country'],
               'html/colorMap.html', '国家分布彩色图'))
     self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/colorMap.html'))
     self.t.start()
예제 #4
0
    def drawMap(self, button):
        button.setEnabled(False)
        self.loadUrl('/config/loadingHtml/loading.html')

        self.t = DrawThread(
            target=drawMap,
            args=(self.csv_file, 'html/map.html', '不同时区店铺数量渐变图')
        )
        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/map.html'))
        self.t.start()
예제 #5
0
    def findTopK(self, button):
        if not self.checkLongAndLat():
            return


        k = self.kEdit.text()
        keyword = self.keyWordEdit.text()

        if k == "":
            QMessageBox.warning(self, "警告", "请输入k值", QMessageBox.Ok)
            return

        button.setEnabled(False)

        k = int(k)

        self.rangeEdit.clear()

        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(
            target=drawTopKMap,
            args=(
                self.csv_file,
                self.longitude,
                self.latitude,
                k,
                keyword,
                self.data,
                'html/topKMap.html', 'topK点图'))

        if keyword != "":
            type = self.FINDTOPKWITHKEYWORD
            self.topKWithKeyWord.append(k)
        else:
            type = self.FINDTOPKWITHOUTKEYWORD
            self.topKWithoutKeyWord.append(k)

        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/topKMap.html', type))
        self.t.start()
예제 #6
0
 def drawPie(self, button, data, fileName='html/pie.hmtl', title=''):
     button.setEnabled(False)
     self.loadUrl('/config/loadingHtml/loading.html')
     self.t = DrawThread(target=drawPie, args=(data, fileName, title))
     self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/' + fileName))
     self.t.start()
예제 #7
0
class UI(QMainWindow):
    """docstring for UI"""

    # 以下三个常量是用于分别标记查询的类型
    # 以便确定查询的类型来保存相应查询的时间
    FINDTOPKWITHKEYWORD = 1
    FINDTOPKWITHOUTKEYWORD = 2
    FINDRANGE = 3

    def __init__(self):
        super(UI, self).__init__()

        if not os.path.exists('./html'):
            os.mkdir('./html')

        self.t = None
        self.hasOpenFile = False

        # 每种查询时延的列表
        self.topKTimeWithoutKeyWord = []
        self.topKTimeWithKeyWord = []
        self.rangeTime = []

        # 每种查询的k或r值
        self.topKWithoutKeyWord = []
        self.topKWithKeyWord = []
        self.range = []

        self.initUI()

    # 初始化UI
    def initUI(self):
        # 将任务栏图标改成 image/StarBucks.png
        if platform.system() == 'Windows':
            # windows任务栏要这样设置才能和图标一致
            import ctypes
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                "image/StarBucks.png")

        self.setWindowTitle('星巴克数据分析')
        self.setWindowIcon(QIcon('image/StarBucks.png'))

        self.mainWidget = QWidget()  # 主窗体控件
        self.mainLayout = QGridLayout()  # 主窗体layout

        self.menuBar()  # 菜单栏
        self.statusBar()  # 状态栏
        self.setOpenFileMenu()  # 打开文件菜单
        self.setShowQueryTimeMenu()  # 显示时延菜单选项

        self.setShowButton()  # 设置显示统计图的按钮
        self.setFindInfoWidget()

        self.setWebEngineView()

        self.mainWidget.setLayout(self.mainLayout)
        self.setCentralWidget(self.mainWidget)

        self.center()  # 居中窗口, 但固定窗口大小后失效

        self.show()

    # 设置webEngineView
    def setWebEngineView(self):
        self.webEngine = QWebEngineView(self)

        # 设置JS和Python的交互通道
        self.pWebChannel = QWebChannel(self.webEngine.page())
        self.pythonJsInteractObj = PythonJsInteractObject(self)
        self.pWebChannel.registerObject('interactObj', self.pythonJsInteractObj)
        self.webEngine.page().setWebChannel(self.pWebChannel)
        self.pythonJsInteractObj.sigReceivedMessFromJS.connect(self.onReceiveMessageFromJS)

        self.mainLayout.addWidget(self.webEngine, 1, 8, 11, 15)

    # 从JS获取数据
    def onReceiveMessageFromJS(self, strParameter):
        print('ReceiveFromJS', strParameter)
        data = strParameter.split(' ')  # Js传过来的数据格式为 '行号 分数'

        self.csv_file['Score'].iloc[int(data[0])] += data[1] + ' '  # 添加分数到记录中

        score = self.csv_file['Score'].iloc[int(data[0])].strip().split(' ')
        avgScore = sum([float(s) for s in score]) / len(score)  # 求平均分
        self.csv_file['AvgScore'].iloc[int(data[0])] = avgScore
        # print('Score:', score)
        # print('AvgScore:', avgScore)

    # 加载html
    def showInWebEngineView(self, button, fileName, type=None):
        button.setEnabled(True)
        self.statusBar().showMessage(self.t.time)
        t = float(self.t.time.split(' ')[1][:-1])  # 去掉中文字符和单位s
        if type == self.FINDRANGE:
            self.rangeTime.append(t)
        elif type == self.FINDTOPKWITHKEYWORD:
            self.topKTimeWithKeyWord.append(t)
        elif type == self.FINDTOPKWITHOUTKEYWORD:
            self.topKTimeWithoutKeyWord.append(t)
        self.loadUrl(fileName)

    # 加载html
    def loadUrl(self, fileName):
        self.webEngine.page().load(QUrl.fromLocalFile(fileName))

    # 检查输入是否合法
    def checkLongAndLat(self):
        self.longitude = self.longitudeEdit.text()
        self.latitude = self.latitudeEdit.text()
        if self.longitude == "":
            QMessageBox.warning(self, "警告", "请输入经度", QMessageBox.Ok)
            return False

        try:
            self.longitude = float(self.longitude)
            if self.longitude > 180 or self.longitude < -180:
                QMessageBox.warning(self, "错误", "经度在-180~180之间", QMessageBox.Ok)
                return False
        except:
            QMessageBox.warning(self, "错误", "请输入数字", QMessageBox.Ok)
            return False

        if self.latitude == "":
            QMessageBox.warning(self, "警告", "请输入纬度", QMessageBox.Ok)
            return False

        try:
            self.latitude = float(self.latitude)
            if self.latitude > 90 or self.latitude < -90:
                QMessageBox.warning(self, "错误", "纬度在-90~90之间", QMessageBox.Ok)
                return False
        except:
            QMessageBox.warning(self, "错误", "请输入数字", QMessageBox.Ok)
            return False
        return True

    # top-k的输入框,按钮的控件
    def setFindInfoWidget(self):
        longitudeLabel = QLabel()
        latitudeLabel = QLabel()
        rangeLabel = QLabel()
        kLabel = QLabel()
        keyWordLabel = QLabel()

        longitudeLabel.setText("经度: ")
        latitudeLabel.setText("纬度: ")
        rangeLabel.setText("range:")
        kLabel.setText("k: ")
        keyWordLabel.setText("关键词:")

        self.longitudeEdit = QLineEdit()
        self.latitudeEdit = QLineEdit()
        self.rangeEdit = QLineEdit()
        self.kEdit = QLineEdit()
        self.keyWordEdit = QLineEdit()

        longitudeValidator = QDoubleValidator(self)
        longitudeValidator.setRange(-180.00, 180.00)
        longitudeValidator.setNotation(QDoubleValidator.StandardNotation)
        longitudeValidator.setDecimals(2)
        self.longitudeEdit.setValidator(longitudeValidator)

        latitudeValidator = QDoubleValidator(self)
        latitudeValidator.setRange(-90.00, 90.00)
        latitudeValidator.setNotation(QDoubleValidator.StandardNotation)
        latitudeValidator.setDecimals(2)
        self.latitudeEdit.setValidator(latitudeValidator)

        kIntValidator = QIntValidator(self)
        kIntValidator.setRange(0, 25000)
        self.rangeEdit.setPlaceholderText("单位: km")
        self.rangeEdit.setValidator(kIntValidator)

        self.findRangeButton = QPushButton()
        self.findRangeButton.setText("查找range")
        self.findRangeButton.setEnabled(False)
        self.findRangeButton.clicked.connect(lambda : self.findRange(self.findRangeButton))

        self.findTopKButton = QPushButton()
        self.findTopKButton.setText("查找top-k")
        self.findTopKButton.setEnabled(False)
        self.findTopKButton.clicked.connect(lambda : self.findTopK(self.findTopKButton))

        self.longitudeEdit.setStatusTip("经度取值范围: -180.00-180.00")
        self.latitudeEdit.setStatusTip("纬度取值范围: -180.00-180.00")
        self.rangeEdit.setStatusTip("半径取值: 0-25000")

        vBox = QVBoxLayout(self)
        vBox.addWidget(longitudeLabel)
        vBox.addWidget(self.longitudeEdit, 0)
        vBox.addWidget(latitudeLabel)
        vBox.addWidget(self.latitudeEdit, 0)
        vBox.addWidget(rangeLabel)
        vBox.addWidget(self.rangeEdit, 0)
        vBox.addWidget(kLabel)
        vBox.addWidget(self.kEdit, 0)
        vBox.addWidget(keyWordLabel)
        vBox.addWidget(self.keyWordEdit, 0)
        vBox.addWidget(self.findRangeButton, 0)
        vBox.addWidget(self.findTopKButton, 0)

        self.longitudeEdit.setEnabled(False)
        self.latitudeEdit.setEnabled(False)
        self.rangeEdit.setEnabled(False)
        self.kEdit.setEnabled(False)
        self.keyWordEdit.setEnabled(False)

        hWidget = QWidget()
        hWidget.setLayout(vBox)
        self.mainLayout.addWidget(hWidget, 2, 3, 6, 4)

    # range范围查询
    def findRange(self, button):
        if not self.checkLongAndLat():
            return

        r = self.rangeEdit.text()

        if r == "":
            QMessageBox.warning(self, "警告", "请输入range值", QMessageBox.Ok)
            return

        button.setEnabled(False)

        r = int(r)

        self.kEdit.clear()
        self.keyWordEdit.clear()

        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(
            target=drawRangeMap,
            args=(self.csv_file,
                  self.longitude,
                  self.latitude,
                  r,
                  'html/rangeMap.html', '距离range图'))
        type = self.FINDRANGE
        self.range.append(r)
        self.t.endTrigger.connect(
            lambda: self.showInWebEngineView(button, '/html/rangeMap.html', type))
        self.t.start()

    # topK查询
    def findTopK(self, button):
        if not self.checkLongAndLat():
            return


        k = self.kEdit.text()
        keyword = self.keyWordEdit.text()

        if k == "":
            QMessageBox.warning(self, "警告", "请输入k值", QMessageBox.Ok)
            return

        button.setEnabled(False)

        k = int(k)

        self.rangeEdit.clear()

        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(
            target=drawTopKMap,
            args=(
                self.csv_file,
                self.longitude,
                self.latitude,
                k,
                keyword,
                self.data,
                'html/topKMap.html', 'topK点图'))

        if keyword != "":
            type = self.FINDTOPKWITHKEYWORD
            self.topKWithKeyWord.append(k)
        else:
            type = self.FINDTOPKWITHOUTKEYWORD
            self.topKWithoutKeyWord.append(k)

        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/topKMap.html', type))
        self.t.start()

    # 扩展显示
    def showExtension(self):
        self.extensionWidget.setVisible(not self.extensionWidget.isVisible())
        if self.extensionWidget.isVisible():
            self.extensionButton.setText("<<")
        else:
            self.extensionButton.setText(">>")

    # 设置基本按钮, 后续可能要重写
    def setShowButton(self):
        self.drawMapButton = QPushButton('世界分布图', self)
        self.drawColorMapButton = QPushButton('国家分布彩色图', self)
        self.countStoreByTimezoneButton_bar = QPushButton('时区店铺数量柱状图', self)
        self.countStoreByTimezoneButton_pie = QPushButton('时区店铺数量饼图', self)
        self.countStoreByCountryButton_bar = QPushButton('国家店铺数量柱状图', self)
        self.countStoreByCountryButton_pie = QPushButton('国家店铺数量饼图', self)

        self.drawMapButton.setEnabled(False)
        self.drawColorMapButton.setEnabled(False)
        self.countStoreByTimezoneButton_bar.setEnabled(False)
        self.countStoreByTimezoneButton_pie.setEnabled(False)
        self.countStoreByCountryButton_bar.setEnabled(False)
        self.countStoreByCountryButton_pie.setEnabled(False)

        self.drawMapButton.clicked.connect(lambda : self.drawMap(self.drawMapButton))
        self.drawColorMapButton.clicked.connect(lambda : self.drawColorMap(self.drawColorMapButton))
        self.countStoreByTimezoneButton_bar.clicked.connect(
            lambda: self.drawBar(self.countStoreByTimezoneButton_bar,
                                 self.csv_file['Timezone'],
                                 'html/timezoneBar.html', '时区店铺数量柱状图'))
        self.countStoreByTimezoneButton_pie.clicked.connect(
            lambda: self.drawPie(
                self.countStoreByTimezoneButton_pie,
                self.csv_file['Timezone'],
                                 'html/timezonePie.html', '时区店铺数量饼图')
        )

        self.countStoreByCountryButton_bar.clicked.connect(
            lambda: self.drawBar(
                self.countStoreByCountryButton_bar,
                self.csv_file['Country'],
                                 'html/countryBar.html', '国家店铺数量柱状图'))
        self.countStoreByCountryButton_pie.clicked.connect(
            lambda: self.drawPie(
                self.countStoreByCountryButton_pie,
                self.csv_file['Country'],
                                 'html/countryPie.html', '国家店铺数量饼图'))

        # 将旧的需求加入到扩展控件中
        self.extensionWidget = QWidget()
        vBox = QVBoxLayout(self)
        vBox.addWidget(self.drawMapButton)
        vBox.addWidget(self.drawColorMapButton)
        vBox.addWidget(self.countStoreByTimezoneButton_bar)
        vBox.addWidget(self.countStoreByTimezoneButton_pie)
        vBox.addWidget(self.countStoreByCountryButton_bar)
        vBox.addWidget(self.countStoreByCountryButton_pie)
        self.extensionWidget.setLayout(vBox)
        self.extensionWidget.hide()

        # 控制显示和隐藏
        self.extensionButton = QPushButton(">>", self)
        self.extensionButton.setCheckable(True)
        self.extensionButton.setAutoDefault(False)
        self.extensionButton.toggled.connect(self.showExtension)

        self.mainLayout.addWidget(self.extensionButton, 1, 3, 1, 1)
        self.mainLayout.addWidget(self.extensionWidget, 1, 1, 7, 1)

    # 画时区店铺数量渐变彩色点
    def drawMap(self, button):
        button.setEnabled(False)
        self.loadUrl('/config/loadingHtml/loading.html')

        self.t = DrawThread(
            target=drawMap,
            args=(self.csv_file, 'html/map.html', '不同时区店铺数量渐变图')
        )
        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/map.html'))
        self.t.start()

    # 画国家分布彩色渐变图
    def drawColorMap(self, button):
        button.setEnabled(False)
        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(
            target=drawColorMaps,
            args=(self.csv_file['Country'],
                  'html/colorMap.html', '国家分布彩色图'))
        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/colorMap.html'))
        self.t.start()

    # 画柱状图
    def drawBar(self, button, data, fileName='html/bar.html', title=''):
        button.setEnabled(False)
        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(target=drawBar, args=(data, fileName, title))
        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/' + fileName))
        self.t.start()

    # 画饼图
    def drawPie(self, button, data, fileName='html/pie.hmtl', title=''):
        button.setEnabled(False)
        self.loadUrl('/config/loadingHtml/loading.html')
        self.t = DrawThread(target=drawPie, args=(data, fileName, title))
        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/' + fileName))
        self.t.start()

    # 显示时延
    def showTime(self, button):
        data = [
            (self.rangeTime, self.range, 'range查找'),
            (self.topKTimeWithoutKeyWord, self.topKWithoutKeyWord, 'topK无关键字'),
            (self.topKTimeWithKeyWord, self.topKWithKeyWord, 'topK有关键字')
        ]
        self.t = DrawThread(target=drawLineChart, args=(data, 'html/showTime.html'))
        self.t.endTrigger.connect(lambda: self.showInWebEngineView(button, '/html/showTime.html'))
        self.t.start()

    # 设置显示时延的菜单
    def setShowQueryTimeMenu(self):
        menuBar = self.menuBar()
        viewMenu = menuBar.addMenu('View')

        showQueryTimeMenu = QAction(QIcon('image/time.png'), 'show time', self)
        showQueryTimeMenu.setShortcut('Ctrl+1')
        showQueryTimeMenu.setStatusTip('显示时延')
        showQueryTimeMenu.triggered.connect(lambda : self.showTime(showQueryTimeMenu))

        viewMenu.addAction(showQueryTimeMenu)

    # 设置打开文件的功能
    def setOpenFileMenu(self):
        menuBar = self.menuBar()
        fileMenu = menuBar.addMenu('File')

        openFile = QAction(QIcon('image/open.png'), 'open file', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('打开文件')
        openFile.triggered.connect(self.showFileDialog)

        fileMenu.addAction(openFile)

    # 显示文件对话框
    def showFileDialog(self):
        # 打开文件,只允许打开'csv'文件
        filePath = QFileDialog.getOpenFileName(self, caption='打开文件', directory='./',
                                               filter='*.csv')
        if filePath[0]:
            file = filePath[0]
            filename = os.path.split(file)[1].split('.')[0]

            savePickle = 'config/' + filename + '.pickle'
            threading.Thread(target=self.openFile, args=(file, savePickle)).start()
            self.setWindowTitle(file)

    # 检查上次打开的这个csv文件修改时间和这次打开的这个csv文件的时间是否一致
    def checkFile(self, file, savePickle):
        savePickleChange = savePickle + 'change'
        with open(savePickleChange, 'rb') as f:
            changeTime = pickle.load(f)
        nowChangeTime = time.localtime(os.stat(file).st_mtime)

        # 如果时间一致,表明两个文件相同, 不一致则需要重新打开
        if changeTime == nowChangeTime:
            return True
        else:
            return False

    # 打开文件
    def openFile(self, file, savePickle):
        self.file = file
        self.savePickle = savePickle
        if os.path.isfile(savePickle) and self.checkFile(file, savePickle):
            # 该文件曾被打开过, 有pickle缓存
            # 该文件没有被修改过
            with open(savePickle, 'rb') as f:
                self.csv_file = pickle.load(f)

            with open(savePickle + 'data', 'rb') as f:
                self.data = pickle.load(f)
        else:
            # 该文件没有被打开过
            # 一是打开它
            # 二是保存该文件的csv类型, 提高下次打开效率
            # 三是保存该文件的最后修改时间,用于下次打开时判断是否被修改过
            self.csv_file = pd.read_csv(file)

            with open(savePickle, 'wb') as f:
                pickle.dump(self.csv_file, f)

            try:
                self.csv_file['AvgScore']
            except:
                self.csv_file['AvgScore'] = 0

            try:
                self.csv_file['Score']
            except:
                self.csv_file['Score'] = ''

            try:
                self.csv_file['Id']
            except:
                self.csv_file['Id'] = list(range(len(self.csv_file)))


            # self.data是每行整合成一行的列表
            csv_file_tmp = self.csv_file.fillna("").astype(str)
            self.data = [" ".join(list(csv_file_tmp.iloc[x])) for x in
                         range(len(csv_file_tmp))]

            with open(savePickle + 'data', 'wb') as f:
                pickle.dump(self.data, f)

            changeTime = time.localtime(os.stat(file).st_mtime)
            savePickleChange = savePickle + 'change'
            with open(savePickleChange, 'wb') as f:
                pickle.dump(changeTime, f)

        self.drawMapButton.setEnabled(True)
        self.drawColorMapButton.setEnabled(True)
        self.countStoreByTimezoneButton_bar.setEnabled(True)
        self.countStoreByTimezoneButton_pie.setEnabled(True)
        self.countStoreByCountryButton_bar.setEnabled(True)
        self.countStoreByCountryButton_pie.setEnabled(True)
        self.findTopKButton.setEnabled(True)
        self.findRangeButton.setEnabled(True)

        self.longitudeEdit.setEnabled(True)
        self.latitudeEdit.setEnabled(True)
        self.kEdit.setEnabled(True)
        self.rangeEdit.setEnabled(True)
        self.keyWordEdit.setEnabled(True)

        kIntValidator = QIntValidator(self)
        kIntValidator.setRange(0, len(self.csv_file))
        self.kEdit.setPlaceholderText("0-" + str(len(self.csv_file)))
        self.kEdit.setStatusTip("k值范围: 0-" + str(len(self.csv_file)))
        self.kEdit.setValidator(kIntValidator)

        self.hasOpenFile = True

    # 窗口居中
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def closeEvent(self, *args, **kwargs):
        super().closeEvent(*args, **kwargs)

        if self.hasOpenFile:

            try:
                self.csv_file = self.csv_file.drop(['TimeZoneCount'], axis=1)
            except:
                pass

            try:
                self.csv_file = self.csv_file.drop(['info'], axis=1)
            except:
                pass

            try:
                self.csv_file = self.csv_file.drop(['Distance'], axis=1)
            except:
                pass

            self.csv_file.to_csv('directory.csv')       # 保存csv_file

            with open(self.savePickle, 'wb') as f:
                pickle.dump(self.csv_file, f)

            # self.data是每行整合成一行的列表
            # csv_file_tmp = self.csv_file.fillna("").astype(str)
            # self.data = [" ".join(list(csv_file_tmp.iloc[x])) for x in
            #              range(len(csv_file_tmp))]
            #
            # with open(self.savePickle + 'data', 'wb') as f:
            #     pickle.dump(self.data, f)

            changeTime = time.localtime(os.stat(self.file).st_mtime)
            savePickleChange = self.savePickle + 'change'
            with open(savePickleChange, 'wb') as f:
                pickle.dump(changeTime, f)