Skip to content

Instantly share code, notes, and snippets.

@Hayao0819
Last active August 5, 2022 05:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hayao0819/ed70edac37ccff58c4a3bd8221deb6a0 to your computer and use it in GitHub Desktop.
Save Hayao0819/ed70edac37ccff58c4a3bd8221deb6a0 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# Version 2, December 2004
#
#:: License
#
# Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
#
# Everyone is permitted to copy and distribute verbatim or modified
# copies of this license document, and changing it is allowed as long
# as the name is changed.
#
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
# 0. You just DO WHAT THE FUCK YOU WANT TO.
#
#:: Dependencies
# - python3
# - python-feedparser
# - python-wxpython
#
#:: Usage
# Run: python3 archnews.py
import wx;
import feedparser;
import sys;
import wx.html as html;
import os;
import urllib.request
Japanese=True;
Version = 0.1
class RSSParseError(Exception):
pass
class ArchNews():
RSSEnglish = "http://www.archlinux.org/feeds/news/";
RSSJapaese = "https://www.archlinux.jp/feeds/news.xml";
CurrentFeedURL=None
def __init__(self):
FeedURL=""
if Japanese:
FeedURL=self.RSSJapaese
else:
FeedURL=self.RSSEnglish
self.UpdateFeed(FeedURL)
def GetTitleList(self):
TitleList = [];
for entry in self.NewsList:
TitleList.append(entry.title);
return TitleList;
def GetDetailFromTitle(self,Title):
for entry in self.NewsList:
if entry.title == Title:
return entry.summary;
return ""
def UpdateFeed(self,url):
self.CurrentFeedURL=url
self.Feed = feedparser.parse(url)
self.NewsList=self.Feed.entries;
try:
self.Feed.status
except (NameError, AttributeError):
raise RSSParseError
class MainPanel(wx.Panel):
News=None # ArchNewsオブジェクト
CurrentHTML="" # ArchNewsから取得した現在のHTML
NewsListSizer=None # 左のニュース一覧
NewsListBox=None # 左のニュース一覧のListBox
NewsDetailSizer=None # 右の詳細
HTMLWindow=None # 右の詳細の中のHTMLビューアー
WrapSizer=None # 全体
def __init__(self, parent):
try:
self.News = ArchNews();
except RSSParseError:
self.CreateErrorWindow();
sys.exit(1)
# パネル初期化
wx.Panel.__init__(self, parent)
self.CreateWrapSizer()
self.CreateLeftNewsListSizer()
self.CreateRightNewsDetailSizer()
self.SetSizer(self.WrapSizer)
def CreateErrorWindow(self):
ErrorMsgBox = wx.MessageDialog(None, 'RSSの取得に失敗しました', 'RSSエラー')
ErrorMsgBox.ShowModal()
ErrorMsgBox.Destroy()
def CreateWrapSizer(self):
self.WrapSizer= wx.FlexGridSizer(rows=1, cols=2, gap=(10, 10))
self.WrapSizer.AddGrowableCol(1) # News Detail を拡張可能にする
self.WrapSizer.AddGrowableRow(0)
return
def CreateLeftNewsListSizer(self):
self.NewsListSizer = wx.StaticBoxSizer(orient=wx.VERTICAL, parent=self, label="News List")
self.NewsListBox = wx.ListBox(self, -1, choices=self.News.GetTitleList())
self.NewsListSizer.Add(self.NewsListBox, 1, wx.EXPAND)
self.Bind(wx.EVT_LISTBOX, self.NewsSelected, self.NewsListBox)
self.WrapSizer.Add(self.NewsListSizer, 1, wx.EXPAND)
return
# News Detail
def CreateRightNewsDetailSizer(self,html_text=""):
Sizer = wx.StaticBoxSizer(orient=wx.VERTICAL, parent=self, label="News Detail")
self.HTMLWindow = html.HtmlWindow(self,id=1); # HTMLWindowをNewsSelectedからアクセスできるようにclass全体の変数にした
Sizer.Add(self.HTMLWindow, 1, wx.EXPAND)
self.WrapSizer.Add(Sizer, 1, wx.EXPAND)
return
# ニュースが選択されたときに実行される関数
def NewsSelected(self, event):
self.UpdateNewsDetail(event.GetString())
#def SaveMenuHTML(self):
# print(self.CurrentHTML)
def UpdateNewsDetail(self,title):
self.CurrentHTML= self.News.GetDetailFromTitle(title);
if self.CurrentHTML == "":
self.CurrentHTML = "<h1>No News Selected</h1>";
if self.HTMLWindow == None: #self.HTMLWindowがCreateRightNewsDetailSizerによって定義される前にこの関数が実行されると「None」が代入されているので関数を終了する
return
self.HTMLWindow.SetPage(self.CurrentHTML);
def UpdateNewsListBox(self, url="",file=None):
if file != None:
self.News.UpdateFeed(f"file://{file}") # ArchNewsオブジェクトの情報をファイルから更新する
else:
self.News.UpdateFeed(url) # ArchNewsオブジェクトの情報を更新する
self.NewsListBox.SetItems(self.News.GetTitleList()) # ニュースリストを更新する
self.NewsListBox.SetSelection(0) # 先頭を選択状態にする
self.UpdateNewsDetail(self.NewsListBox.GetString(0)) # 先頭のニュースのタイトルを取得して右のHTMLを更新する
def SwitchLanguage(self):
global Japanese
if Japanese:
self.UpdateNewsListBox(self.News.RSSEnglish)
Japanese = False
else:
self.UpdateNewsListBox(self.News.RSSJapaese)
Japanese=True;
def GetCurrentFeedURL(self):
return self.News.CurrentFeedURL
class OpenDialog(wx.Dialog):
Path=None
BrowseSizer=None
ButtonSizer=None
WrapSizer=None
InputBox=None
BrowseButton=None
OKButton=None
CancelButton=None
def __init__(self):
wx.Dialog.__init__(self, None, -1, 'Title', size=(500,120))
# Sizerを作成
self.WrapSizer = wx.BoxSizer(wx.VERTICAL)
self.BrowseSizer = wx.BoxSizer(wx.HORIZONTAL)
self.ButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
# InputBoxを作成
self.BrowseSizer.Add(wx.StaticText(self, -1, 'File Name:'), 0, wx.ALIGN_CENTER_VERTICAL) #ラベル
self.InputBox = wx.TextCtrl(self, -1, '')
self.BrowseSizer.Add(self.InputBox, 1, wx.ALIGN_CENTER_VERTICAL)
# 参照ボタンを作成
self.BrowseButton = wx.Button(self, -1, '参照')
self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.BrowseButton)
self.BrowseSizer.Add(self.BrowseButton, 0, wx.ALIGN_CENTER_VERTICAL)
# OKボタンを作成
self.OKButton = wx.Button(self, wx.ID_OK, 'OK')
self.ButtonSizer.Add(self.OKButton, 0, wx.CENTER)
self.Bind(wx.EVT_BUTTON, self.OnOK, self.OKButton)
# Cancelボタンを作成
self.CancelButton = wx.Button(self, wx.ID_CANCEL, 'Cancel')
self.ButtonSizer.Add(self.CancelButton, 0, wx.CENTER )
self.Bind(wx.EVT_BUTTON, self.OnCancel, self.CancelButton)
# Sizerを設定
self.WrapSizer.Add(self.BrowseSizer, 1, wx.EXPAND)
self.WrapSizer.Add(self.ButtonSizer, 0, wx.EXPAND)
self.SetSizer(self.WrapSizer)
def OnBrowse(self, event):
BrowseDialog = wx.FileDialog(self, 'RSSファイルを指定してください',wildcard='RSS files (*.xml)|*.xml|All files(*)|*',style=wx.FD_OPEN)
if BrowseDialog.ShowModal() == wx.ID_OK:
self.Path = BrowseDialog.GetPath()
self.InputBox.SetValue(self.Path)
BrowseDialog.Destroy()
def OnOK(self, event):
self.EndModal(wx.ID_OK)
return
def OnCancel(self, event):
self.EndModal(wx.ID_CANCEL)
return
class MainWindowFrame(wx.Frame):
def __init__(self):
super().__init__(None, id=-1, title='wxPython')
self.SetSize(900, 500)
self.StatusBar = self.CreateStatusBar()
self.SetMenuBar(self.CreateMenuBar())
self.SetStatusText("ようこそ")
self.Panel = MainPanel(self)
self.Show()
def Menu_File_SaveHTML(self,event):
PathSelectDialog = wx.FileDialog(self, '保存先を選択してください', defaultDir=os.environ["HOME"], defaultFile="", wildcard="HTMLファイル(*.html)|*.html", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
if PathSelectDialog.ShowModal() == wx.ID_OK:
Path = PathSelectDialog.GetPath()
# 拡張子を追加
if not Path.endswith(".html"):
Path += ".html"
File = open(Path, "w")
File.write(self.Panel.CurrentHTML)
File.close()
return
def Menu_File_SaveRSS(self, event):
PathSelectDialog = wx.FileDialog(self, '保存先を選択してください', defaultDir=os.environ["HOME"], defaultFile="", wildcard="XMLファイル(*.xml)|*.xml", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
if PathSelectDialog.ShowModal() == wx.ID_OK:
Path = PathSelectDialog.GetPath()
# 拡張子を追加
if not Path.endswith(".xml"):
Path += ".xml"
urllib.request.urlretrieve(self.Panel.GetCurrentFeedURL(), Path)
return
# Todo: ファイルパスを入力できるようなテキストボックスも一緒に表示する
def Menu_File_Open(self,event):
#def BrowseRSSFile(self):
# with wx.FileDialog(self, 'RSSファイルを指定してください',wildcard='RSS files (*.xml)|*.xml|All files(*)|*',style=wx.FD_OPEN) as dialog:
# # Todo: 例外処理をちゃんと書く
# if dialog.ShowModal() == wx.ID_OK:
# return dialog.GetPaths()[0]
#self.Panel.UpdateNewsListBox(file=BrowseRSSFile(self))
Dialog = OpenDialog()
if Dialog.ShowModal() == wx.ID_OK:
self.Panel.UpdateNewsListBox(file=Dialog.Path)
return
# Todo: 例外処理をちゃんと書く
# Todo: Menu_File_Openと一緒にする
def Menu_File_URL(self,event):
URLDialog=wx.TextEntryDialog(None,"Copy and Paste below","heading","This is my text")
if URLDialog.ShowModal() == wx.ID_OK:
self.Panel.UpdateNewsListBox(URLDialog.GetValue())
return
def Menu_File_Switch(self,event):
self.SetStatusText("言語を切り替えています...")
self.Panel.SwitchLanguage()
self.SetStatusText("完了")
return
def Menu_File_Exit(self,event):
sys.exit(0)
def Menu_Help_Version(self,event):
# あとでウィジェットを配置したちゃんとしたダイアログで書き直す
VersionDialogBox = wx.MessageDialog(None, f'Version: {Version}\nCreated by Hayao Yamada', 'バージョン情報')
VersionDialogBox.ShowModal()
VersionDialogBox.Destroy()
def CreateMenuBar(self):
MenuBar = wx.MenuBar()
#-- ファイル --#
# メニュー作成
FileMenu = wx.Menu()
# 項目作成
FileMenuSaveHTML = FileMenu.Append(-1, "HTMLを保存", "ニュースをテキストファイルに保存します\tCtrl+S")
self.Bind(wx.EVT_MENU, self.Menu_File_SaveHTML, FileMenuSaveHTML)
FileMenuSaveRSS = FileMenu.Append(-1, "RSSを保存", "現在のRSSを保存します\tCtrl+X")
self.Bind(wx.EVT_MENU, self.Menu_File_SaveRSS, FileMenuSaveRSS)
FileMenuOpen = FileMenu.Append(-1, "開く", "RSSファイルを開きます\tCtrl+O")
self.Bind(wx.EVT_MENU, self.Menu_File_Open, FileMenuOpen)
FileMenuSwitch = FileMenu.Append(-1, "言語切り替え", "日本語と英語でニュースの言語を切り替えます")
self.Bind(wx.EVT_MENU, self.Menu_File_Switch, FileMenuSwitch)
FileMenuExit = FileMenu.Append(-1, "終了", "アプリケーションを終了します\tCtrl+Q")
self.Bind(wx.EVT_MENU, self.Menu_File_Exit, FileMenuExit)
# メニューバーに「ファイル」を追加
MenuBar.Append(FileMenu, "ファイル")
# ヘルプ
HelpMenu = wx.Menu()
#HelpMenu.Append(-1, "使い方", "オンラインで使い方を表示します\tCtrl+U")
#HelpMenu.Append(-1, "公式サイト", "Fascode Networkの公式サイトを開きます")
HelpMenuVersion = HelpMenu.Append(-1, "バージョン", 'バージョン情報を表示します\tCtrl+V')
self.Bind(wx.EVT_MENU, self.Menu_Help_Version, HelpMenuVersion)
MenuBar.Append(HelpMenu, "ヘルプ")
return MenuBar
if __name__ == '__main__':
App = wx.App()
Frame=MainWindowFrame()
App.SetTopWindow(Frame)
App.MainLoop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment