예제 #1
0
    def temp_setup(self):
        """
         Description
        -------------
         - Temporary function to setup some Qt GUI functionality for proof-of-concept testing.
        """
        monitor = QApplication.desktop().geometry()

        # TODO: Remove these buttons once you're done testing and showing proof-of-concept
        up_button = QPushButton("UP", self)
        down_button = QPushButton("DOWN", self)
        left_button = QPushButton("LEFT", self)
        right_button = QPushButton("RIGHT", self)

        up_button.move(self.board_width,
                       monitor.height() / 3 - up_button.height())
        down_button.move(self.board_width,
                         monitor.height() / 3 - (up_button.height() * 2))
        left_button.move(self.board_width,
                         monitor.height() / 3 - (up_button.height() * 3))
        right_button.move(self.board_width,
                          monitor.height() / 3 - (up_button.height() * 4))

        self.connect(up_button, SIGNAL("clicked()"),
                     partial(self.get_direction, "UP"))
        self.connect(down_button, SIGNAL("clicked()"),
                     partial(self.get_direction, "DOWN"))
        self.connect(left_button, SIGNAL("clicked()"),
                     partial(self.get_direction, "LEFT"))
        self.connect(right_button, SIGNAL("clicked()"),
                     partial(self.get_direction, "RIGHT"))

        get_dice_amount_button = QPushButton("Move Player", self)
        get_dice_amount_button.move(self.board_width, monitor.height() / 3)
        get_dice_amount_button.clicked.connect(self.get_dice_amount)
        get_dice_amount_button.show()

        reset_player_button = QPushButton("Reset", self)
        reset_player_button.move(
            get_dice_amount_button.x() + get_dice_amount_button.width(),
            get_dice_amount_button.y() + get_dice_amount_button.height())
        reset_player_button.clicked.connect(self.reset_player)
        reset_player_button.show()

        self.dice_text_field = QTextEdit("<Enter Dice Amount>", self)
        self.dice_text_field.move(self.board_width + 100, monitor.height() / 3)
        self.dice_text_field.show()

        roll_dice_button = QPushButton("Roll & Move", self)
        roll_dice_button.move(self.board_width,
                              monitor.height() / 3 - (up_button.height() * 5))
        roll_dice_button.clicked.connect(self.roll_dice)
        roll_dice_button.show()

        self.initialize_player_tokens()
        self.initialize_dice()
        self.layout().addChildWidget(self.player_widget)
        self.layout().addChildWidget(self.dice_widget)
예제 #2
0
class FieldNameListEditor(QWidget):
    """A widget to edit foreign keys' field name lists."""

    data_committed = Signal(name="data_committed")

    def __init__(self, parent, option, index):
        """Initialize class."""
        super().__init__(parent)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.model = MinimalTableModel(self)
        self.model.flags = self.model_flags
        self.view = QTableView(self)
        self.view.setModel(self.model)
        self.view.verticalHeader().hide()
        self.view.horizontalHeader().hide()
        self.view.setShowGrid(False)
        check_box_delegate = CheckBoxDelegate(self)
        self.view.setItemDelegateForColumn(0, check_box_delegate)
        check_box_delegate.data_committed.connect(
            self._handle_check_box_data_committed)
        self.button = QPushButton("Ok", self)
        self.button.setFlat(True)
        self.view.verticalHeader().setDefaultSectionSize(option.rect.height())
        self.button.setFixedHeight(option.rect.height())
        layout.addWidget(self.view)
        layout.addWidget(self.button)
        self.button.clicked.connect(self._handle_ok_button_clicked)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)
        x_offset = parent.parent().columnViewportPosition(index.column())
        y_offset = parent.parent().rowViewportPosition(index.row())
        self.position = parent.mapToGlobal(QPoint(0, 0)) + QPoint(
            x_offset, y_offset)

    def model_flags(self, index):
        """Return index flags."""
        if not index.isValid():
            return Qt.NoItemFlags
        if index.column() != 0:
            return ~Qt.ItemIsEditable
        return Qt.ItemIsEditable

    @Slot("QModelIndex", name="_handle_check_box_data_committed")
    def _handle_check_box_data_committed(self, index):
        """Called when checkbox delegate wants to edit data. Toggle the index's value."""
        data = index.data(Qt.EditRole)
        self.model.setData(index, not data)

    @Slot("bool", name="_handle_ok_button_clicked")
    def _handle_ok_button_clicked(self, checked=False):
        """Called when user pressed Ok."""
        self.data_committed.emit()

    def set_data(self, field_names, current_field_names):
        """Set values to show in the 'menu'. Reset model using those values and update geometry."""
        data = [[name in current_field_names, name] for name in field_names]
        self.model.reset_model(data)
        self.view.resizeColumnsToContents()
        width = self.view.horizontalHeader().length() + qApp.style(
        ).pixelMetric(QStyle.PM_ScrollBarExtent)
        self.setFixedWidth(width + 2)
        height = self.view.verticalHeader().length() + self.button.height()
        parent_height = self.parent().height()
        self.setFixedHeight(min(height, parent_height / 2) + 2)
        self.move(self.position)

    def data(self):
        return ",".join(
            [name for checked, name in self.model._main_data if checked])
