Example #1
0
class TestQtElements(TestCase):
    def setUp(self):
        self.main = QWidget()

    def test_creation_of_app_buttons(self):
        app_buttons = qt.AppButtons()
        self.main.setLayout(app_buttons)

        self.assertIsInstance(app_buttons.button_new_game, QPushButton)
        self.assertEqual(app_buttons.button_new_game.text(), 'New Game')
        self.assertIn(app_buttons.button_new_game, self.main.children())

        self.assertIsInstance(app_buttons.button_help, QPushButton)
        self.assertEqual(app_buttons.button_help.text(), 'Help')
        self.assertIn(app_buttons.button_help, self.main.children())

        self.assertEqual(app_buttons.spacing(), qt.SPACING)

    def test_creation_of_epidemic_menu(self):
        menu = qt.EpidemicMenu()
        self.main.setLayout(menu)

        self.assertIsInstance(menu.combo_box, QComboBox)
        self.assertIsInstance(menu.button, QPushButton)
        self.assertEqual(menu.button.text(), 'Shuffle Epidemic')

        self.assertIn(menu.combo_box, self.main.children())
        self.assertIn(menu.button, self.main.children())

        self.assertEqual(menu.spacing(), qt.SPACING)

    def test_creation_of_destinations_radio_box(self):
        destinations = {
            'dest1': QRadioButton('Destination 1'),
            'dest2': QRadioButton('Destination 2'),
            'dest3': QRadioButton('Destination 3')
        }
        radio_box = qt.DestinationRadioBox(destinations)
        self.assertEqual(len(radio_box.b_group.buttons()), 3)
        for item in destinations.values():
            self.assertIn(item, radio_box.children())

    def test_creation_of_stats_section(self):
        pass
Example #2
0
def get_child_widget(parent: QWidget,
                     name: str) -> Union[Optional[QWidget], None]:
    for child in parent.children():
        if child.objectName() == name:
            return child
        sub_child = get_child_widget(child, name)
        if sub_child is not None:
            return sub_child

    return None
Example #3
0
    def testLoadFileUnicodeFilePath(self):
        filePath = str(os.path.join(os.path.dirname(__file__), 'test.ui'))
        loader = QUiLoader()
        parent = QWidget()
        w = loader.load(filePath, parent)
        self.assertNotEqual(w, None)

        self.assertEqual(len(parent.children()), 1)

        child = w.findChild(QWidget, "child_object")
        self.assertNotEqual(child, None)
        self.assertEqual(w.findChild(QWidget, "grandson_object"), child.findChild(QWidget, "grandson_object"))
Example #4
0
    def testLoadFile(self):
        loader = QUiLoader()
        parent = QWidget()
        w = loader.load(self._filePath, parent)
        self.assertNotEqual(w, None)

        self.assertEqual(len(parent.children()), 1)

        child = w.findChild(QWidget, "child_object")
        self.assertNotEqual(child, None)
        self.assertEqual(w.findChild(QWidget, "grandson_object"),
                         child.findChild(QWidget, "grandson_object"))
Example #5
0
    def testLoadFileUnicodeFilePath(self):
        filePath = str(os.path.join(os.path.dirname(__file__), 'test.ui'))
        loader = QUiLoader()
        parent = QWidget()
        w = loader.load(filePath, parent)
        self.assertNotEqual(w, None)

        self.assertEqual(len(parent.children()), 1)

        child = w.findChild(QWidget, "child_object")
        self.assertNotEqual(child, None)
        self.assertEqual(w.findChild(QWidget, "grandson_object"),
                         child.findChild(QWidget, "grandson_object"))
Example #6
0
def walk_children(root: QtWidgets.QWidget):
    def key(child):
        try:
            k = f"{child.variable_name} - {str(child)}"
        except:
            k = str(child)
        return k

    if not isinstance(root, QtWidgets.QWidget) and not isinstance(
            root, QtWidgets.QLayout):
        return root

    return {
        key(child): walk_children(child)
        for child in root.children()
        if not isinstance(child, QtWidgets.QLayout)  # do not show layouts
    }
