コード例 #1
0
 def hostsMenu(self, menu):
     global Settings
     global Hosts
     global Translations
     if (len(Hosts) <= 0):
         return False
     #
     hostMenu = menu.addMenu(Translations["Menu::Machines"])
     hostGroup = QActionGroup(hostMenu)
     hostGroup.setEnabled(True)
     hostGroup.setExclusive(False)
     hostGroup.triggered.connect(self.doHostTriggered)
     #
     KEYs = Hosts.keys()
     for H in KEYs:
         hostAction = QAction(H, hostMenu)
         hostAction.setData(H)
         hostMenu.addAction(hostAction)
         hostGroup.addAction(hostAction)
     #
     self.Actions["Machines"] = hostMenu
     self.Actions["MachinesGroup"] = hostGroup
     return True
コード例 #2
0
class PangoToolBarWidget(QToolBar):
    del_labels_signal = pyqtSignal(int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setIconSize(QSize(16, 16))
        self.scene = None

        spacer_left = QWidget()
        spacer_left.setFixedWidth(10)
        spacer_middle = QWidget()
        spacer_middle.setFixedWidth(50)
        spacer_right = QWidget()
        spacer_right.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Preferred)

        # Label Related
        self.color_display = QLabel()
        self.color_display.setFixedSize(QSize(50, 20))
        self.label_select = self.LabelSelect(self.color_display)
        self.label_select.lineEdit().returnPressed.connect(self.add)

        self.add_action = QAction("Add")
        self.add_action.triggered.connect(self.add)
        icon = pango_get_icon("add")
        self.add_action.setIcon(icon)

        self.del_action = QAction("Delete")
        self.del_action.triggered.connect(self.delete)
        icon = pango_get_icon("del")
        self.del_action.setIcon(icon)

        self.color_action = QAction("Palette")
        self.color_action.triggered.connect(self.set_color)
        icon = pango_get_icon("palette")
        self.color_action.setIcon(icon)

        # Tool Related
        self.size_select = QSpinBox()
        self.size_select.valueChanged.connect(self.set_tool_size)

        self.size_select.setSuffix("px")
        self.size_select.setRange(1, 99)
        self.size_select.setSingleStep(5)
        self.size_select.setValue(10)

        self.pan_action = QAction("Pan")
        self.lasso_action = QAction("Lasso")
        self.path_action = QAction("Path")
        self.bbox_action = QAction("Bbox")
        self.poly_action = QAction("Poly")

        self.action_group = QActionGroup(self)
        self.action_group.setExclusive(True)
        self.action_group.triggered.connect(self.set_tool)

        self.action_group.addAction(self.pan_action)
        self.action_group.addAction(self.lasso_action)
        self.action_group.addAction(self.bbox_action)
        self.action_group.addAction(self.poly_action)
        self.action_group.addAction(self.path_action)

        for action in self.action_group.actions():
            icon = pango_get_icon(action.text())
            action.setIcon(icon)
            action.setCheckable(True)

        # Other
        font = QFont("Arial", 10)
        self.info_display = QLabel()
        self.info_display.setFixedWidth(100)
        self.info_display.setFont(font)

        self.coord_display = QLabel()
        self.coord_display.setFixedWidth(40)
        self.coord_display.setFont(font)

        # Layouts
        self.addWidget(spacer_left)
        self.addWidget(self.color_display)
        self.addWidget(self.label_select)
        self.addAction(self.add_action)
        self.addAction(self.del_action)
        self.addAction(self.color_action)

        self.addWidget(spacer_middle)
        self.addActions(self.action_group.actions())
        self.addWidget(self.size_select)

        self.addWidget(spacer_right)
        self.addWidget(self.info_display)
        self.addWidget(self.coord_display)

        self.size_select.setEnabled(False)

        self.del_action.setEnabled(False)
        self.action_group.setEnabled(False)
        self.color_action.setEnabled(False)

    def set_color(self):
        dialog = QColorDialog()
        dialog.setOption(QColorDialog.ShowAlphaChannel, False)
        color = dialog.getColor()
        if color == QColor():
            return

        row = self.label_select.currentIndex()
        label = self.label_select.model().item(row, 0)
        label.color = color
        label.set_icon()
        for i in range(0, label.rowCount()):
            shape = label.child(i)
            shape.set_icon()

        # Refresh label (for scene reticle etc.)
        self.label_select.setCurrentIndex(0)
        self.label_select.setCurrentIndex(row)
        self.label_select.color_display.update()

    def add(self):
        self.del_action.setEnabled(True)
        self.color_action.setEnabled(True)
        if self.scene.fpath is not None:
            self.action_group.setEnabled(True)

        item = PangoLabelItem()
        root = self.label_select.model().invisibleRootItem()
        root.appendRow(item)
        item.name = "Unnamed Label " + str(item.row())
        item.visible = True
        item.color = pango_get_palette(item.row())
        item.set_icon()

        bottom_row = self.label_select.model().rowCount() - 1
        self.label_select.setCurrentIndex(bottom_row)
        if bottom_row == 0:
            self.label_select.currentIndexChanged.emit(
                self.label_select.currentIndex())

    def delete(self):
        self.reset_tool()
        self.del_labels_signal.emit(self.label_select.currentIndex())

        if self.label_select.model().rowCount() == 0:
            self.del_action.setEnabled(False)
            self.action_group.setEnabled(False)
            self.color_action.setEnabled(False)

    def set_tool(self, action):
        if self.scene is None:
            return

        self.scene.tool = action.text()
        self.scene.reset_com()
        self.scene.reticle.setVisible(self.scene.tool == "Path")
        self.scene.views()[0].set_cursor(self.scene.tool)

        if action.text() == "Path" or action.text() == "Filled Path":
            self.size_select.setEnabled(True)
        else:
            self.size_select.setEnabled(False)

    def reset_tool(self):
        self.lasso_action.setChecked(True)
        self.set_tool(self.lasso_action)

    def set_tool_size(self, size, additive=False):
        if self.scene is None:
            return

        if additive:
            self.scene.tool_size += size
        else:
            self.scene.tool_size = size
        self.scene.reset_com()

        self.scene.reticle.setRect(-size / 2, -size / 2, size, size)
        if self.size_select.value() != self.scene.tool_size:
            self.size_select.setValue(self.scene.tool_size)

        if not self.scene.sceneRect().contains(self.scene.reticle.pos()):
            view = self.scene.views()[0]
            x = self.size_select.geometry().center().x()
            y = view.rect().top() + size / 2
            self.scene.reticle.setPos(view.mapToScene(QPoint(x, y)))

    def set_scene(self, scene):
        if self.scene is not None:
            self.scene.clear_tool.disconnect(self.reset_tool)

        self.scene = scene
        self.scene.clear_tool.connect(self.reset_tool)
        self.reset_tool()

    class LabelSelect(QComboBox):
        def __init__(self, color_display, parent=None):
            super().__init__(parent)
            self.color_display = color_display
            self.setFixedWidth(150)
            self.setEditable(True)

            self.editTextChanged.connect(self.edit_text_changed)

        def paintEvent(self, event):
            super().paintEvent(event)
            item = self.model().item(self.currentIndex())
            if item is not None and item.color is not None:
                self.color_display.setStyleSheet(
                    "QLabel { background-color : " + item.color.name() + "}")
            else:
                self.color_display.setStyleSheet(
                    "QLabel { background-color : rgba(255, 255, 255, 10)}")

        def edit_text_changed(self, text):
            row = self.currentIndex()
            self.setItemData(row, text, Qt.DisplayRole)

        def select_next_label(self):
            idx = self.currentIndex() + 1
            if idx > -1 and idx < self.count():
                self.setCurrentIndex(idx)

        def select_prev_label(self):
            idx = self.currentIndex() - 1
            if idx > -1 and idx < self.count():
                self.setCurrentIndex(idx)