예제 #3
0
class LoadingBar(QWidget):
    def __init__(self, parent, fn, *args):
        super(LoadingBar, self).__init__(parent)

        self.fn = fn
        self.args = args
        self.worker = Worker(fn, *args)
        self.worker.signals.result.connect(
            main_window.MainWindow.get_instance().calc_done)

        main_window.MainWindow.get_instance().threadpool.start(self.worker)

        # overlay
        # make the window frameless
        self.loading_icon = QSvgRenderer('resources/images/loading_icon.svg')
        self.qp = QPainter()

        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)

        self.fillColor = QColor(30, 30, 30, 120)
        self.penColor = QColor("#333333")

        self.close_btn = QPushButton(self)
        self.close_btn.setText("Abbrechen")

        font = QFont()
        font.setPixelSize(18)
        font.setBold(True)
        self.close_btn.setFont(font)
        self.close_btn.setStyleSheet("QPushButton {"
                                     "background-color: #EAEAEA;"
                                     "border: None;"
                                     "padding-top: 12px;"
                                     "padding-bottom: 12px;"
                                     "padding-left: 20px;"
                                     "padding-right: 20px;"
                                     "}"
                                     "QPushButton:hover {"
                                     "background-color: #DFDFDF;"
                                     "}")
        #self.close_btn.setFixedSize(30, 30)
        self.close_btn.clicked.connect(self._onclose)

        self.signals = LoadingBarSignals()

    def resizeEvent(self, event=None):
        button_x = (self.width() - self.close_btn.width()) / 2
        button_y = (self.height() - self.close_btn.height()) / 2 + 60
        self.close_btn.move(button_x, button_y)

    def paintEvent(self, event):
        # This method is, in practice, drawing the contents of
        # your window.

        # get current window size
        s = self.size()
        self.qp.begin(self)
        self.qp.setRenderHint(QPainter.Antialiasing, True)
        self.qp.setPen(self.penColor)
        self.qp.setBrush(self.fillColor)
        self.qp.drawRect(0, 0, s.width(), s.height())

        icon_size = 180
        self.loading_icon.render(
            self.qp,
            QRect((self.width() - icon_size) / 2,
                  (self.height() - icon_size) / 2 - 60, icon_size, icon_size))
        self.qp.end()

    def _onclose(self):
        self.signals.close.emit()
예제 #4
0
class AutoFilterWidget(QWidget):
    """A widget to show the auto filter 'menu'."""
    def __init__(self, parent):
        """Initialize class."""
        super().__init__(parent)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.model = MinimalTableModel(self)
        self.model.flags = self.model_flags
        self.view = QTableView(self)
        self.view.setModel(self.model)
        self.view.verticalHeader().hide()
        self.view.horizontalHeader().hide()
        self.view.setShowGrid(False)
        check_box_delegate = CheckBoxDelegate(self)
        self.view.setItemDelegateForColumn(0, check_box_delegate)
        check_box_delegate.data_committed.connect(
            self._handle_check_box_data_committed)
        self.button = QPushButton("Ok", self)
        self.button.setFlat(True)
        layout.addWidget(self.view)
        layout.addWidget(self.button)
        self.button.clicked.connect(self.hide)
        self.hide()
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)

    def model_flags(self, index):
        """Return index flags."""
        if not index.isValid():
            return Qt.NoItemFlags
        if index.column() == 1:
            return ~Qt.ItemIsEditable
        return Qt.ItemIsEditable

    @Slot("QModelIndex", name="_handle_check_box_data_committed")
    def _handle_check_box_data_committed(self, index):
        """Called when checkbox delegate wants to edit data. Toggle the index's value."""
        data = index.data(Qt.EditRole)
        model_data = self.model._main_data
        row_count = self.model.rowCount()
        if index.row() == 0:
            # Ok row
            value = data in (None, False)
            for row in range(row_count):
                model_data[row][0] = value
            self.model.dataChanged.emit(self.model.index(0, 0),
                                        self.model.index(row_count - 1, 0))
        else:
            # Data row
            self.model.setData(index, not data)
            self.set_ok_index_data()

    def set_ok_index_data(self):
        """Set data for ok index based on data from all other indexes."""
        ok_index = self.model.index(0, 0)
        true_count = 0
        for row_data in self.model._main_data[1:]:
            if row_data[0] == True:
                true_count += 1
        if true_count == len(self.model._main_data) - 1:
            self.model.setData(ok_index, True)
        elif true_count == 0:
            self.model.setData(ok_index, False)
        else:
            self.model.setData(ok_index, None)

    def set_values(self, values):
        """Set values to show in the 'menu'. Reset model using those values and update geometry."""
        self.model.reset_model([[None, "All"]] + values)
        self.set_ok_index_data()
        self.view.horizontalHeader().hideSection(
            2)  # Column 2 holds internal data (cls_id_set)
        self.view.resizeColumnsToContents()
        width = self.view.horizontalHeader().length() + qApp.style(
        ).pixelMetric(QStyle.PM_ScrollBarExtent)
        self.setFixedWidth(width + 2)
        height = self.view.verticalHeader().length() + self.button.height()
        parent_height = self.parent().height()
        self.setFixedHeight(min(height, parent_height / 2) + 2)

    def set_section_height(self, height):
        """Set vertical header default section size as well as button height."""
        self.view.verticalHeader().setDefaultSectionSize(height)
        self.button.setFixedHeight(height)