Example #7
0
class MainWindow(QMainWindow, Ui_window_main):
    """Main window of the application."""
    def __init__(self, app):
        """
        Initialize the ui files for the application
        """
        super().__init__()
        self.app = app
        self.files = FileManager(self.app.config, self.app.data_home)
        self.player = None
        self.current_show = None
        self.setupUi(self)

    def setupUi(self, window_main):
        """Setup all the child widgets of the main window"""
        super().setupUi(window_main)
        self._setup_tab_browser()
        self._setup_tab_files()
        self.action_open_folder.triggered.connect(self.choose_dir)
        self.action_source_code.triggered.connect(open_source_code)
        self.action_refresh_library.triggered.connect(self.refresh_library)
        self.widget_tab.currentChanged.connect(self.handle_current_tab_changed)

    @Slot(int)
    def handle_current_tab_changed(self, index):
        """
        Handles when the tab changes in the widget tab.

        Args:
            index:
                The index of the new tab

        Returns:
            None
        """
        if index == 2:
            if not self.media_browser.is_setup:
                self.media_browser.is_setup = True
                self.app.pool.submit(
                    self.media_browser.fetch_control_info,
                    self.combobox_genre_tag,
                    self.combobox_streaming,
                )
            self.check_box_adult.setVisible(
                self.app.config.get('Allow Adult Content', default=False))

    def _setup_tab_browser(self):
        self.media_browser = MediaBrowser(
            self.app, self.button_sort_order, self.combobox_season,
            self.spinbox_year_min, self.spinbox_year_max, self.combobox_sort,
            self.combobox_format, self.combobox_status, self.check_box_on_list,
            self.check_box_adult)

        master_layout = QHBoxLayout()
        self.tab_browser.setLayout(master_layout)

        left_mid_bot_layout = QHBoxLayout()
        left_layout = QVBoxLayout()
        left_layout.setAlignment(Qt.AlignTop)

        left_layout_control = QWidget()
        left_layout_control.setLayout(left_layout)

        left_layout.addWidget(self.label_season)
        left_layout.addWidget(self.combobox_season)
        left_layout.addWidget(self.groupbox_year)
        left_layout.addWidget(self.label_filter)
        left_mid_bot_layout.addWidget(self.combobox_sort)
        left_mid_bot_layout.addWidget(self.button_sort_order)
        left_layout.addLayout(left_mid_bot_layout)
        left_layout.addWidget(self.combobox_format)
        left_layout.addWidget(self.combobox_status)
        left_layout.addWidget(self.combobox_streaming)
        left_layout.addWidget(self.combobox_genre_tag)
        left_layout.addWidget(self.check_box_on_list)
        left_layout.addWidget(self.check_box_adult)

        left_layout_control.setMaximumWidth(self.groupbox_year.width())
        left_layout.setSpacing(0)
        left_layout.setMargin(0)

        master_layout.addWidget(left_layout_control)
        master_layout.addWidget(self.media_browser)

    def _setup_tab_files(self):
        """
        Sets up the local files tab. Triggered when the tab is selected
        """
        # TODO: Refactor
        self.files.build_all_from_db()
        series_layout = FlowLayout()
        series_layout.setAlignment(Qt.AlignTop)

        for show in sorted(self.files.series):
            button = SeriesButton(show, len(self.files.series[show]))
            button.clicked.connect(self.on_series_click)
            series_layout.addWidget(button)

        series_layout.setSizeConstraint(FlowLayout.SetMaximumSize)
        self.page_widget = QWidget()
        self.page_widget.setLayout(series_layout)
        series_page = QScrollArea()
        series_page.setWidget(self.page_widget)
        series_page.setWidgetResizable(True)
        self.stack_local_files.addWidget(series_page)
        self.stack_local_files.addWidget(QWidget())

    def on_series_click(self, *, show=None):
        """
        Sets up the window for the episodes of a show. Triggered when a show is
        clicked from the local files page
        """
        if show is None:
            show = self.sender().title
        self.current_show = show
        self.stack_local_files.setCurrentIndex(1)
        menu_layout = QGridLayout()
        menu = QWidget()
        layout = FlowLayout()
        layout.setAlignment(Qt.AlignTop)

        back_button = QPushButton('Back')
        back_button.clicked.connect(self.on_back_click)
        menu_layout.addWidget(back_button, 0, 0)

        refresh_button = QPushButton('Refresh')
        refresh_button.clicked.connect(self.refresh_show)
        menu_layout.addWidget(refresh_button, 1, 0)

        delete_button = QPushButton('Delete')
        delete_button.clicked.connect(self.delete_show)
        menu_layout.addWidget(delete_button, 0, 1)

        rename_button = QPushButton('Rename')
        rename_button.clicked.connect(self.rename_show)
        menu_layout.addWidget(rename_button, 1, 1)

        label = QLabel(show)
        menu_layout.addWidget(label, 0, 2, 2, 2)
        menu.setLayout(menu_layout)
        menu.setMaximumWidth(self.stack_local_files.width())
        menu.setMinimumWidth(self.stack_local_files.width() / 2)
        layout.addWidget(menu)

        self.files.series[show].sort()
        for episode in self.files.series[show]:
            button = EpisodeButton(episode)
            button.clicked.connect(self.play_episode)
            layout.addWidget(button)
        self.stack_local_files.currentWidget().setLayout(layout)

    def refresh_show(self):
        """
        Refreshes the episodes for the current show and dumps the new shows to
        the database. Triggered by clicking Refresh in the view for a show
        """
        new = self.files.refresh_single_show(self.current_show)
        layout = self.stack_local_files.currentWidget().layout()
        for episode in new:
            button = EpisodeButton(episode)
            button.clicked.connect(self.play_episode)
            layout.addWidget(button)
        self.files.dump_to_db()

    def play_episode(self):
        """
        Plays the selected episode with the users player of choice
        """
        if self.player is not None and self.player.needs_destruction():
            self.player = None

        if self.player is None:
            self.player = ene.player.get_player(self.app.config)
        episode = self.sender().path
        self.player.play(str(episode))

    def on_back_click(self):
        """
        Deletes all child widgets of the layout for episodes and the layout
        itself then returns to the shows page
        """
        self.current_show = None
        layout = self.stack_local_files.currentWidget().layout()
        for i in reversed(range(layout.count())):
            layout.itemAt(i).widget().deleteLater()
        layout.deleteLater()
        self.stack_local_files.setCurrentIndex(0)

    def choose_dir(self) -> Path:
        """
        Choose a directory from a file dialog

        Returns: The directory path
        """
        args = [self, self.tr("Open Directory"), str(Path.home())]
        if IS_WIN:
            args.append(QFileDialog.DontUseNativeDialog)
        dir_ = QFileDialog.getExistingDirectory(*args)
        # TODO do something with this
        return Path(dir_)

    def refresh_library(self):
        """
        Triggers a full refresh of the users library, updating episode counts
        and adding new shows to the UI as needed
        """
        old = set(self.files.series.keys())
        self.files.refresh_shows()
        new = set(self.files.series.keys()) - old
        for show in self.page_widget.children():
            if isinstance(show, SeriesButton):
                show.update_episode_count(len(self.files.series[show.title]))
        for show in sorted(new):
            button = SeriesButton(show, len(self.files.series[show]))
            button.clicked.connect(self.on_series_click)
            self.page_widget.layout().addWidget(button)
        self.files.dump_to_db()

    def rename_show(self):
        """
        Renames the current show. Displays an input box to get the new name
        """
        title = QInputDialog().getText(self,
                                       'Rename',
                                       'New Title:',
                                       text=self.current_show)
        if title[1]:
            self.files.rename_show(self.current_show, title[0])

    def delete_show(self):
        """
        Deletes the current show
        """
        self.files.delete_show(self.current_show)
        self.on_back_click()