コード例 #3
0
ファイル: Devices.py プロジェクト: inspiredbylife/tdm
class ListWidget(QWidget):
    deviceSelected = pyqtSignal(TasmotaDevice)
    openRulesEditor = pyqtSignal()
    openConsole = pyqtSignal()
    openTelemetry = pyqtSignal()
    openWebUI = pyqtSignal()

    def __init__(self, parent, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)
        self.setWindowTitle("Devices list")
        self.setWindowState(Qt.WindowMaximized)
        self.setLayout(VLayout(margin=0, spacing=0))

        self.mqtt = parent.mqtt
        self.env = parent.env

        self.device = None
        self.idx = None

        self.nam = QNetworkAccessManager()
        self.backup = bytes()

        self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat)
        views_order = self.settings.value("views_order", [])

        self.views = {}
        self.settings.beginGroup("Views")
        views = self.settings.childKeys()
        if views and views_order:
            for view in views_order.split(";"):
                view_list = self.settings.value(view).split(";")
                self.views[view] = base_view + view_list
        else:
            self.views = default_views
        self.settings.endGroup()

        self.tb = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonTextBesideIcon)
        self.tb_relays = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonIconOnly)
        # self.tb_filter = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonTextBesideIcon)
        self.tb_views = Toolbar(Qt.Horizontal, 24, Qt.ToolButtonTextBesideIcon)

        self.pwm_sliders = []

        self.layout().addWidget(self.tb)
        self.layout().addWidget(self.tb_relays)
        # self.layout().addWidget(self.tb_filter)

        self.device_list = TableView()
        self.device_list.setIconSize(QSize(24, 24))
        self.model = parent.device_model
        self.model.setupColumns(self.views["Home"])

        self.sorted_device_model = QSortFilterProxyModel()
        self.sorted_device_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.sorted_device_model.setSourceModel(parent.device_model)
        self.sorted_device_model.setSortRole(Qt.InitialSortOrderRole)
        self.sorted_device_model.setFilterKeyColumn(-1)

        self.device_list.setModel(self.sorted_device_model)
        self.device_list.setupView(self.views["Home"])
        self.device_list.setSortingEnabled(True)
        self.device_list.setWordWrap(True)
        self.device_list.setItemDelegate(DeviceDelegate())
        self.device_list.sortByColumn(self.model.columnIndex("FriendlyName"), Qt.AscendingOrder)
        self.device_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.device_list.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.layout().addWidget(self.device_list)

        self.layout().addWidget(self.tb_views)

        self.device_list.clicked.connect(self.select_device)
        self.device_list.customContextMenuRequested.connect(self.show_list_ctx_menu)

        self.ctx_menu = QMenu()
        self.ctx_menu_relays = None

        self.create_actions()
        self.create_view_buttons()
        # self.create_view_filter()

        self.device_list.doubleClicked.connect(lambda: self.openConsole.emit())

    def create_actions(self):
        self.ctx_menu_cfg = QMenu("Configure")
        self.ctx_menu_cfg.setIcon(QIcon("GUI/icons/settings.png"))
        self.ctx_menu_cfg.addAction("Module", self.configureModule)
        self.ctx_menu_cfg.addAction("GPIO", self.configureGPIO)
        self.ctx_menu_cfg.addAction("Template", self.configureTemplate)
        # self.ctx_menu_cfg.addAction("Wifi", self.ctx_menu_teleperiod)
        # self.ctx_menu_cfg.addAction("Time", self.cfgTime.emit)
        # self.ctx_menu_cfg.addAction("MQTT", self.ctx_menu_teleperiod)

        # self.ctx_menu_cfg.addAction("Logging", self.ctx_menu_teleperiod)

        self.ctx_menu.addMenu(self.ctx_menu_cfg)
        self.ctx_menu.addSeparator()

        self.ctx_menu.addAction(QIcon("GUI/icons/refresh.png"), "Refresh", self.ctx_menu_refresh)

        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon("GUI/icons/clear.png"), "Clear retained", self.ctx_menu_clear_retained)
        self.ctx_menu.addAction("Clear Backlog", self.ctx_menu_clear_backlog)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon("GUI/icons/copy.png"), "Copy", self.ctx_menu_copy)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon("GUI/icons/restart.png"), "Restart", self.ctx_menu_restart)
        self.ctx_menu.addAction(QIcon(), "Reset", self.ctx_menu_reset)
        self.ctx_menu.addSeparator()
        self.ctx_menu.addAction(QIcon("GUI/icons/delete.png"), "Delete", self.ctx_menu_delete_device)

        console = self.tb.addAction(QIcon("GUI/icons/console.png"), "Console", self.openConsole.emit)
        console.setShortcut("Ctrl+E")

        rules = self.tb.addAction(QIcon("GUI/icons/rules.png"), "Rules", self.openRulesEditor.emit)
        rules.setShortcut("Ctrl+R")

        self.tb.addAction(QIcon("GUI/icons/timers.png"), "Timers", self.configureTimers)

        buttons = self.tb.addAction(QIcon("GUI/icons/buttons.png"), "Buttons", self.configureButtons)
        buttons.setShortcut("Ctrl+B")

        switches = self.tb.addAction(QIcon("GUI/icons/switches.png"), "Switches", self.configureSwitches)
        switches.setShortcut("Ctrl+S")

        power = self.tb.addAction(QIcon("GUI/icons/power.png"), "Power", self.configurePower)
        power.setShortcut("Ctrl+P")

        # setopts = self.tb.addAction(QIcon("GUI/icons/setoptions.png"), "SetOptions", self.configureSO)
        # setopts.setShortcut("Ctrl+S")

        self.tb.addSpacer()

        telemetry = self.tb.addAction(QIcon("GUI/icons/telemetry.png"), "Telemetry", self.openTelemetry.emit)
        telemetry.setShortcut("Ctrl+T")

        webui = self.tb.addAction(QIcon("GUI/icons/web.png"), "WebUI", self.openWebUI.emit)
        webui.setShortcut("Ctrl+U")

        # self.tb.addAction(QIcon(), "Multi Command", self.ctx_menu_webui)

        self.agAllPower = QActionGroup(self)
        self.agAllPower.addAction(QIcon("GUI/icons/P_ON.png"), "All ON")
        self.agAllPower.addAction(QIcon("GUI/icons/P_OFF.png"), "All OFF")
        self.agAllPower.setEnabled(False)
        self.agAllPower.setExclusive(False)
        self.agAllPower.triggered.connect(self.toggle_power_all)
        self.tb_relays.addActions(self.agAllPower.actions())

        self.agRelays = QActionGroup(self)
        self.agRelays.setVisible(False)
        self.agRelays.setExclusive(False)

        for a in range(1, 9):
            act = QAction(QIcon("GUI/icons/P{}_OFF.png".format(a)), "")
            act.setShortcut("F{}".format(a))
            self.agRelays.addAction(act)

        self.agRelays.triggered.connect(self.toggle_power)
        self.tb_relays.addActions(self.agRelays.actions())

        self.tb_relays.addSeparator()
        self.actColor = self.tb_relays.addAction(QIcon("GUI/icons/color.png"), "Color", self.set_color)
        self.actColor.setEnabled(False)

        self.actChannels = self.tb_relays.addAction(QIcon("GUI/icons/sliders.png"), "Channels")
        self.actChannels.setEnabled(False)
        self.mChannels = QMenu()
        self.actChannels.setMenu(self.mChannels)
        self.tb_relays.widgetForAction(self.actChannels).setPopupMode(QToolButton.InstantPopup)

    def create_view_buttons(self):
        self.tb_views.addWidget(QLabel("View mode: "))
        ag_views = QActionGroup(self)
        ag_views.setExclusive(True)
        for v in self.views.keys():
            a = QAction(v)
            a.triggered.connect(self.change_view)
            a.setCheckable(True)
            ag_views.addAction(a)
        self.tb_views.addActions(ag_views.actions())
        ag_views.actions()[0].setChecked(True)

        stretch = QWidget()
        stretch.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.tb_views.addWidget(stretch)
        # actEditView = self.tb_views.addAction("Edit views...")

    # def create_view_filter(self):
    #     # self.tb_filter.addWidget(QLabel("Show devices: "))
    #     # self.cbxLWT = QComboBox()
    #     # self.cbxLWT.addItems(["All", "Online"d, "Offline"])
    #     # self.cbxLWT.currentTextChanged.connect(self.build_filter_regex)
    #     # self.tb_filter.addWidget(self.cbxLWT)
    #
    #     self.tb_filter.addWidget(QLabel(" Search: "))
    #     self.leSearch = QLineEdit()
    #     self.leSearch.setClearButtonEnabled(True)
    #     self.leSearch.textChanged.connect(self.build_filter_regex)
    #     self.tb_filter.addWidget(self.leSearch)
    #
    # def build_filter_regex(self, txt):
    #     query = self.leSearch.text()
    #     # if self.cbxLWT.currentText() != "All":
    #     #     query = "{}|{}".format(self.cbxLWT.currentText(), query)
    #     self.sorted_device_model.setFilterRegExp(query)

    def change_view(self, a=None):
        view = self.views[self.sender().text()]
        self.model.setupColumns(view)
        self.device_list.setupView(view)

    def ctx_menu_copy(self):
        if self.idx:
            string = dumps(self.model.data(self.idx))
            if string.startswith('"') and string.endswith('"'):
                string = string[1:-1]
            QApplication.clipboard().setText(string)

    def ctx_menu_clear_retained(self):
        if self.device:
            relays = self.device.power()
            if relays and len(relays.keys()) > 0:
                for r in relays.keys():
                    self.mqtt.publish(self.device.cmnd_topic(r), retain=True)
            QMessageBox.information(self, "Clear retained", "Cleared retained messages.")

    def ctx_menu_clear_backlog(self):
        if self.device:
            self.mqtt.publish(self.device.cmnd_topic("backlog"), "")
            QMessageBox.information(self, "Clear Backlog", "Backlog cleared.")

    def ctx_menu_restart(self):
        if self.device:
            self.mqtt.publish(self.device.cmnd_topic("restart"), payload="1")
            for k in list(self.device.power().keys()):
                self.device.p.pop(k)

    def ctx_menu_reset(self):
        if self.device:
            reset, ok = QInputDialog.getItem(self, "Reset device and restart", "Select reset mode", resets, editable=False)
            if ok:
                self.mqtt.publish(self.device.cmnd_topic("reset"), payload=reset.split(":")[0])
                for k in list(self.device.power().keys()):
                    self.device.p.pop(k)

    def ctx_menu_refresh(self):
        if self.device:
            for k in list(self.device.power().keys()):
                self.device.p.pop(k)

            for c in initial_commands():
                cmd, payload = c
                cmd = self.device.cmnd_topic(cmd)
                self.mqtt.publish(cmd, payload, 1)

    def ctx_menu_delete_device(self):
        if self.device:
            if QMessageBox.question(self, "Confirm", "Do you want to remove the following device?\n'{}' ({})"
                    .format(self.device.p['FriendlyName1'], self.device.p['Topic'])) == QMessageBox.Yes:
                self.model.deleteDevice(self.idx)

    def ctx_menu_teleperiod(self):
        if self.device:
            teleperiod, ok = QInputDialog.getInt(self, "Set telemetry period", "Input 1 to reset to default\n[Min: 10, Max: 3600]", self.device.p['TelePeriod'], 1, 3600)
            if ok:
                if teleperiod != 1 and teleperiod < 10:
                    teleperiod = 10
            self.mqtt.publish(self.device.cmnd_topic("teleperiod"), teleperiod)

    def ctx_menu_config_backup(self):
        if self.device:
            self.backup = bytes()
            self.dl = self.nam.get(QNetworkRequest(QUrl("http://{}/dl".format(self.device.p['IPAddress']))))
            self.dl.readyRead.connect(self.get_dump)
            self.dl.finished.connect(self.save_dump)

    def ctx_menu_ota_set_url(self):
        if self.device:
            url, ok = QInputDialog.getText(self, "Set OTA URL", '100 chars max. Set to "1" to reset to default.', text=self.device.p['OtaUrl'])
            if ok:
                self.mqtt.publish(self.device.cmnd_topic("otaurl"), payload=url)

    def ctx_menu_ota_set_upgrade(self):
        if self.device:
            if QMessageBox.question(self, "OTA Upgrade", "Are you sure to OTA upgrade from\n{}".format(self.device.p['OtaUrl']), QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
                self.mqtt.publish(self.device.cmnd_topic("upgrade"), payload="1")

    def show_list_ctx_menu(self, at):
        self.select_device(self.device_list.indexAt(at))
        self.ctx_menu.popup(self.device_list.viewport().mapToGlobal(at))

    def select_device(self, idx):
        self.idx = self.sorted_device_model.mapToSource(idx)
        self.device = self.model.deviceAtRow(self.idx.row())
        self.deviceSelected.emit(self.device)

        relays = self.device.power()

        self.agAllPower.setEnabled(len(relays) >= 1)

        for i, a in enumerate(self.agRelays.actions()):
            a.setVisible(len(relays) > 1 and i < len(relays))

        color = self.device.color().get("Color", False)
        has_color = bool(color)
        self.actColor.setEnabled(has_color and not self.device.setoption(68))

        self.actChannels.setEnabled(has_color)

        if has_color:
            self.actChannels.menu().clear()

            max_val = 100
            if self.device.setoption(15) == 0:
                max_val = 1023

            for k, v in self.device.pwm().items():
                channel = SliderAction(self, k)
                channel.slider.setMaximum(max_val)
                channel.slider.setValue(int(v))
                self.mChannels.addAction(channel)
                channel.slider.valueChanged.connect(self.set_channel)

            dimmer = self.device.color().get("Dimmer")
            if dimmer:
                saDimmer = SliderAction(self, "Dimmer")
                saDimmer.slider.setValue(int(dimmer))
                self.mChannels.addAction(saDimmer)
                saDimmer.slider.valueChanged.connect(self.set_channel)

    def toggle_power(self, action):
        if self.device:
            idx = self.agRelays.actions().index(action)
            relay = list(self.device.power().keys())[idx]
            self.mqtt.publish(self.device.cmnd_topic(relay), "toggle")

    def toggle_power_all(self, action):
        if self.device:
            idx = self.agAllPower.actions().index(action)
            for r in self.device.power().keys():
                self.mqtt.publish(self.device.cmnd_topic(r), str(not bool(idx)))

    def set_color(self):
        if self.device:
            color = self.device.color().get("Color")
            if color:
                dlg = QColorDialog()
                new_color = dlg.getColor(QColor("#{}".format(color)))
                if new_color.isValid():
                    new_color = new_color.name()
                    if new_color != color:
                        self.mqtt.publish(self.device.cmnd_topic("color"), new_color)

    def set_channel(self, value=0):
        cmd = self.sender().objectName()

        if self.device:
            self.mqtt.publish(self.device.cmnd_topic(cmd), str(value))

    def configureSO(self):
        if self.device:
            dlg = SetOptionsDialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureModule(self):
        if self.device:
            dlg = ModuleDialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureGPIO(self):
        if self.device:
            dlg = GPIODialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureTemplate(self):
        if self.device:
            dlg = TemplateDialog(self.device)
            dlg.sendCommand.connect(self.mqtt.publish)
            dlg.exec_()

    def configureTimers(self):
        if self.device:
            self.mqtt.publish(self.device.cmnd_topic("timers"))
            timers = TimersDialog(self.device)
            self.mqtt.messageSignal.connect(timers.parseMessage)
            timers.sendCommand.connect(self.mqtt.publish)
            timers.exec_()

    def configureButtons(self):
        if self.device:
            backlog = []
            buttons = ButtonsDialog(self.device)
            if buttons.exec_() == QDialog.Accepted:
                for c, cw in buttons.command_widgets.items():
                    current_value = self.device.p.get(c)
                    new_value = ""

                    if isinstance(cw.input, SpinBox):
                        new_value = cw.input.value()

                    if isinstance(cw.input, QComboBox):
                        new_value = cw.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("{} {}".format(c, new_value))

                for so, sow in buttons.setoption_widgets.items():
                    current_value = self.device.setoption(so)
                    new_value = -1

                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("SetOption{} {}".format(so, new_value))

                if backlog:
                    backlog.append("status 3")
                    self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def configureSwitches(self):
        if self.device:
            backlog = []
            switches = SwitchesDialog(self.device)
            if switches.exec_() == QDialog.Accepted:
                for c, cw in switches.command_widgets.items():
                    current_value = self.device.p.get(c)
                    new_value = ""

                    if isinstance(cw.input, SpinBox):
                        new_value = cw.input.value()

                    if isinstance(cw.input, QComboBox):
                        new_value = cw.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("{} {}".format(c, new_value))

                for so, sow in switches.setoption_widgets.items():
                    current_value = self.device.setoption(so)
                    new_value = -1

                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("SetOption{} {}".format(so, new_value))

                for sw, sw_mode in enumerate(self.device.p['SwitchMode']):
                    new_value = switches.sm.inputs[sw].currentIndex()

                    if sw_mode != new_value:
                        backlog.append("switchmode{} {}".format(sw+1, new_value))

                if backlog:
                    backlog.append("status")
                    backlog.append("status 3")
                self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def configurePower(self):
        if self.device:
            backlog = []
            power = PowerDialog(self.device)
            if power.exec_() == QDialog.Accepted:
                for c, cw in power.command_widgets.items():
                    current_value = self.device.p.get(c)
                    new_value = ""

                    if isinstance(cw.input, SpinBox):
                        new_value = cw.input.value()

                    if isinstance(cw.input, QComboBox):
                        new_value = cw.input.currentIndex()

                    if current_value != new_value:
                        backlog.append("{} {}".format(c, new_value))

                for so, sow in power.setoption_widgets.items():
                    new_value = -1

                    if isinstance(sow.input, SpinBox):
                        new_value = sow.input.value()

                    if isinstance(sow.input, QComboBox):
                        new_value = sow.input.currentIndex()

                    if new_value != self.device.setoption(so):
                        backlog.append("SetOption{} {}".format(so, new_value))

                new_interlock_value = power.ci.input.currentData()
                new_interlock_grps = " ".join([grp.text().replace(" ", "") for grp in power.ci.groups]).rstrip()

                if new_interlock_value != self.device.p.get("Interlock", "OFF"):
                    backlog.append("interlock {}".format(new_interlock_value))

                if new_interlock_grps != self.device.p.get("Groups", ""):
                    backlog.append("interlock {}".format(new_interlock_grps))

                for i, pt in enumerate(power.cpt.inputs):
                    ptime = "PulseTime{}".format(i+1)
                    current_ptime = self.device.p.get(ptime)
                    if current_ptime:
                        current_value = list(current_ptime.keys())[0]
                        new_value = str(pt.value())

                        if new_value != current_value:
                            backlog.append("{} {}".format(ptime, new_value))

                if backlog:
                    backlog.append("status")
                    backlog.append("status 3")
                    self.mqtt.publish(self.device.cmnd_topic("backlog"), "; ".join(backlog))

    def get_dump(self):
        self.backup += self.dl.readAll()

    def save_dump(self):
        fname = self.dl.header(QNetworkRequest.ContentDispositionHeader)
        if fname:
            fname = fname.split('=')[1]
            save_file = QFileDialog.getSaveFileName(self, "Save config backup", "{}/TDM/{}".format(QDir.homePath(), fname))[0]
            if save_file:
                with open(save_file, "wb") as f:
                    f.write(self.backup)

    def check_fulltopic(self, fulltopic):
        fulltopic += "/" if not fulltopic.endswith('/') else ''
        return "%prefix%" in fulltopic and "%topic%" in fulltopic

    def closeEvent(self, event):
        event.ignore()
コード例 #4
0
ファイル: sniffer.py プロジェクト: tcqefjm/Sniffer
class MainGui(QMainWindow):
	def __init__(self):
		self.sniffer=Sniffer()
		self.sniffer.interface,ok=QInputDialog.getItem(QWidget(),'Sniffer','Welcome!\n\nChoose Interface:',\
														self.sniffer.interfaces,0,False)
		if ok:
			super().__init__()
			self.initUI()
			self.reassembler=Reassembler()
		else:
			exit()

	def initUI(self):
		'''Define action'''
		self.open_act=QAction(QIcon('./icons/open.png'),'Open',self)
		self.open_act.setShortcut('Ctrl+O')
		self.open_act.triggered.connect(self.OpenFile)
		self.save_act=QAction(QIcon('./icons/save.png'),'Save',self)
		self.save_act.setShortcut('Ctrl+S')
		self.save_act.triggered.connect(self.SaveFile)
		self.save_act.setEnabled(False)
		self.quit_act=QAction(QIcon('./icons/quit.png'),'Quit',self)
		self.quit_act.setShortcut('Ctrl+Q')
		self.quit_act.triggered.connect(self.close)
		self.filter_find_act=QAction(QIcon('./icons/filter&find.png'),'Filter and Find',self)
		self.filter_find_act.setShortcut('Ctrl+F')
		self.filter_find_act.triggered.connect(self.FilterPackets)
		self.filter_find_act.setEnabled(False)
		self.reassemble_act=QAction(QIcon('./icons/reassemble.png'),'Reassemble',self)
		self.reassemble_act.setShortcut('Ctrl+Alt+R')
		self.reassemble_act.triggered.connect(self.BrowseReassembly)
		self.reassemble_act.setEnabled(False)
		self.start_act=QAction(QIcon('./icons/start.png'),'Start',self)
		self.start_act.setShortcut('Ctrl+E')
		self.start_act.triggered.connect(self.StartCapture)
		self.stop_act=QAction(QIcon('./icons/stop.png'),'Stop',self)
		self.stop_act.setShortcut('Ctrl+E')
		self.stop_act.triggered.connect(self.StopCapture)
		self.stop_act.setEnabled(False)
		self.iface_act_group=QActionGroup(self)
		self.iface_act_group.triggered.connect(self.UpdateInterface)

		self.status_label=QLabel()
		self.statusBar().addPermanentWidget(self.status_label)
		self.statusBar().showMessage('Ready to capture')

		'''Define menu'''
		menubar=self.menuBar()
		file_menu=menubar.addMenu('&File')
		analyze_menu=menubar.addMenu('&Analyze')
		interface_menu=menubar.addMenu('&Interface')
		file_menu.addAction(self.open_act)
		file_menu.addAction(self.save_act)
		file_menu.addAction(self.quit_act)
		analyze_menu.addAction(self.filter_find_act)
		analyze_menu.addAction(self.reassemble_act)

		'''Interface choose menu'''
		for iface in self.sniffer.interfaces:
			iface_act=QAction(iface,self,checkable=True)
			if iface==self.sniffer.interface:
				iface_act.setChecked(True)
			self.iface_act_group.addAction(iface_act)
			interface_menu.addAction(iface_act)

		toolbar=self.addToolBar('Toolbar')
		toolbar.setMovable(False)
		toolbar.addAction(self.start_act)
		toolbar.addAction(self.stop_act)
		toolbar.addSeparator()
		toolbar.addAction(self.open_act)
		toolbar.addAction(self.save_act)
		toolbar.addSeparator()
		toolbar.addAction(self.filter_find_act)
		toolbar.addAction(self.reassemble_act)

		'''Main region for filter and find function

		filter fields include source address and port, destination address and port, protocol, and a search field
		'''
		filter_label=QLabel('Filter:')
		source_label=QLabel('Source')
		destination_label=QLabel('Destination')
		protocol_label=QLabel('Protocol')
		find_label=QLabel('	Find:')
		self.source_edit=QLineEdit()
		self.sport_edit=QLineEdit()
		self.destination_edit=QLineEdit()
		self.dport_edit=QLineEdit()
		self.protocol_edit=QLineEdit()
		self.find_edit=QLineEdit()
		self.source_edit.setClearButtonEnabled(True)
		self.sport_edit.setClearButtonEnabled(True)
		self.destination_edit.setClearButtonEnabled(True)
		self.dport_edit.setClearButtonEnabled(True)
		self.protocol_edit.setClearButtonEnabled(True)
		self.find_edit.setClearButtonEnabled(True)

		self.protocol_completer=QCompleter()
		self.protocol_completer.setCaseSensitivity(False)
		self.protocol_edit.setCompleter(self.protocol_completer)

		self.source_edit.textChanged.connect(self.UpdateSourceFilter)
		self.sport_edit.textChanged.connect(self.UpdateSportFilter)
		self.destination_edit.textChanged.connect(self.UpdateDestinationFilter)
		self.dport_edit.textChanged.connect(self.UpdateDportFilter)
		self.protocol_edit.textChanged.connect(self.UpdateProtocolFilter)
		self.find_edit.textChanged.connect(self.UpdateFindFilter)

		'''Main region to display captured or filtered packets'''
		self.packet_table=QTableWidget()
		self.packet_table.setEditTriggers(QTableWidget.NoEditTriggers)
		self.packet_table.setSelectionBehavior(QTableWidget.SelectRows)
		self.packet_table.setSelectionMode(QTableWidget.SingleSelection)
		self.packet_table.setShowGrid(False)
		self.packet_table.setMinimumHeight(80)
		self.packet_table.itemSelectionChanged.connect(self.BrowseSelectedPacket)
		self.packet_table.verticalHeader().hide()
		self.packet_table.verticalHeader().setDefaultSectionSize(25)
		self.packet_table.setColumnCount(7)
		self.packet_table.setHorizontalHeaderLabels(['No.','Time','Source','Destination','Protocol','Length','Info'])
		self.packet_table.setColumnWidth(0,60)
		self.packet_table.setColumnWidth(1,120)
		self.packet_table.setColumnWidth(2,160)
		self.packet_table.setColumnWidth(3,160)
		self.packet_table.setColumnWidth(4,100)
		self.packet_table.setColumnWidth(5,60)
		self.packet_table.setColumnWidth(6,320)
		self.packet_table.horizontalHeader().setStretchLastSection(True)

		'''Main region to browse detailed information about packet'''
		self.details_browser=QTextBrowser()
		self.bytes_browser=QTextBrowser()
		self.details_browser.setMinimumHeight(80)
		self.bytes_browser.setMinimumHeight(80)

		horizontal_splitter=QSplitter(Qt.Horizontal)
		vertical_splitter=QSplitter(Qt.Vertical)
		horizontal_splitter.addWidget(self.details_browser)
		horizontal_splitter.addWidget(self.bytes_browser)
		vertical_splitter.addWidget(self.packet_table)
		vertical_splitter.addWidget(horizontal_splitter)
		vertical_splitter.setSizes([1,1])

		'''Design mainwindow layout'''
		grid=QGridLayout()
		grid.addWidget(filter_label,0,0,1,1)
		grid.addWidget(source_label,0,1,1,1)
		grid.addWidget(self.source_edit,0,2,1,1)
		grid.addWidget(self.sport_edit,0,3,1,1)
		grid.addWidget(destination_label,0,4,1,1)
		grid.addWidget(self.destination_edit,0,5,1,1)
		grid.addWidget(self.dport_edit,0,6,1,1)
		grid.addWidget(protocol_label,0,7,1,1)
		grid.addWidget(self.protocol_edit,0,8,1,1)
		grid.addWidget(find_label,0,9,1,1)
		grid.addWidget(self.find_edit,0,10,1,1)
		grid.addWidget(vertical_splitter,1,0,1,11)
		grid.setColumnStretch(2,3)
		grid.setColumnStretch(3,1)
		grid.setColumnStretch(5,3)
		grid.setColumnStretch(6,1)
		grid.setColumnStretch(8,3)
		grid.setColumnStretch(10,2)

		central=QWidget()
		self.setCentralWidget(central)
		central.setLayout(grid)

		self.setGeometry(100,60,1120,630)
		self.setWindowIcon(QIcon('./icons/sniffer.png'))
		self.setWindowTitle('Sniffer')
		self.show()

	def closeEvent(self,event):
		'''Override default closeEvent of MainWindow

		This is for checking before quit, also finish up jobs before quit
		'''
		reply=QMessageBox.question(self,'Quit','Are you sure?',
						QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes)
		if reply==QMessageBox.Yes:
			self.sniffer.capturing=False
			self.reassembler.close()
			event.accept()
		else:
			event.ignore()

	def UpdateInterface(self,iface_act):
		'''When selection in interface_menu changes, update interface'''
		self.sniffer.interface=iface_act.text()

	def StartCapture(self):
		'''Start capturing packets'''
		if not self.sniffer.capturing:
			self.sniffer.Sniff()
			self.packet_table.setRowCount(0)
			self.statusBar().showMessage(self.sniffer.interface+': live capture in progress')
			self.status_label.clear()
			self.open_act.setEnabled(False)
			self.save_act.setEnabled(False)
			self.filter_find_act.setEnabled(False)
			self.start_act.setEnabled(False)
			self.stop_act.setEnabled(True)
			self.iface_act_group.setEnabled(False)

	def StopCapture(self):
		'''Stop capturing packets'''
		self.sniffer.StopSniffing()
		self.statusBar().showMessage('Ready to capture')
		if self.sniffer.total_number:
			self.status_label.setText('Packets: {} · Displayed: {} ({:.1%})'.format(self.sniffer.total_number,\
				len(self.sniffer.packet_filter.packet_list),len(self.sniffer.packet_filter.packet_list)/self.sniffer.total_number))
		self.open_act.setEnabled(True)
		self.save_act.setEnabled(True)
		self.filter_find_act.setEnabled(True)
		self.start_act.setEnabled(True)
		self.stop_act.setEnabled(False)
		self.iface_act_group.setEnabled(True)

	def OpenFile(self):
		'''Open a pcap file'''
		file=QFileDialog.getOpenFileName(self,'Open Packets','./','pcap(*.pcap)')[0]
		if file:
			self.packet_table.setRowCount(0)
			self.statusBar().showMessage('Loading: '+file)
			self.sniffer.OpenPackets(file)
			self.statusBar().showMessage('Ready to capture')
			if self.sniffer.total_number:
				self.status_label.setText('Packets: {} · Displayed: {} ({:.1%})'.format(self.sniffer.total_number,\
					len(self.sniffer.packet_filter.packet_list),len(self.sniffer.packet_filter.packet_list)/self.sniffer.total_number))
			self.save_act.setEnabled(True)
			self.filter_find_act.setEnabled(True)

	def SaveFile(self):
		'''Save packets as unreadable pcap or readable txt'''
		file=QFileDialog.getSaveFileName(self,'Save Packets','./','unreadable(*.pcap);;readable(*.txt)')[0]
		if file:
			self.sniffer.SavePackets(file)

	def FilterPackets(self):
		'''Filter packets and display filtered packets'''
		self.sniffer.packet_filter.FilterPackets()

		self.packet_table.setRowCount(0)
		for row_number,packet_tuple in enumerate(self.sniffer.packet_filter.packet_list):
			self.packet_table.insertRow(row_number)
			for column in range(7):
				self.packet_table.setItem(row_number,column,QTableWidgetItem(packet_tuple[column]))
				self.packet_table.item(row_number,column).setBackground(packet_tuple[7])
				self.packet_table.item(row_number,column).setForeground(packet_tuple[8])
		self.packet_table.scrollToBottom()

		if self.sniffer.total_number:
			self.status_label.setText('Packets: {} · Displayed: {} ({:.1%})'.format(self.sniffer.total_number,\
				len(self.sniffer.packet_filter.packet_list),len(self.sniffer.packet_filter.packet_list)/self.sniffer.total_number))

	'''Following functions update filter string when text in the filter and find region changes

	Also, inputs are checked if valid, and background change as a prompt
	'''
	def UpdateSourceFilter(self):
		self.sniffer.packet_filter.src_filter=self.source_edit.text()
		self.sniffer.packet_filter.src_filter_enable=\
							bool(match(self.sniffer.packet_filter.address_re,self.sniffer.packet_filter.src_filter))
		if self.sniffer.packet_filter.src_filter=='':
			self.source_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 255, 255) }')
		elif self.sniffer.packet_filter.src_filter_enable:
			self.source_edit.setStyleSheet('QLineEdit { background-color: rgb(175, 255, 175) }')
		else:
			self.source_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 175, 175) }')

	def UpdateSportFilter(self):
		self.sniffer.packet_filter.sport_filter=self.sport_edit.text()
		self.sniffer.packet_filter.sport_filter_enable=\
							bool(match(self.sniffer.packet_filter.port_re,self.sniffer.packet_filter.sport_filter))
		if self.sniffer.packet_filter.sport_filter=='':
			self.sport_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 255, 255) }')
		elif self.sniffer.packet_filter.sport_filter_enable:
			self.sport_edit.setStyleSheet('QLineEdit { background-color: rgb(175, 255, 175) }')
		else:
			self.sport_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 175, 175) }')

	def UpdateDestinationFilter(self):
		self.sniffer.packet_filter.dst_filter=self.destination_edit.text()
		self.sniffer.packet_filter.dst_filter_enable=\
							bool(match(self.sniffer.packet_filter.address_re,self.sniffer.packet_filter.dst_filter))
		if self.sniffer.packet_filter.dst_filter=='':
			self.destination_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 255, 255) }')
		elif self.sniffer.packet_filter.dst_filter_enable:
			self.destination_edit.setStyleSheet('QLineEdit { background-color: rgb(175, 255, 175) }')
		else:
			self.destination_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 175, 175) }')

	def UpdateDportFilter(self):
		self.sniffer.packet_filter.dport_filter=self.dport_edit.text()
		self.sniffer.packet_filter.dport_filter_enable=\
							bool(match(self.sniffer.packet_filter.port_re,self.sniffer.packet_filter.dport_filter))
		if self.sniffer.packet_filter.dport_filter=='':
			self.dport_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 255, 255) }')
		elif self.sniffer.packet_filter.dport_filter_enable:
			self.dport_edit.setStyleSheet('QLineEdit { background-color: rgb(175, 255, 175) }')
		else:
			self.dport_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 175, 175) }')

	def UpdateProtocolFilter(self):
		'''The completer makes protocol filter input more convinient'''
		if self.sniffer.capturing:
			protocol_set=set([packet_tuple[4] for packet_tuple in self.sniffer.packet_filter.packet_list])
			self.protocol_completer.setModel(QStringListModel(protocol_set,self.protocol_completer))
		elif not self.sniffer.packet_filter.protocol_set:
			protocol_set=set([packet_tuple[4] for packet_tuple in self.sniffer.total_packets])
			self.protocol_completer.setModel(QStringListModel(protocol_set,self.protocol_completer))
			self.sniffer.packet_filter.protocol_set=protocol_set
		else:
			protocol_set=self.sniffer.packet_filter.protocol_set
		
		self.sniffer.packet_filter.protocol_filter=self.protocol_edit.text()
		self.sniffer.packet_filter.protocol_filter_enable=self.sniffer.packet_filter.protocol_filter in protocol_set
		if self.sniffer.packet_filter.protocol_filter=='':
			self.protocol_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 255, 255) }')
		elif self.sniffer.packet_filter.protocol_filter_enable:
			self.protocol_edit.setStyleSheet('QLineEdit { background-color: rgb(175, 255, 175) }')
		else:
			self.protocol_edit.setStyleSheet('QLineEdit { background-color: rgb(255, 175, 175) }')

	def UpdateFindFilter(self):
		self.sniffer.packet_filter.find_filter=self.find_edit.text()

	def BrowseReassembly(self):
		'''This is for browse TCP or IP reassembly'''
		selected=self.packet_table.selectedItems()
		packet_tuple=self.sniffer.packet_filter.packet_list[selected[0].row()]
		if self.reassembler.is_tcp_segment:
			self.reassembler.ReassembleTCP(packet_tuple)
			self.reassembler.setWindowTitle('TCP Reassembly')
			reassembly=str(len(self.reassembler.packet_numbers))+' Reassembled TCP Segments:\n#'
		else:
			self.reassembler.ReassembleIP(packet_tuple)
			self.reassembler.setWindowTitle('IP Reassembly')
			reassembly=str(len(self.reassembler.packet_numbers))+' Reassembled IP Fragments:\n#'
		reassembly+=', #'.join(self.reassembler.packet_numbers)+'\n\n'
		reassembly+=self.sniffer.hexdump(self.reassembler.reassembly)
		self.reassembler.browser.setText(reassembly)
		self.reassembler.show()

	def BrowseSelectedPacket(self):
		'''Called Upon selection in packet table changes

		Browse detailed information and hexdump about packet
		'''
		selected=self.packet_table.selectedItems()
		if selected:
			packet=self.sniffer.packet_filter.packet_list[selected[0].row()]
			self.details_browser.setText(packet[-1].show(dump=True))
			self.bytes_browser.setText(self.sniffer.hexdump(packet[-1]))
			self.reassemble_act.setEnabled(self.reassembler.isFragment(packet))
		else:
			self.details_browser.clear()
			self.bytes_browser.clear()
			self.reassemble_act.setEnabled(False)

	def UpdatePacketTable(self,packet):
		'''Called each time sniffer captures a packet

		Thus glitches do appear when too many packets captured in a short time

		The function collects necessary info about packets and display in the table
		'''
		row_number=self.packet_table.rowCount()
		if row_number:
			time=packet.time-self.sniffer.initial_time
		else:
			self.sniffer.initial_time=packet.time
			self.sniffer.sniffed_time=0
			time=0

		layer_number=2
		layer=packet.getlayer(layer_number)
		while layer and layer.name not in ('Raw','Padding'):
			layer_number+=1
			layer=packet.getlayer(layer_number)
		protocol=packet.getlayer(layer_number-1).name

		if protocol=='ARP':
			source=packet.getlayer(1).hwsrc
			destination=packet.getlayer(1).hwdst
		else:
			source=packet.getlayer(1).src
			destination=packet.getlayer(1).dst
		
		'''Further analyse protocol through ports'''
		if packet.haslayer(TCP) or packet.haslayer(UDP):
			sport,dport=packet.sport,packet.dport
			if protocol=='TCP':
				sport_string=packet[TCP].fields_desc[0].i2repr(packet,sport)
				dport_string=packet[TCP].fields_desc[1].i2repr(packet,dport)
				if sport_string!=str(sport):
					protocol=sport_string.upper()
				if dport_string!=str(dport):
					protocol=dport_string.upper()
			elif protocol=='UDP':
				sport_string=packet[UDP].fields_desc[0].i2repr(packet,sport)
				dport_string=packet[UDP].fields_desc[1].i2repr(packet,dport)
				if sport_string!=str(sport):
					protocol=sport_string.upper()
				if dport_string!=str(dport):
					protocol=dport_string.upper()
		else:
			sport,dport=None,None

		info_list=packet.summary().split(' / ')[1:]
		info_list.sort(key=lambda x:len(x))

		'''The coloring rules are the default ones in Wireshark'''
		if protocol=='ARP':
			background,foreground=QColor(250,240,215),QColor(18,39,46) #ARP
		elif 'ICMP' in protocol:
			if protocol=='ICMP' and packet[ICMP].type in (3,4,5,11):
				background,foreground=QColor(18,39,46),QColor(183,247,116) #ICMP errors
			else:
				background,foreground=QColor(252,224,255),QColor(18,39,46) #ICMP or ICMPv6
		elif packet.haslayer(TCP):
			flag=int(packet[TCP].flags)
			if flag>>2&1:
				background,foreground=QColor(164,0,0),QColor(255,252,156) #TCP reset
			elif sport==80 or dport==80:
				background,foreground=QColor(228,255,199),QColor(18,39,46) #HTTP
			elif flag&3:
				background,foreground=QColor(160,160,160),QColor(18,39,46) #TCP SYN/FIN
			else:
				background,foreground=QColor(231,230,255),QColor(18,39,46) #TCP
		elif packet.haslayer(UDP):
			background,foreground=QColor(218,238,255),QColor(18,39,46) #UDP
		else:
			background,foreground=QColor(255,255,255),QColor(18,39,46)

		'''('No.','Time','Source','Destination','Protocol','Length','Info',background,foreground,sport,dport,packet)'''
		packet_tuple=(str(row_number),'{:.6f}'.format(time),source,destination,\
					protocol,str(len(packet)),info_list[-1],background,foreground,\
					sport,dport,packet)
		self.sniffer.packet_filter.packet_list.append(packet_tuple)

		self.packet_table.insertRow(row_number)
		for column in range(7):
			self.packet_table.setItem(row_number,column,QTableWidgetItem(packet_tuple[column]))
			self.packet_table.item(row_number,column).setBackground(background)
			self.packet_table.item(row_number,column).setForeground(foreground)

		'''Avoid scrolling too fast'''
		if time>0.05+self.sniffer.sniffed_time:
			self.packet_table.scrollToBottom()
			self.sniffer.sniffed_time=time