예제 #5
0
class MapWidget(QQuickWidget):
    def __init__(self, data, model):
        super(MapWidget,
              self).__init__(resizeMode=QQuickWidget.SizeRootObjectToView)

        self.data = data
        self.model = model

        self.attribute_button = QPushButton(self)
        self.attribute_button.setStyleSheet("color: black")
        self.menu = QMenu("Pick an attribute", self)

        # Create Menu Options
        self.humidity_attribute = QAction("humidity")
        self.pressure_attribute = QAction("pressure")
        self.temperature_attribute = QAction("temperature")
        self.wind_speed_attribute = QAction("wind_speed")

        # due to frequent access of dates based on index, we store this data separately
        self.uniqueDates = self.data["datetime"].apply(
            lambda x: x.split(' ')[0]).unique().tolist()
        self.aggregation = 1

        self.rootContext().setContextProperty("markermodel", model)
        self.rootContext().setContextProperty("MapWidget", self)
        qml_path = os.path.join(os.path.dirname(__file__), "map.qml")
        self.setSource(QUrl.fromLocalFile(qml_path))

        positions = self.get_positions(self.data)
        names = self.get_names(self.data)

        # get first date of dataset in yy-mm-dd
        self.currentDate = self.uniqueDates[0]

        # Set Labels
        self.date_label = QLabel(
            "selected date: " + str(self.currentDate).replace("-", "."), self)
        self.date_label.setStyleSheet("color: black")

        # Set Previous and Next Buttons
        self.previous_button = QPushButton("Previous", self)
        self.next_button = QPushButton("Next", self)

        self.previous_button.clicked.connect(
            partial(self.on_button_clicked, "previous"))
        self.next_button.clicked.connect(
            partial(self.on_button_clicked, "next"))

        values = self.get_values(self.data, "humidity")
        colors = self.get_colors(self.data, "humidity")

        for i in range(0, len(names)):
            geo_coordinates = QGeoCoordinate(positions[i][0], positions[i][1])
            name = names[i]
            value = values[i]
            color = colors[i]

            model.append_marker({
                "position": geo_coordinates,
                "color": color,
                "name": name,
                "value": value,
                "date": self.currentDate
            })

        self.create_interface()

        return

    def add_attribute_to_menu(self, attribute_action, attribute):
        attribute_action.triggered.connect(lambda: self.clicked(attribute))
        self.menu.addAction(attribute_action)

    def create_date_picker(self, index):
        tmp_time = self.uniqueDates[index]
        time_QFormat = tmp_time.split("-")

        # date is parsed and converted to int to comply with required format of QDate
        date_picker = QDateTimeEdit(
            QDate(int(time_QFormat[0]), int(time_QFormat[1]),
                  int(time_QFormat[2])), self)
        date_picker.setDisplayFormat("yyyy.MM.dd")
        date_picker.setCalendarPopup(True)
        date_picker.setCalendarWidget(QCalendarWidget())
        date_picker.resize(date_picker.width() + 20, date_picker.height())

        return date_picker

    def set_date_pickers(self, slider):
        # Set Date Picker for Start of self.slider
        date_picker_start = self.create_date_picker(0)
        date_picker_start.setToolTip(
            "Select the BEGINNING of the time period from which the data is displayed"
        )
        date_picker_start.move(
            slider.property("x") - date_picker_start.width() - 30,
            slider.property("y"))

        # Set Date Picker for End of self.slider
        date_picker_end = self.create_date_picker(-1)
        date_picker_end.setToolTip(
            "Select the END of the time period from which the data is displayed"
        )
        date_picker_end.move(
            slider.property("x") + slider.property("width") + 30,
            slider.property("y"))

        # Set Date Pickers Boundaries Based on First and Last Date in Given Data
        date_picker_start.setMinimumDate(date_picker_start.date())
        date_picker_end.setMinimumDate(date_picker_start.date())
        date_picker_start.setMaximumDate(date_picker_end.date())
        date_picker_end.setMaximumDate(date_picker_end.date())

        return date_picker_start, date_picker_end

    def create_interface(self):
        self.attribute_button.move(50, 0)

        # Create a Menu Option for Each Attribute
        self.add_attribute_to_menu(self.humidity_attribute, "humidity")
        self.add_attribute_to_menu(self.pressure_attribute, "pressure")
        self.add_attribute_to_menu(self.temperature_attribute, "temperature")
        self.add_attribute_to_menu(self.wind_speed_attribute, "wind_speed")

        self.attribute_button.setMenu(self.menu)
        self.attribute_button.resize(self.menu.width() + 50,
                                     self.attribute_button.height())

        # Get self.slider from QML File
        self.slider = self.rootObject().findChild(QObject, "slider")

        # Set Date Pickers
        self.date_picker_start, self.date_picker_end = self.set_date_pickers(
            self.slider)

        self.date_picker_start.dateChanged.connect(lambda: self.change_date(
            self.slider, self.self.date_picker_start, self.date_picker_end))
        self.date_picker_end.dateChanged.connect(lambda: self.change_date(
            self.slider, self.self.date_picker_start, self.date_picker_end))

        # Label Holding the Current Date Selected by User
        self.date_label.move(
            self.slider.property("x") + (self.slider.width() / 2) - 100,
            self.slider.property("y") + 30)
        self.date_label.adjustSize()

        # Set Buttons Position
        self.previous_button.setStyleSheet("color: black")
        self.previous_button.move(self.slider.property("x"),
                                  self.slider.property("y") + 50)
        self.previous_button.adjustSize()
        self.next_button.setStyleSheet("color: black")
        self.next_button.move(
            self.slider.property("x") + self.slider.width() - 70,
            self.slider.property("y") + 50)
        self.next_button.adjustSize()

        jump_label = QLabel("self.slider jump (in days): ", self)
        jump_label.setStyleSheet("color: black")
        jump_label.move(self.date_label.x(), self.date_label.y() + 40)
        jump_label.adjustSize()

        self.jump_value = QLineEdit(self)
        self.jump_value.move(jump_label.x() + jump_label.width(),
                             jump_label.y() - 5)
        self.jump_value.resize(35, self.jump_value.height())
        self.jump_value.editingFinished.connect(
            lambda: self.slider.setProperty("stepSize", self.jump_value.text()
                                            ))

        agg_label = QLabel(self)
        agg_label.setStyleSheet("color: black")
        agg_label.move(self.date_label.x(), self.jump_value.y() + 40)
        agg_label.setText("mean (in days): ")
        agg_label.adjustSize()

        agg_value = QLineEdit(self)
        agg_value.move(self.jump_value.x(), agg_label.y() - 5)
        agg_value.resize(35, agg_value.height())
        agg_value.editingFinished.connect(
            lambda: self.set_agg(agg_value.text()))

        # Initialize Visualization
        self.humidity_attribute.trigger()
        self.change_date(self.slider, self.date_picker_start,
                         self.date_picker_end)

    def on_button_clicked(self, action):
        jump_value = int(self.jump_value.text())
        slider_value = int(self.slider.property("value"))

        current_date = pandas.to_datetime(self.currentDate)

        start_date = self.date_picker_start.date().toPython()
        end_date = self.date_picker_end.date().toPython()

        if action == "next":
            if current_date + datetime.timedelta(days=jump_value) <= end_date:
                self.slider.setProperty("value", slider_value + jump_value)
                self.update_date(int(self.slider.property("value")))
        elif action == "previous":
            if current_date - datetime.timedelta(
                    days=jump_value) >= start_date:
                self.slider.setProperty("value", slider_value - jump_value)
                self.update_date(int(self.slider.property("value")))

    @Slot(int)
    def update_date(self, value):
        self.currentDate = self.uniqueDates[value - 1]
        self.date_label.setText("selected date: " +
                                str(self.currentDate).replace("-", "."))
        self.clicked(self.attribute_button.text())

    # TODO: visualise time series data, not just int created by aggregation
    # TODO: create setting of visualised time period for user

    # calculates the difference (in days) between start date and end date and rescales the self.slider
    def set_agg(self, value):
        self.aggregation = int(value)
        self.clicked(self.attribute_button.text())

    def change_date(self, slider, date_picker_start, date_picker_end):
        dif = self.uniqueDates\
                  .index(date_picker_end.date().toString("yyyy-MM-dd")) - self.uniqueDates.index(date_picker_start.date().toString("yyyy-MM-dd"))
        slider.setProperty("to", dif + 1)

    # when button is clicked, changes values in all model items to a different attribute
    def clicked(self, attribute):
        self.attribute_button.setText(attribute)
        values = self.get_values(self.data, attribute)

        colors = self.get_colors(self.data, attribute)

        for i in range(0, len(values)):
            self.model.setData(i, values[i], colors[i], MarkerModel.ValueRole)

    @staticmethod
    def get_positions(data):
        tmp = data.drop_duplicates('city').sort_values(by=['city'])
        positions = [[x, y] for x, y in zip(tmp['latitude'], tmp['longitude'])]

        return positions

    @staticmethod
    def get_names(data):
        tmp = data.drop_duplicates('city').sort_values(by=['city'])
        names = tmp['city'].values.tolist()

        return names

    # creates an ordered list of aggregated values of a specified attribute
    def get_values(self, data, attribute):
        data['datetime'] = pandas.to_datetime(data['datetime'])

        start_date = pandas.to_datetime(self.currentDate)
        end_date = start_date + datetime.timedelta(days=self.aggregation)

        tmp = data[data['datetime'] >= start_date]
        tmp = tmp[tmp['datetime'] <= end_date]

        values = tmp.groupby('city').apply(
            lambda x: x[attribute].mean()).values.round(2).tolist()

        return values

    @staticmethod
    def get_colors(data, attribute):
        tmp = data.groupby('city').agg({attribute: 'mean'})

        max_value = round(tmp[attribute].max())
        min_value = round(tmp[attribute].min())

        diff = max_value - min_value
        step = round(1 / 6 * diff)

        if attribute == 'pressure':
            attribute_values = {
                0: [255, 255, 255],
                1: [204, 229, 255],
                2: [102, 178, 255],
                3: [0, 128, 255],
                4: [0, 0, 255],
                5: [0, 0, 102],
                6: [0, 0, 51]
            }
        elif attribute == 'temperature':
            attribute_values = {
                0: [0, 102, 204],
                1: [102, 178, 255],
                2: [204, 229, 255],
                3: [255, 204, 204],
                4: [255, 102, 102],
                5: [204, 0, 0],
                6: [102, 0, 0]
            }

        # TODO: create more suited colors for humidity and wind speed

        elif attribute == 'humidity':
            attribute_values = {
                0: [0, 102, 204],
                1: [102, 178, 255],
                2: [204, 229, 255],
                3: [255, 204, 204],
                4: [255, 102, 102],
                5: [204, 0, 0],
                6: [102, 0, 0]
            }
        elif attribute == 'wind_speed':
            attribute_values = {
                0: [0, 102, 204],
                1: [102, 178, 255],
                2: [204, 229, 255],
                3: [255, 204, 204],
                4: [255, 102, 102],
                5: [204, 0, 0],
                6: [102, 0, 0]
            }

        values = numpy.array([
            min_value, min_value + 1 * step, min_value + 2 * step,
            min_value + 3 * step, min_value + 4 * step, min_value + 5 * step,
            max_value
        ])

        tmp['distances'] = tmp[attribute].apply(lambda x: abs(x - values))
        tmp['index'] = tmp['distances'].apply(lambda x: numpy.argmin(x))
        tmp['color'] = tmp['index'].apply(lambda x: attribute_values.get(x))

        colors = tmp['color'].tolist()
        colors_list = []

        for color_tmp in colors:
            color = QColor(color_tmp[0], color_tmp[1], color_tmp[2], 255)
            colors_list.append(color)

        # returns QJSValue
        return colors_list

    def createAction(self, attribute):
        action = QAction(attribute)
        action.triggered.connect(self.clicked(attribute))
        self.menu.addAction(action)
        return action