Example #8
0
class MainWindow(QMainWindow):
    forum = Forum(id=-1, name="UNKNOWN")
    topics = []
    forum_model = ForumModel()
    day_model = DayModel()

    def __init__(self):
        QMainWindow.__init__(self)
        self.date_view = QListView()
        self.forum_view = QListView()
        self.contentMax = 0
        self.ioloop = asyncio.get_event_loop()
        icon = QIcon()
        icon.addPixmap(QPixmap(FAVICON_ICO), QIcon.Normal)
        self.setWindowIcon(icon)
        self.setWindowTitle(APP_TITLE)

        self.settings = QSettings('karoStudio', 'nnm-stats')
        self.resize(self.settings.value('main/size', QSize(640, 480)))
        self.move(self.settings.value('main/pos', QPoint(200, 200)))

        self.splitter = QSplitter()
        self.get_menu()

        self.content = QScrollArea()
        self.content.verticalScrollBar().valueChanged.connect(
            self.scrollBarChanged)
        self.content.verticalScrollBar().rangeChanged.connect(
            self.rangeChanged)
        self.torrents_list_view = QWidget()
        layout = QGridLayout()
        self.torrents_list_view.setLayout(layout)
        self.content.setWidget(self.torrents_list_view)
        self.splitter.addWidget(self.content)
        self.splitter.setSizes([160, 350])
        self.setCentralWidget(self.splitter)

        self.timer = QTimer()
        self.timer.singleShot(1600, self.load_task)

    def get_menu(self):
        scroll = QScrollArea(self)
        self.forum_view.setStyleSheet("QListView{font: bold 12px;}")
        self.forum_view.clicked.connect(self.listViewClick)
        self.forum_view.setModel(self.forum_model)

        self.date_view.setStyleSheet("QListView{font: bold 12px;}")
        self.date_view.clicked.connect(self.listViewClick)
        self.date_view.setModel(self.day_model)

        menu_splitter = QSplitter(self)
        menu_splitter.setOrientation(Qt.Vertical)
        menu_splitter.addWidget(self.forum_view)
        menu_splitter.addWidget(self.date_view)
        scroll.setWidget(menu_splitter)
        scroll.setWidgetResizable(True)
        self.splitter.addWidget(scroll)

    def load_task(self):
        self.ioloop.run_until_complete(self.load_forums())

    async def load_forums(self):
        tasks = [asyncio.ensure_future((get_forums("http://nnmclub.to/")))]
        done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED)
        forums = done.pop().result()
        for forum in forums:
            print(forum)
            self.forum_model.add(forum)

    def load_torrents_task(self, forum, start):
        self.ioloop.run_until_complete(self.load_torrents(forum, start))

    async def load_torrents(self, forum, start=0):
        tasks = [asyncio.ensure_future((get_topics(forum, start)))]
        done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED)
        if start == 0:
            self.topics = done.pop().result()
        else:
            self.topics = self.topics + done.pop().result()
        layout = QGridLayout()
        self.topics.sort(key=lambda x: x.likes, reverse=True)
        days = {}
        for i, topic in enumerate(self.topics):
            d = topic.published.date()
            if d in days.keys():
                days[d]['count'] += 1
                days[d]['likes'] += topic.likes
            else:
                days[d] = {'count': 1, 'likes': topic.likes}
            # layout.addWidget(TopicView(topic), i, 0)
        for day in days.keys():
            self.day_model.add(day, days[day])
        self.torrents_list_view = QWidget()
        self.torrents_list_view.setLayout(layout)
        self.content.setWidget(self.torrents_list_view)

    def rangeChanged(self, vert_min, vert_max):
        self.contentMax = vert_max

    def scrollBarChanged(self, value):
        if value == self.contentMax:
            print("LOADING {}".format(self.torrents_list_view.children()))
            self.timer.singleShot(
                1000,
                lambda: self.load_torrents_task(self.forum, len(self.topics)))

    def listViewClick(self, index):
        self.forum = self.forum_model.forums[index.row()]
        self.topics = []
        self.setWindowTitle("{} / {}".format(APP_TITLE, self.forum.name))
        self.day_model.clear()
        # self.timer.singleShot(1000, lambda: self.load_torrents_task(self.forum, 0))
        self.timer.timeout.connect(
            lambda: self.load_torrents_task(self.forum, len(self.topics)))
        self.timer.start(8000)

    def closeEvent(self, event):
        self.settings.setValue('main/size', self.size())
        self.settings.setValue('main/pos', self.pos())
        self.ioloop.close()