예제 #6
0
class Window(QWidget):
    def __init__(self, app, parent=None):

        print("Window init")

        super().__init__(parent)

        # self.win_event_filter = WinEventFilter("window")
        # self.installNativeEventFilter(self.win_event_filter)

        self.app = app

        self.window_size = QtCore.QSize(400, 250)
        self.window_size_offset = QtCore.QSize(0, 150)
        self.window_position = QtCore.QPoint(0, 0)
        self.window_position_offset = QtCore.QPoint(0, 0)

        # self.setWindowFlags(
        #    QtCore.Qt.Window |
        #    QtCore.Qt.CustomizeWindowHint |
        #    QtCore.Qt.WindowTitleHint |
        #    QtCore.Qt.WindowCloseButtonHint |
        #    QtCore.Qt.WindowStaysOnTopHint
        # )

        self.setWindowFlags(self.windowFlags() |
                            QtCore.Qt.FramelessWindowHint)
        self.setWindowFlags(self.windowFlags() |
                            QtCore.Qt.WindowStaysOnTopHint)

        self.setWindowFlags(
            QtCore.Qt.FramelessWindowHint |
            QtCore.Qt.WindowStaysOnTopHint |
            QtCore.Qt.Tool)
        # hlayout = QHBoxLayout()
        # hlayout.setMargin(0)
        # hlayout.setContentsMargins(0, 0, 0, 0)
        # hlayout.setSpacing(0)

        # buttonslayout = QVBoxLayout()

        self.labels = []

        self.menuButton = QPushButton(u"\U00002261")
        self.menuLabel = QLabel("Menu")
        myFontBold = self.menuLabel.font()
        myFontBold.setBold(True)
        # buttons
        myFont = self.menuButton.font()
        myFont2 = self.menuButton.font()
        if (myFont.pointSize() > 0):
            myFont.setPointSizeF(1.25 * myFont.pointSizeF())
            myFont2.setPointSizeF(1.4 * myFont.pointSizeF())
        else:
            myFont.setPixelSize(1.25 * myFont.pixelSize())
            myFont2.setPixelSize(1.4 * myFont.pixelSize())
        self.menuLabel.setFont(myFontBold)
        width = self.menuButton.fontMetrics().boundingRect("OO").width() + 7
        height = width  # okButton.height()
        self.menuButton.setFont(myFont2)
        self.menuButton.setMaximumWidth(width)
        self.menuButton.setMinimumWidth(width)
        self.menuButton.setFlat(True)
        self.menuButton.clicked.connect(self.menuPressed)

        mainButton = QPushButton(u"\U0000239A")
        mainLabel = QLabel("Main")
        width = mainButton.fontMetrics().boundingRect("OO").width() + 7
        height = width  # okButton.height()
        mainButton.setFont(myFont2)
        mainButton.setMaximumWidth(width)
        mainButton.setMinimumWidth(width)
        mainButton.clicked.connect(self.main)
        mainButton.setFlat(True)
        setupButton = QPushButton(u"\U0001F527")
        setupLabel = QLabel("Setup")
        setupButton.setFont(myFont)
        setupButton.setFlat(True)
        setupButton.setMaximumWidth(width)
        setupButton.setMinimumWidth(width)
        setupButton.clicked.connect(self.setup)

        identifyButton = QPushButton(u"\U00002755")
        identifyLabel = QLabel("Identify")
        identifyButton.setFont(myFont)
        identifyButton.setFlat(True)
        identifyButton.setMaximumWidth(width)
        identifyButton.setMinimumWidth(width)
        identifyButton.clicked.connect(self.identify)

        self.refreshButton = QPushButton(u"\U000021BB")
        self.refreshLabel = QLabel("Detect")
        self.refreshButton.setFont(myFont)
        self.refreshButton.setFlat(True)
        self.refreshButton.setMaximumWidth(width)
        self.refreshButton.setMinimumWidth(width)
        self.refreshButton.clicked.connect(self.refreshPressed)

        aboutButton = QPushButton(u"\U00002754")
        aboutLabel = QLabel("About")
        aboutButton.setFont(myFont)
        aboutButton.setFlat(True)
        aboutButton.setMaximumWidth(width)
        aboutButton.setMinimumWidth(width)
        aboutButton.clicked.connect(self.about)

        # closeButton = QPushButton(u"\U00002573")
        closeButton = QPushButton(u"\U000026CC")
        closeLabel = QLabel("Close")
        closeButton.setFont(myFont)
        closeButton.setFlat(True)
        closeButton.setMaximumWidth(width)
        closeButton.setMinimumWidth(width)
        closeButton.clicked.connect(self.close_)

        buttongrid = QGridLayout()
        buttongrid.addWidget(self.menuButton, 0, 0)
        buttongrid.addWidget(mainButton, 1, 0)
        buttongrid.addWidget(setupButton, 2, 0)
        buttongrid.addWidget(self.refreshButton, 3, 0)
        buttongrid.addWidget(identifyButton, 4, 0)
        buttongrid.addWidget(aboutButton, 6, 0)
        buttongrid.addWidget(closeButton, 7, 0)

        buttongrid.addWidget(self.menuLabel, 0, 1)
        buttongrid.addWidget(mainLabel, 1, 1)
        buttongrid.addWidget(setupLabel, 2, 1)
        buttongrid.addWidget(self.refreshLabel, 3, 1)
        buttongrid.addWidget(identifyLabel, 4, 1)
        buttongrid.addWidget(aboutLabel, 6, 1)
        buttongrid.addWidget(closeLabel, 7, 1)
        self.labels.append(self.menuLabel)
        self.labels.append(mainLabel)
        self.labels.append(setupLabel)
        self.labels.append(self.refreshLabel)
        self.labels.append(identifyLabel)
        self.labels.append(aboutLabel)
        self.labels.append(closeLabel)
        self.menuLabel .mousePressEvent = self.menuLabelPressed
        mainLabel .mousePressEvent = self.mainLabel
        setupLabel.mousePressEvent = self.setupLabel
        self.refreshLabel.mousePressEvent = self.refreshLabelPressed
        identifyLabel.mousePressEvent = self.identifyLabel
        aboutLabel.mousePressEvent = self.aboutLabel
        closeLabel.mousePressEvent = self.closeLabel

        buttongrid.setRowStretch(0, 0)
        buttongrid.setRowStretch(1, 0)
        buttongrid.setRowStretch(2, 0)
        buttongrid.setRowStretch(3, 0)
        buttongrid.setRowStretch(4, 0)
        buttongrid.setRowStretch(5, 1)
        buttongrid.setRowStretch(6, 0)
        buttongrid.setRowStretch(7, 0)
        self.labels_set_visible(False)

        self.layout = QHBoxLayout()

        # buttonslayout.addWidget(mainButton)
        # buttonslayout.addWidget(setupButton)
        # buttonslayout.addStretch(1)
        # buttonslayout.addWidget(aboutButton)
        # hlayout.addLayout(buttonslayout)
        # hlayout.addLayout(buttongrid)

        # grid.addLayout(hlayout, 1, 1)
        buttongrid.setSpacing(0)

        self.layout.addLayout(buttongrid)

        self.body_layout = QVBoxLayout()
        self.body_layout.setContentsMargins(0, 0, 0, 1)
        self.body_layout.setSpacing(0)

        self.title_layout = QHBoxLayout()
        self.title_layout.setContentsMargins(0, 0, 0, 0)
        self.title_layout.setSpacing(0)
        self.titleLabel = QLabel("Monitor Control")
        self.titleLabel.setWordWrap(True)
        self.titleLabel.setSizeIncrement(10, 10)
        myFont = self.titleLabel.font()
        myFont.setBold(True)
        self.titleLabel.setFont(myFont)
        width = self.titleLabel.fontMetrics().boundingRect("OO").width() + 7
        height = width  # okButton.height()
        self.titleLabel.mousePressEvent = self.mainLabel

        self.backButton = QPushButton(u"\U00002190", self)
        myFont = self.backButton.font()
        myFont.setBold(True)
        self.backButton.setFont(myFont)
        self.backButton.setMaximumWidth(width)
        self.backButton.setMinimumWidth(width)
        self.backButton.setFlat(True)
        self.backButton.clicked.connect(self.main)
        self.titleLabel.setMinimumHeight(self.backButton.height())

        self.title_layout.addWidget(self.backButton, 0, QtCore.Qt.AlignVCenter)
        self.title_layout.addSpacing(20)
        self.title_layout.addWidget(self.titleLabel, 1, QtCore.Qt.AlignVCenter)
        # self.backButton.setAlignment(Qt.AlignTop)
        self.title_layout.setAlignment(QtCore.Qt.AlignTop)

        self.body_layout.addLayout(self.title_layout)

        self.main_frame = QtWidgets.QFrame(self)
        self.main_layout = QVBoxLayout()
        self.feature_brightness = FeatureWidget(
            self.main_frame, "Brightness", self.app.brightness)
        self.feature_contrast = FeatureWidget(
            self.main_frame, "Contrast", self.app.contrast)
        self.main_layout.addWidget(self.feature_brightness)
        self.main_layout.addWidget(self.feature_contrast)
        self.main_layout.addStretch(1)
        self.main_frame.setLayout(self.main_layout)
        self.main_frame.hide()
        self.body_layout.addWidget(self.main_frame, 1)

        self.setup_frame = QtWidgets.QFrame(self)

        leftButton = QPushButton("<", self.setup_frame)
        width = leftButton.fontMetrics().boundingRect("<").width() + 7
        leftButton.setFlat(True)
        leftButton.setMaximumWidth(width)
        leftButton.setMinimumWidth(width)
        leftButton.setSizePolicy(QtWidgets.QSizePolicy(
            QSizePolicy.Fixed, QSizePolicy.Expanding))

        self.setup_layout = QHBoxLayout()
        self.setup_layout.addWidget(leftButton)
        self.feature_setup_widget = FeatureSetupWidget(
            self.app, self.setup_frame)
        # hlayout.addWidget(self.feature_setup_widget, 1)
        self.feature_setup_widget.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Expanding)
        rightButton = QPushButton(">", self.setup_frame)
        rightButton.setFlat(True)
        rightButton.setMaximumWidth(width)
        rightButton.setMinimumWidth(width)
        rightButton.setSizePolicy(QtWidgets.QSizePolicy(
            QSizePolicy.Fixed, QSizePolicy.Expanding))
        self.setup_layout.addWidget(self.feature_setup_widget, 1)
        self.setup_layout.addWidget(rightButton)
        self.setup_layout.setContentsMargins(0, 0, 0, 0)
        self.setup_layout.setSpacing(0)
        leftButton.clicked.connect(self.feature_setup_widget.previous)
        rightButton.clicked.connect(self.feature_setup_widget.next)

        self.setup_frame.setLayout(self.setup_layout)

        # self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(10)
        self.body_layout.addWidget(self.setup_frame, 1)

        self.layout.addLayout(self.body_layout, 1)

        self.about_frame = QtWidgets.QFrame(self)
        self.about_layout = QVBoxLayout()
        self.aboutLabel1 = QLabel("About", self.about_frame)
        self.aboutLabel1.setWordWrap(True)
        myFont = self.aboutLabel1.font()
        myFont.setBold(True)
        self.aboutLabel1.setFont(myFont)
        about = "©️ ™️ Juno\n\nMonitor Control synchronizes your monitor hardware properties like brightness and contrast.\nThese properties can be changed by the software sliders, or monitor buttons. These changes are monitored and read, and subsequently set to the other monitors using a calibration. This will ensure an input change has the same result on all monitors.\n"
        self.aboutLabel2 = QLabel("{}".format(about), self.about_frame)
        self.aboutLabel2.setAlignment(
            QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
        self.aboutLabel2.setWordWrap(True)
        self.about_layout.addWidget(self.aboutLabel1)
        self.about_layout.addWidget(self.aboutLabel2, 1)
        self.about_frame.setLayout(self.about_layout)
        self.about_frame.hide()
        self.body_layout.addWidget(self.about_frame, 1)

        # self.layout.setSizeConstraint(QtGui.QLayout.setFixedSize)

        self.setLayout(self.layout)

        self.setWindowIcon(QtGui.QIcon('artifacts/icon.png'))
        # set the title
        self.setWindowTitle("Monitors Control")

        self.main()

        self.setFixedSize(400, 250)

    def labels_set_visible(self, visible):
        for label in self.labels:
            if (self.refreshLabel == label) and visible:
                self.refreshLabel.setVisible(self.refreshButton.isVisible())
            else:
                label.setVisible(visible)

    def refresh_visible(self, visible):
        if (visible):
            self.refreshButton.setVisible(visible)
            self.refreshLabel.setVisible(self.menuLabel.isVisible())
        else:
            self.refreshLabel.setVisible(visible)
            self.refreshButton.setVisible(visible)

    def focusOutEvent(self, event):
        print('Lost focus')

    def menuLabelPressed(self, event):
        self.menuPressed()

    def menuPressed(self):
        print("Menu")
        self.labels_set_visible(not self.labels[0].isVisible())

    def aboutLabel(self, event):
        self.about()

    def about(self):
        print("About")
        self.setupUpdate()

        self.setMinimumSize(200, 130)

        # self.feature_setup_widget.hide()
        self.setup_frame.hide()
        self.main_frame.hide()
        self.refresh_visible(False)
        self.backButton.show()
        self.about_frame.show()

        self.move(self.window_position)
        self.setFixedSize(self.window_size)

    def closeLabel(self, event):
        self.close_()

    def close_(self):
        print("Close {}".format(len(self.app.identifyWindows)))
        self.setupUpdate()
        if (len(self.app.identifyWindows) == 0):
            self.hide()

    def setupLabel(self, event):
        self.setup()

    def setup(self):
        print("Setup")
        self.move(self.window_position + self.window_position_offset)
        self.setFixedSize(self.window_size + self.window_size_offset)

        self.app.monitors._calibrations.loadYaml()

        self.feature_setup_widget.init()
        self.backButton.show()

        self.main_frame.hide()
        self.about_frame.hide()
        self.refresh_visible(True)
        self.setup_frame.show()

        self.setMinimumSize(200, 130)

    def setupUpdate(self):
        if (self.setup_frame.isVisible()):
            self.app.monitors._calibrations.saveYaml()

    def mainLabel(self, event):
        self.main()

    def main(self):
        print("Main")

        self.setMinimumSize(200, 130)
        self.setupUpdate()

        self.refresh_visible(False)
        self.backButton.hide()
        # self.feature_setup_widget.hide()
        self.setup_frame.hide()
        self.about_frame.hide()
        self.main_frame.hide()

        self.move(self.window_position)
        self.setFixedSize(self.window_size)

        self.main_frame.show()

    def identifyLabel(self, event):
        self.identify()

    def identify(self):
        print("Identify")

        self.app.identify()

    def refreshLabelPressed(self, event):
        self.refreshPressed()

    def refreshPressed(self):
        QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        print("detect")
        self.feature_setup_widget.clear()
        self.app.detect()
        self.setup()
        self.feature_setup_widget.set_infos(self.app.monitors)
        self.feature_setup_widget.init()

        self.app.list_monitors()

        QApplication.restoreOverrideCursor()

    def position_show(self):
        print("position_show")
        self.app.position_next_to_tray()
        self.main()
        self.show()
        # self.requestActivate()
        # QtCore.Qt.ActiveWindowFocusReason
        self.activateWindow()
        self.setFocus(QtCore.Qt.PopupFocusReason)

    def contrast(self, value):
        # from gui
        self.app.contrast(value)

    def brightness(self, value):
        # from gui
        self.app.brightness(value)

    def set_contrast(self, value):
        # to gui
        self.feature_contrast.set_value(value)
        self.feature_setup_widget.set_contrast(value)

    def set_brightness(self, value):
        # to gui
        self.feature_brightness.set_value(value)
        self.feature_setup_widget.set_brightness(value)

    def show(self):
        # to gui
        value = self.app.monitors.get_contrast()
        if (value is not None):
            self.set_contrast(value)

        value = self.app.monitors.get_brightness()
        if (value is not None):
            self.set_brightness(value)

        self.feature_setup_widget.set_infos(self.app.monitors)

        super().show()