Handle Back Button

本文详细介绍了如何使用JavaScript中的'onbeforeunload'事件来控制浏览器的回退功能,确保用户登出后无法通过回退按钮查看之前的登录页面内容,从而提高网站的安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mastering The Back Button With Javascript

Filed: Wed, Feb 28 2007 under Programming|| Tags: ajax history secure sessions

Ask how to control the back button on a forum and you'll be quickly lectured how interfering with the browser's history is evil and that you're a bad person for wanting to do it. You'll then be told it's impossible. But, guess what? Both groups are wrong!

Almost everyone who asks how to control the back button is trying to make pages accessed while a user was logged in, inaccessible once a user has logged out. A good example of this is a banking page. If you log in, transfer a few funds, then log out, no one should be able to hit the back button a few time and see what you were doing.

So wanting to control the back button isn't an evil request. There are many, MANY legitimate uses for wanting to do so.

window.onbeforeunload

Around Internet Explorer version 4.0, Microsoft decided there needed to be a way for secure banking and e-commerce sites to have some control over the session history. They did this with the javascript event “onbeforeunload”, a very funky little method that is called right before the browser navigates to a new URL.

There are two things that happen when you set an onbeforeunload event. One, you can give the user a chance to re-think the decision to leave the page by spawning a "yes/no" dialog box; and two, just having an onbeforeunload event will cause the browser to never cache the page: This means that the page will always be rendered exactly as if the user was hitting it for the very first time.

“onbeforeunload” is a method of window (the global/root javascript object). You set up the event pretty much like any other event.

window.onbeforeunload = function () {
   
// stuff do do before the window is unloaded here.
}
Are you really, really sure you want to leave my glorious page?

What makes “onbeforeunload” quirky is that if you return ANYTHING at all in your event handler it will pop up a confirmation alert box asking the user if he or she is REALLY sure they want to leave the page. The exact text is:

Are you sure you want to navigate away from this page?

Click OK to continue, or Cancel to stay on the current page.

This dialog box will appear if you return ANYTHING in your function. return null, return false, return true, return "some text" will all bring up the confirmation dialog box.

You can insert your own explanatory text BETWEEN those two lines by including a string on the return statement. For instance:

window.onbeforeunload = function () {
   
return "You have not saved your document yet.  If you continue, your work will not be saved."
}

…will change the alert box to read...

Are you sure you want to navigate away from this page?

You have not saved your document yet.  If you continue, your work will not be saved.

Click OK to continue, or Cancel to stay on the current page.

As you can see, the starting and ending lines stay the same, your text is inserted into the middle.

If the user clicks ok, the onunload event will trigger and the browser will navigate to the new page. If the user clicks cancel the user will be returned to the current page where he or she left off.

You can see an example of this by clicking this link. If you click OK this timestamp should change (Timestamp: 1240067510515). If you click cancel, the timestamp should remain the same.

If you don't want a confirmation dialog box, simply do not include a return statement in your function. Just close the curly braces ( } ) naturally without a return statement.

window.onbeforeunload = function () {
   
// This fucntion does nothing.  It won't spawn a confirmation dialog
   
// But it will ensure that the page is not cached by the browser.
}
Detecting When The User Has Clicked Cancel

One of the things you may want to do is to be notified when the user clicks cancel, aborting a page unload. Unfortunately there's no way to be immediately notified. The best you can do is to set a unique global variable in your “onbeforeunload” event and then look to see if that variable has been set in other functions. There is no way to get an immediate notification that the user has aborted a page unload.

The example code I used above to do an example of an “onbeforeunload” dialog is as follows:

var _isset=0;

function demo() {
   window
.onbeforeunload = function () {
     
if (_isset==0) {
         _isset
=1;  // This will only be seen elsewhere if the user cancels.
         
return "This is a demonstration, you won't leave the page whichever option you select.";
     
}
   
}
   _isset
=0;
   window
.location.reload();
   
return false;
}

This code defines a global variabled named _isset, and then initializes it to zero. In our “onbeforeunload” event the variable is checked and if it's set to one, no unload dialog box will appear. The only way _isset could ever be one is if the user previously aborted a page unload.

But as you can see this method won't help you if you need to be immediately notified that that the user has finished dealing with the confirmation box. You can detect when it appears on the screen but there's no way to know when the user has finished interacting with it if the user clicked cancel (if the user clicked OK, then of course the unload event will have been tripped).

 

Truly Dynamic Pages

Just having an unbeforeunload event handler -- regardless of whether or not it actually does anything, regardless of whether or not you spawn a dialog box or not, even if the entire function declaration consists entirely of just { } -- just defining an event handler will prevent the page from being cached -- ever.

As a matter of fact, even if you allow page caching, the page will be not be cached. Having an “onbeforeunload” event means the page will be re-built every single time it is accessed. Javascripts will re-run, server-side scripts will be re-run, the page will be built as if the user was hitting it for the very first time, even if the user got to the page just by hitting the back or forward button.

Some security considerations.

Although “onbeforeunload” ensures the page will be fresh each time, you may still want to issue meta commands and server-side headers to control the cache -- just to be sure the browser doesn't store a copy on the sly. You can do this with server side headers (php examples here)...

header("Cache-Control: no-cache, must-revalidate");
header
("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header
("Pragma: no-cache");

And with browser meta tags (not as reliable but it never hurts to try).

<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
<META HTTP-EQUIV="EXPIRES" CONTENT="01 Jan 1970 00:00:00 GMT">
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">

Now in the “what were they thinking” department, you want to put cache control meta tags in a <head> section below the <body> tag. Microsoft has a good writeup on why this is so. Just mark this up as yet another browser idiosyncrasy to contend with.

Good cache-control will reduce the chance that someone can go through the physical cache on the user's machine and extract sensitive information.

Logged out? No page for you!

Since “onbeforeunload” event pages are not cached and are rebuilt each time the user access it, it means your server side script can determine if the user is logged in or not and serve the appropriate pages. You can even see if the session has timed out and redirect the user to the login page.

Shutdown Nicely

In addition to disabling page caching, “onbeforeunload” gives you a chance to nicely handle unload events. For instance you could submit the current form data, marked as incomplete and rebuild it if the user comes back to the page. Another good example of this is handling session logging. By incorporating a small synchronous AJAX call you can log when the user left the page, giving you an idea of how long each user is staying on the page. If you don't set up a confirmation dialog, this activity would be completely transparent to the user. Here's a simple template you can use to do just that.

window.onbeforeunload = function() {
   
// This template uses no error checking, it's just concept code to be
   
// expanded on.
         
   
// Create a new XMLHttpRequest object
   
var AJAX=new XMLHttpRequest();  
         
   
// Handle ready state changes ( ignore them until readyState = 4 )
   AJAX
.onreadystatechange= function() { if (AJAX.readyState!=4) return false; }
         
   
// we're passing false so this is a syncronous request.
   
// The script will stall until the document has been loaded.
   
// the open statement depends on a global variable titled _userID.
   AJAX
.open("GET", 'http://someurl.com/endsession.php?id='+_userID, false);
   AJAX
.send(null);
}

The choice to use a synchronous call is deliberate and important. A synchronous request will stall the browser until it gets a reply back from the server. If we were to initiate an asynchronous request the request would be made but the browser would continue the unload event and chances are the browser would never get the response back from the server. So in your unload events you need to keep AJAX synchronous.

Browser Check

“onbeforeunload” was introduced with Internet Explorer 4, so it has pretty good support. A lot of users surf without javascript however so if you decide to use “onbeforeunload” to help manage your session you'll need to make extensive use of <noscript></noscript> and dynamic HTML generation through javascript to nudge users into enabling javascript, at least for your pages. On the server side you'll also need to do some browser checking to ensure the browser supports “onbeforeunload”.

You can't change history

The history object, for good reason, has been put into a security lockdown. You can't use it to modify the history. You can however have some small and very imperfect control over the current url.

location.replace(url)

Using the command above you can replace the current page with the url of your choice, this replaces the current URL in the user's history as well. If the page is VERY sensitive you can use this when the user opts to log out. Firefox will let you use location.replace(URL) right before the return statement in an “onbeforeunload” event. That is, if you do a location.replace and then spawn a confirmation box, the current page will be altered.

window.onbeforeunload = function () {
   location
.replace('http://www.google.com');
   
return "This session is expired and the history altered.";
}

It's kind of clunky in that if the user opts to cancel he'll be on the replacement page instead of the original page, and the replacement page will actually be loaded as he ponders the confirmation dialog. If he selects “OK” however he'll navigate forward normally and if he hits the “back” button he'll be taken to the replacement URL.

It's important to note than in Internet Explorer 6 that you can not use a location method at all in any unload event, so the trick of modifying the history of the current page will work (kinda) only in Firefox. It's not something you can depend on.

If you MUST control History

If you absolutely must be able to clear out the history, then spawn a new window (without a location line) and when the user is done, close the window. The URLs will still be in the browser's history list ( cntrl-h ), but the session's backward and forward button list will have been destroyed.

Conclusion

Really, manipulation of the browser's history is unnecessary as long as you are dynamically generating your web pages and you use “onbeforeunload” events to ensure the page will always be fresh. If the page is being dynamically generated then even if someone goes through the history list and clicks on one of your session URLs, it won't really matter because the page will be rebuilt from scratch meaning you can check to see if the session has expired and if the user is logged in or not. If the check is failed, you simply re-direct the browser to a login page.

And there you go! Everything you need to know to ensure secure sessions, stay secure and private, no matter who sits down at the browser an hour later.

 

 

From:http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript

强调:下面的代码是能够运行,现在修改,只是局部修改问题,不要胡乱修改。。。修改时一定要说明在什么类的什么地方,这样不要用户进行修改。找准位置,认真修改,本着用户至上的原则。生成完整代码。。。问题:修改要求:用户原来设计主界面是三栏布局(包括左侧面板、右侧面板和中间区域),中间区域(中间区域分为两个框),现在要求确保动态区严格作为中间区域的第二个(从上往下数),用户在运行代码时就发现:错误的问题是每个模块的名称,标签及条目占了整个界面的下半部分,这是一个严重的错误,,用户现在要求把子类界面改成与主界面一样(也是三栏布局(包括左侧面板、右侧面板和中间区域)。只是每个模块的名称,标签及条目必须完全嵌套在动态区框里面(中间区域往下的第二个框里面),在动态区里面,举例说明,也就是在有四个按钮的框里。。。。原代码# ==================== 主界面修改 ==================== class MainInterface: def init(self, root: Tk, pool: NumberPool): self.root = root self.pool = pool self.left_panel = None self.center_frame = None self.right_panel = None self.core_vars = {} self.pool_vars = {} self.status_var = StringVar() self.dynamic_text = None self.current_module = None self._setup_ui() self._setup_event_handlers() self.module_instances = {} self._init_modules() # 初始化排除号码相关控件 self.exclude_front_entries = [] self.exclude_back_entries = [] self.front_dan_entries = [] self.back_dan_entries = [] # 初始化结果文本控件 self.result_text = None # 初始化排除号码变量 self.exclude_front_var = StringVar() self.exclude_back_var = StringVar() self.recommend_front_var = StringVar() self.recommend_back_var = StringVar() # 初始化模块内容框架 self.dynamic_content = None self.module_content_frame = None self.labels = { 'InputAnalysis_analysis': [ "输入号码:" , "前区:" , "后区:" , "推荐号码:", "前区:" , "后区:", ], 'combination_analysis': [ "前区热号:", "前数字频:", "前频繁推:", "后区热号:", "后数字频:", "后低频推:" ], 'follow_analysis': [ "前推荐多:", "前推荐少:", "后推荐多:", "后推荐少:" ], 'trend_analysis': [ "和值:", "质合比:", "奇偶比:", "断区推荐:", "连号推荐:", "冷热推荐:", "后区热号:", "后区冷号:", "趋势号:" ], 'NumberGeneration_analysis': [ "胆码:" , "前区:" , "后区:", "推荐5注号码:", "1:" "", "2:" "", "3:" "", "4:" "", "5:" "" ], } # 初始化所有模块的条目引用 self.front_dan_entry = None self.back_dan_entry = None self.result_text = None self.exclude_front_entry = None self.exclude_back_entry = None self.front_entry = None self.back_entry = None def _focus_adjacent_entry(self, event, current_idx, offset, area): """移动焦点到相邻的输入框""" entries = self.front_exclude_entries if area == 'front' else self.back_exclude_entries new_idx = current_idx + offset if 0 <= new_idx < len(entries): entries[new_idx].focus_set() def _init_modules(self): """初始化所有分析模块""" modules = { 'input_analysis': InputAnalysisModule, 'combination_analysis': CombinationAnalysisModule, 'follow_analysis': FollowAnalysisModule, 'trend_analysis': TrendAnalysisModule, 'number_generation': NumberGenerationModule } for name, cls in modules.items(): self.module_instances[name] = cls(name) def _setup_event_handlers(self): """初始化事件处理器""" event_center.subscribe(EventType.MODULE_COMPLETE, self._handle_module_complete) event_center.subscribe(EventType.UI_UPDATE, self._handle_ui_update) event_center.subscribe(EventType.EXCLUDE_NUMBERS, self._handle_exclude_numbers) def _setup_ui(self): self.root.title(f"大乐透智能分析平台 - {GlobalConfig.VERSION}") self.root.geometry("1400x800") # 添加主标题 title_frame = Frame(self.root) title_frame.pack(fill='x', pady=5) Label(title_frame, text="大乐透智能分析平台", font=('微软雅黑', 16, 'bold')).pack(expand=True) # 主容器 - 三栏布局 main_container = PanedWindow(self.root, orient=HORIZONTAL, sashrelief=RAISED, sashwidth=5) main_container.pack(fill='both', expand=True, padx=5, pady=(0, 5)) # 左侧面板 self.left_panel = Frame(main_container, width=200, bg="#eaeaea") main_container.add(self.left_panel, minsize=150, stretch="never") # 中间内容区 self.center_paned = PanedWindow(main_container, orient=VERTICAL, sashrelief=RAISED, sashwidth=5) main_container.add(self.center_paned, minsize=500, stretch="always") # 右侧面板 self.right_panel = Frame(main_container, width=700, bg="#f5f5f5") main_container.add(self.right_panel, minsize=250, stretch="never") # 初始化各区域 self._setup_left_panel() # 修正方法名 self._setup_center_area() # 修正方法名 self._setup_right_panel() def _setup_left_panel(self): # 修正方法名,添加缺失的"t" """初始化左侧模块按钮区""" module_names = { 'input_analysis': '1. 输入分析', 'combination_analysis': '2. 组合分析', 'follow_analysis': '3. 跟随分析', 'trend_analysis': '4. 趋势分析', 'number_generation': '5. 数字生成' } for module in GlobalConfig.MODULES: Button( self.left_panel, text=module_names[module], width=18, command=lambda m=module: self._on_module_button_click(m) ).pack(pady=3, padx=5, ipady=3) def _setup_center_area(self): """设置中间区域布局,分为上下两部分""" # 上半部分 - 核心区 self.core_frame = Frame(self.center_paned, bd=1, relief='solid') self.center_paned.add(self.core_frame, minsize=150, stretch="never") # 核心区固定高度 # 核心区标题 Label(self.core_frame, text="核心区", font=('微软雅黑', 12, 'bold')).pack(anchor='w', padx=5, pady=2) # 核心数据展示 self.core_vars = { 'front_area': StringVar(), 'back_area': StringVar(), 'front_hot': StringVar(), 'front_cold': StringVar(), 'back_hot': StringVar(), 'back_cold': StringVar() } for label, var_name in [ ("前区:", 'front_area'), ("后区:", 'back_area'), ("前区热号:", 'front_hot'), ("前区冷号:", 'front_cold'), ("后区热号:", 'back_hot'), ("后区冷号:", 'back_cold') ]: frame = Frame(self.core_frame) frame.pack(fill='x', padx=5, pady=2) Label(frame, text=label, width=10, anchor='w').pack(side='left') entry_container = Frame(frame) entry_container.pack(side='left', fill='x', expand=True, padx=(0, 2)) entry = Entry(entry_container, textvariable=self.core_vars[var_name], font=('微软雅黑', 10), state='readonly', readonlybackground='#f0f0f0', relief='sunken', bd=1) entry.pack(fill='x', expand=True) # 下半部分 - 动态区 (严格限制在中间区域下半部分) self.dynamic_frame = Frame(self.center_paned, bd=1, relief='solid') self.center_paned.add(self.dynamic_frame, minsize=200, stretch="always") # 动态区可扩展 # 动态区内部容器 - 确保所有模块内容严格嵌套在此框架内 dynamic_container = Frame(self.dynamic_frame) dynamic_container.pack(fill='both', expand=True, padx=5, pady=5) # 动态区标题框架 dynamic_header = Frame(dynamic_container) dynamic_header.pack(fill='x', pady=5) Label(dynamic_header, text="动态区", font=('微软雅黑', 12, 'bold')).pack(side='left') # 按钮框架 btn_frame = Frame(dynamic_header) btn_frame.pack(side='right') Button(btn_frame, text="运行", width=8, command=self._run_current_module).pack(side='left', padx=2) Button(btn_frame, text="清除", width=8, command=self._clear_dynamic_content).pack(side='left', padx=2) Button(btn_frame, text="保存", width=8, command=self._save_dynamic_content).pack(side='left', padx=2) Button(btn_frame, text="刷新", width=8, command=self._refresh_dynamic).pack(side='left', padx=2) # 模块内容容器 - 严格嵌套在动态区内 self.module_content_frame = Frame(dynamic_container) self.module_content_frame.pack(fill='both', expand=True) # 初始化动态区内容为空白 self.dynamic_content = Frame(self.module_content_frame) self.dynamic_content.pack(fill='both', expand=True) def _on_module_button_click(self, module: str): """模块切换 - 严格在动态区内变化""" self.status_var.set(f"打开 {module} 模块...") self.current_module = module # 清除之前的动态内容 - 仅限动态区 if self.dynamic_content: self.dynamic_content.destroy() # 创建新的动态内容容器 - 严格嵌套在module_content_frame内 self.dynamic_content = Frame(self.module_content_frame) self.dynamic_content.pack(fill='both', expand=True, padx=5, pady=5) # 顶部模块标签区 - 在动态区内 top_label_frame = Frame(self.dynamic_content) top_label_frame.pack(fill='x', pady=5) module_labels = { 'input_analysis': '1. 输入分析', 'combination_analysis': '2. 组合分析', 'follow_analysis': '3. 跟随分析', 'trend_analysis': '4. 趋势分析', 'number_generation': '5. 数字生成' } Label(top_label_frame, text=module_labels.get(module, module), font=('微软雅黑', 14, 'bold')).pack() # 内容区容器 - 在动态区内 content_frame = Frame(self.dynamic_content, bd=1, relief='solid') content_frame.pack(fill='both', expand=True, padx=5, pady=5) # 根据模块类型创建特定内容 - 严格在动态区内 if module == "input_analysis": self._create_input_analysis_content(content_frame) elif module == "combination_analysis": self._create_combination_analysis_content(content_frame) elif module == "follow_analysis": self._create_follow_analysis_content(content_frame) elif module == "trend_analysis": self._create_trend_analysis_content(content_frame) elif module == "number_generation": self._create_number_generation_content(content_frame) # 底部按钮区 - 在动态区内 bottom_frame = Frame(self.dynamic_content) bottom_frame.pack(pady=5, fill='x') Button(bottom_frame, text="运行", command=lambda: self._run_module(module)).pack(side='left', padx=5) Button(bottom_frame, text="清除", command=lambda: self._clear_module_data(module)).pack(side='left', padx=5) Button(bottom_frame, text="保存", command=lambda: self._save_module_data(module)).pack(side='left', padx=5) Button(bottom_frame, text="刷新", command=lambda: self._on_module_renovate(module)).pack(side='right', padx=5) # 模块内容容器 - 这是动态区的核心内容区域 self.module_content_frame = Frame(self.dynamic_frame) self.module_content_frame.pack(fill='both', expand=True, padx=5, pady=5) def _run_current_module(self): """运行当前模块""" if self.current_module: self._run_module(self.current_module) def _clear_dynamic_content(self): """清除动态区内容""" if hasattr(self, 'result_text') and self.result_text: self.result_text.delete(1.0, 'end') def _save_dynamic_content(self): """保存动态区内容""" if hasattr(self, 'result_text') and self.result_text: content = self.result_text.get(1.0, 'end') with open('dynamic_content.txt', 'w', encoding='utf-8') as f: f.write(content) messagebox.showinfo("保存成功", "动态区内容已保存") def _refresh_dynamic(self): """刷新动态区""" if self.current_module: self._on_module_button_click(self.current_module) def _setup_right_panel(self): """设置右侧号码池布局""" # 号码池标题 pool_title_frame = Frame(self.right_panel) pool_title_frame.pack(fill='x', pady=5) Label(pool_title_frame, text="号码池", font=('微软雅黑', 12, 'bold')).pack(anchor='w') # 号码池内容区(添加边框和2px内边距)pool_content = Frame(self.right_panel, bd=1, relief='solid', padx=2, pady=2) pool_content.pack(fill='both', expand=True, padx=5, pady=5) # 创建Canvas和Scrollbar canvas = Canvas(pool_content, highlightthickness=0) scrollbar = Scrollbar(pool_content, orient="vertical", command=canvas.yview) scrollable_frame = Frame(canvas) scrollable_frame.bind( "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")) ) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) # 号码池项目 - 优化布局和样式(带2px右边距)for label, var_name, row_id in GlobalConfig.UI_CONFIG: frame = Frame(scrollable_frame) frame.grid(row=row_id, column=0, sticky='ew', padx=0, pady=1) # 移除水平padding # 左侧标签(固定宽度8字符)lbl = Label(frame, text=label, width=8, anchor='w') lbl.pack(side='left', padx=(0, 5)) # 标签右侧留5px间距 # 右侧输入框容器(带2px右边距)entry_container = Frame(frame) entry_container.pack(side='left', fill='x', expand=True, padx=(0, 2)) var = StringVar() self.pool_vars[var_name] = var entry = Entry(entry_container, textvariable=var, font=('微软雅黑', 9), state='readonly', readonlybackground='#f0f0f0', relief='sunken', bd=1) entry.pack(fill='x', expand=True) canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") # 底部按钮区 btn_frame = Frame(self.right_panel) btn_frame.pack(fill='x', pady=5) Button(btn_frame, text="整理", width=10, command=self._organize_data).pack(side='left', padx=5) Button(btn_frame, text="冻结", width=10, command=self._freeze_data).pack(side='left', padx=5) Button(btn_frame, text="导出", width=10).pack(side='left', padx=5) def _organize_data(self): """整理号码池数据""" organize_event = Event( event_id=int(time.time()), type=EventType.DATA_ORGANIZE, source='main_ui', target='pool' ) event_center.publish(organize_event) def _freeze_data(self): """冻结号码池数据""" freeze_event = Event( event_id=int(time.time()), type=EventType.DATA_FREEZE, source='main_ui', target='pool' ) event_center.publish(freeze_event) def _on_module_button_click(self, module: str): """完全重构的模块显示方法 - 嵌入主界面动态区""" self.status_var.set(f"打开 {module} 模块...") self.current_module = module # 清除之前的动态内容 if self.dynamic_content: self.dynamic_content.destroy() # 创建模块专属容器 self.dynamic_content = Frame(self.module_content_frame) self.dynamic_content.pack(fill='both', expand=True, padx=5, pady=5) # 顶部模块标签区 top_label_frame = Frame(self.dynamic_content) top_label_frame.pack(fill='x', pady=5) module_labels = { 'input_analysis': '1. 输入分析', 'combination_analysis': '2. 组合分析', 'follow_analysis': '3. 跟随分析', 'trend_analysis': '4. 趋势分析', 'number_generation': '5. 数字生成' } Label(top_label_frame, text=module_labels.get(module, module), font=('微软雅黑', 14, 'bold')).pack() # 内容区容器 content_frame = Frame(self.dynamic_content, bd=1, relief='solid') content_frame.pack(fill='both', expand=True, padx=5, pady=5) # 根据模块类型创建特定内容 if module == "input_analysis": self._create_input_analysis_content(content_frame) elif module == "combination_analysis": self._create_combination_analysis_content(content_frame) elif module == "follow_analysis": self._create_follow_analysis_content(content_frame) elif module == "trend_analysis": self._create_trend_analysis_content(content_frame) elif module == "number_generation": self._create_number_generation_content(content_frame) # 底部按钮区 bottom_frame = Frame(self.dynamic_content) bottom_frame.pack(pady=5, fill='x') Button(bottom_frame, text="运行", command=lambda: self._run_module(module)).pack(side='left', padx=5) Button(bottom_frame, text="清除", command=lambda: self._clear_module_data(module)).pack(side='left', padx=5) Button(bottom_frame, text="保存", command=lambda: self._save_module_data(module)).pack(side='left', padx=5) Button(bottom_frame, text="刷新", command=lambda: self._on_module_renovate(module)).pack(side='right', padx=5) def _create_ui_element(self, parent, label_text): """创建统一的UI元素(带右边距2px)""" frame = Frame(parent) frame.pack(fill='x', pady=2) # 标签固定宽度 Label(frame, text=label_text, width=12, anchor='w').pack(side='left') # 添加容器Frame实现右边距2px entry_container = Frame(frame) entry_container.pack(side='left', fill='x', expand=True, padx=(0, 2)) return entry_container def _create_input_analysis_content(self, parent: Frame): """创建输入分析模块的特定内容""" content_frame = Frame(parent) content_frame.pack(fill='both', expand=True, padx=10, pady=10) # 排除号码区 exclude_frame = Frame(content_frame) exclude_frame.pack(fill='x', pady=5) # 排除号码标签 Label(exclude_frame, text="排除号码:", font=('微软雅黑', 10, 'bold')).pack(anchor='w', pady=5) # 前区排除号码 front_exclude_frame = Frame(exclude_frame) front_exclude_frame.pack(fill='x', pady=2) Label(front_exclude_frame, text="前区:", width=5, anchor='w').pack(side='left') self.exclude_front_entry = Entry(front_exclude_frame) self.exclude_front_entry.pack(side='left', fill='x', expand=True) # 后区排除号码 back_exclude_frame = Frame(exclude_frame) back_exclude_frame.pack(fill='x', pady=2) Label(back_exclude_frame, text="后区:", width=5, anchor='w').pack(side='left') self.exclude_back_entry = Entry(back_exclude_frame) self.exclude_back_entry.pack(side='left', fill='x', expand=True) # 号码输入区 input_frame = Frame(content_frame) input_frame.pack(fill='x', pady=5) Label(input_frame, text="输入号码:", font=('微软雅黑', 10, 'bold')).pack(anchor='w', pady=5) # 前区号码 front_frame = Frame(input_frame) front_frame.pack(fill='x', pady=2) Label(front_frame, text="前区:", width=5, anchor='w').pack(side='left') self.front_entry = Entry(front_frame) self.front_entry.pack(side='left', fill='x', expand=True) # 后区号码 back_frame = Frame(input_frame) back_frame.pack(fill='x', pady=2) Label(back_frame, text="后区:", width=5, anchor='w').pack(side='left') self.back_entry = Entry(back_frame) self.back_entry.pack(side='left', fill='x', expand=True) # 结果显示区 result_frame = Frame(content_frame) result_frame.pack(fill='both', expand=True, pady=5) scrollbar = Scrollbar(result_frame) scrollbar.pack(side='right', fill='y') self.result_text = Text(result_frame, yscrollcommand=scrollbar.set, wrap='word') self.result_text.pack(fill='both', expand=True) scrollbar.config(command=self.result_text.yview) def _create_combination_analysis_content(self, parent: Frame): """创建组合分析模块的特定内容""" content_frame = Frame(parent) content_frame.pack(fill='both', expand=True, padx=10, pady=10) # 使用预定义的labels for label in self.labels['combination_analysis']: frame = Frame(content_frame) frame.pack(fill='x', pady=2) Label(frame, text=label, width=12, anchor='w', font=('微软雅黑', 10, 'bold')).pack(side='left') entry = Entry(frame, width=30, state='readonly', readonlybackground='#f0f0f0') entry.pack(side='left', padx=5) # 保存对控件的引用 var_name = label.replace(':', '').replace(' ', '_') setattr(self, f"{var_name}_entry", entry) # 直接保存到实例变量 if var_name == "front_hot": self.front_hot_entry = entry elif var_name == "front_freq": self.front_freq_entry = entry elif var_name == "front_freq_rec": self.front_freq_rec_entry = entry elif var_name == "back_hot": self.back_hot_entry = entry elif var_name == "back_freq": self.back_freq_entry = entry elif var_name == "back_infreq_rec": self.back_infreq_rec_entry = entry # 结果显示区 result_frame = Frame(content_frame) result_frame.pack(fill='both', expand=True) scrollbar = Scrollbar(result_frame) scrollbar.pack(side='right', fill='y') self.result_text = Text(result_frame, yscrollcommand=scrollbar.set, wrap='word') self.result_text.pack(fill='both', expand=True) scrollbar.config(command=self.result_text.yview) def _create_follow_analysis_content(self, parent: Frame): """创建跟随分析模块的特定内容""" content_frame = Frame(parent) content_frame.pack(fill='both', expand=True, padx=10, pady=10) for label in self.labels['follow_analysis']: frame = Frame(content_frame) frame.pack(fill='x', pady=2) Label(frame, text=label, width=12, anchor='w', font=('微软雅黑', 10, 'bold')).pack(side='left') entry = Entry(frame, width=30, state='readonly', readonlybackground='#f0f0f0') entry.pack(side='left', padx=5) var_name = label.replace(':', '').replace(' ', '_') setattr(self, f"{var_name}_entry", entry) # 直接保存到实例变量 if var_name == "front_more": self.front_more_entry = entry elif var_name == "front_less": self.front_less_entry = entry elif var_name == "back_more": self.back_more_entry = entry elif var_name == "back_less": self.back_less_entry = entry # 结果显示区 result_frame = Frame(content_frame) result_frame.pack(fill='both', expand=True) scrollbar = Scrollbar(result_frame) scrollbar.pack(side='right', fill='y') self.result_text = Text(result_frame, yscrollcommand=scrollbar.set, wrap='word') self.result_text.pack(fill='both', expand=True) scrollbar.config(command=self.result_text.yview) def _create_trend_analysis_content(self, parent: Frame): """创建趋势分析模块的特定内容""" content_frame = Frame(parent) content_frame.pack(fill='both', expand=True, padx=10, pady=10) for label in self.labels['trend_analysis']: frame = Frame(content_frame) frame.pack(fill='x', pady=2) Label(frame, text=label, width=12, anchor='w', font=('微软雅黑', 10, 'bold')).pack(side='left') entry = Entry(frame, width=30, state='readonly', readonlybackground='#f0f0f0') entry.pack(side='left', padx=5) var_name = label.replace(':', '').replace(' ', '_') setattr(self, f"{var_name}_entry", entry) # 直接保存到实例变量 if var_name == "sum_value": self.sum_value_entry = entry elif var_name == "prime_ratio": self.prime_ratio_entry = entry elif var_name == "odd_even_ratio": self.odd_even_ratio_entry = entry elif var_name == "zone_rec": self.zone_rec_entry = entry elif var_name == "consec_rec": self.consec_rec_entry = entry elif var_name == "hot_cold_rec": self.hot_cold_rec_entry = entry elif var_name == "hot_rec": self.hot_rec_entry = entry elif var_name == "cold_rec": self.cold_rec_entry = entry elif var_name == "trend_rec": self.trend_rec_entry = entry # 结果显示区 result_frame = Frame(content_frame) result_frame.pack(fill='both', expand=True) scrollbar = Scrollbar(result_frame) scrollbar.pack(side='right', fill='y') self.result_text = Text(result_frame, yscrollcommand=scrollbar.set, wrap='word') self.result_text.pack(fill='both', expand=True) scrollbar.config(command=self.result_text.yview) def _create_number_generation_content(self, parent: Frame): """创建数字生成模块的动态内容""" content_frame = Frame(parent) content_frame.pack(fill='both', expand=True, padx=10, pady=10) # 胆码输入区 dan_frame = Frame(content_frame) dan_frame.pack(fill='x', pady=5) # 前区胆码 front_dan_frame = Frame(dan_frame) front_dan_frame.pack(fill='x') Label(front_dan_frame, text="前区胆码:").pack(side='left') self.front_dan_entries = [] for i in range(5): entry = Entry(front_dan_frame, width=3) entry.pack(side='left', padx=2) self.front_dan_entries.append(entry) self.front_dan_entry = self.front_dan_entries[0] # 保存第一个条目引用 # 后区胆码 back_dan_frame = Frame(dan_frame) back_dan_frame.pack(fill='x') Label(back_dan_frame, text="后区胆码:").pack(side='left') self.back_dan_entries = [] for i in range(5): entry = Entry(back_dan_frame, width=3) entry.pack(side='left', padx=2) self.back_dan_entries.append(entry) self.back_dan_entry = self.back_dan_entries[0] # 保存第一个条目引用 # 生成的号码显示区 generated_frame = Frame(content_frame) generated_frame.pack(fill='x', pady=5) Label(generated_frame, text="生成号码:").pack(anchor='w') self.generated_labels = [] for i in range(1, 6): frame = Frame(generated_frame) frame.pack(fill='x') Label(frame, text=f"{i}.").pack(side='left') label = Label(frame, text="", width=30, anchor='w') label.pack(side='left') self.generated_labels.append(label) # 结果显示区 result_frame = Frame(content_frame) result_frame.pack(fill='both', expand=True) scrollbar = Scrollbar(result_frame) scrollbar.pack(side='right', fill='y') self.result_text = Text(result_frame, yscrollcommand=scrollbar.set, wrap='word') self.result_text.pack(fill='both', expand=True) scrollbar.config(command=self.result_text.yview) def _run_module(self, module: str): """运行模块""" if module == "input_analysis": # 获取排除号码 exclude_front = self.exclude_front_entry.get() exclude_back = self.exclude_back_entry.get() # 发布排除号码事件 exclude_event = Event( event_id=int(time.time()), type=EventType.EXCLUDE_NUMBERS, source='main_ui', target='pool', data={ 'exclude_front': exclude_front, 'exclude_back': exclude_back } ) event_center.publish(exclude_event) # 在结果文本中记录 self.result_text.insert('end', f"已设置排除号码: 前区 {exclude_front}, 后区 {exclude_back}\n") # 发布模块运行事件 run_event = Event( event_id=int(time.time()), type=EventType.MODULE_RUN, source='main_ui', target=module ) event_center.publish(run_event) def _generate_recommend_numbers(self, exclude_front: str, exclude_back: str): """生成推荐号码(示例逻辑)""" # 实际应用中应调用分析模块生成推荐号码 # 这里简化为生成随机推荐号码 import random # 前区号码范围1-35 all_front = [str(idx) for idx in range(1, 36)] exclude_front_list = exclude_front.split() if exclude_front else [] available_front = [num for num in all_front if num not in exclude_front_list] # 后区号码范围1-12 all_back = [str(idx) for idx in range(1, 13)] exclude_back_list = exclude_back.split() if exclude_back else [] available_back = [num for num in all_back if num not in exclude_back_list] # 随机选择5个前区号码 if len(available_front) >= 5: recommend_front = random.sample(available_front, 5) else: recommend_front = random.sample(all_front, 5) # 随机选择2个后区号码 if len(available_back) >= 2: recommend_back = random.sample(available_back, 2) else: recommend_back = random.sample(all_back, 2) # 更新推荐号码显示 self.recommend_front_var.set(' '.join(sorted(recommend_front, key=int))) self.recommend_back_var.set(' '.join(sorted(recommend_back, key=int))) # 在结果文本中记录 self.result_text.insert('end', f"生成推荐号码: 前区 {self.recommend_front_var.get()}, 后区 {self.recommend_back_var.get()}\n") # 更新号码池 self._update_pool_with_recommendations(self.recommend_front_var.get(), self.recommend_back_var.get()) def _update_pool_with_recommendations(self, front: str, back: str): """用推荐号码更新号码池""" # 发布事件更新号码池 update_event = Event( event_id=int(time.time()), type=EventType.POOL_UPDATE, source='input_analysis', target='pool', data={ 'front_numbers': front, 'back_numbers': back } ) event_center.publish(update_event) # 在结果文本中记录 self.result_text.insert('end', "号码池已更新\n") def _clear_module_data(self, module: str): """清除模块数据""" if module == "input_analysis": if hasattr(self, 'front_entry') and self.front_entry: self.front_entry.delete(0, 'end') if hasattr(self, 'back_entry') and self.back_entry: self.back_entry.delete(0, 'end') if hasattr(self, 'exclude_front_entry') and self.exclude_front_entry: self.exclude_front_entry.delete(0, 'end') if hasattr(self, 'exclude_back_entry') and self.exclude_back_entry: self.exclude_back_entry.delete(0, 'end') if hasattr(self, 'recommend_front_var'): self.recommend_front_var.set('') if hasattr(self, 'recommend_back_var'): self.recommend_back_var.set('') if hasattr(self, 'result_text') and self.result_text: self.result_text.delete(1.0, 'end') elif module == "combination_analysis": if hasattr(self, 'front_hot_entry') and self.front_hot_entry: self.front_hot_entry.delete(0, 'end') if hasattr(self, 'front_freq_entry') and self.front_freq_entry: self.front_freq_entry.delete(0, 'end') if hasattr(self, 'front_freq_rec_entry') and self.front_freq_rec_entry: self.front_freq_rec_entry.delete(0, 'end') if hasattr(self, 'back_hot_entry') and self.back_hot_entry: self.back_hot_entry.delete(0, 'end') if hasattr(self, 'back_freq_entry') and self.back_freq_entry: self.back_freq_entry.delete(0, 'end') if hasattr(self, 'back_infreq_rec_entry') and self.back_infreq_rec_entry: self.back_infreq_rec_entry.delete(0, 'end') if hasattr(self, 'result_text') and self.result_text: self.result_text.delete(1.0, 'end') def _save_module_data(self, module: str): """保存模块数据""" try: data = {} if module == "input_analysis": data['front'] = self.front_entry.get() data['back'] = self.back_entry.get() data['exclude_front'] = self.exclude_front_entry.get() data['exclude_back'] = self.exclude_back_entry.get() data['recommend_front'] = self.recommend_front_var.get() data['recommend_back'] = self.recommend_back_var.get() data['result'] = self.result_text.get(1.0, 'end') elif module == "combination_analysis": data['front_hot'] = self.front_hot_entry.get() data['front_freq'] = self.front_freq_entry.get() data['front_freq_rec'] = self.front_freq_rec_entry.get() data['back_hot'] = self.back_hot_entry.get() data['back_freq'] = self.back_freq_entry.get() data['back_infreq_rec'] = self.back_infreq_rec_entry.get() data['result'] = self.result_text.get(1.0, 'end') # 其他模块数据收集... filename = f"{module}_data.json" with open(filename, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) messagebox.showinfo("保存成功", f"数据已保存到{filename}") except Exception as e: messagebox.showerror("保存失败", str(e)) logging.error(f"保存数据失败: {str(e)}", exc_info=True) def _handle_exclude_numbers(self, event: Event): """处理排除号码事件""" if event.data: exclude_front = event.data.get('exclude_front', '') exclude_back = event.data.get('exclude_back', '') # 更新排除号码显示 self.exclude_front_entry.delete(0, 'end') self.exclude_front_entry.insert(0, exclude_front) self.exclude_back_entry.delete(0, 'end') self.exclude_back_entry.insert(0, exclude_back) # 在结果文本中记录 self.result_text.insert('end', f"收到排除号码: 前区 {exclude_front}, 后区 {exclude_back}\n") def _handle_module_complete(self, event: Event): self.status_var.set(f"{event.source} 模块运行完成") if hasattr(self, 'result_text') and self.result_text: self.result_text.insert('end', f"\n{event.source} 模块已完成分析\n") def _on_module_renovate(self, module: str): """刷新模块""" if module == self.current_module: self._on_module_button_click(module) def _handle_ui_update(self, event: Event): """处理UI更新事件""" if not event.data or 'update_type' not in event.data: return update_type = event.data['update_type'] data = event.data.get('data', {}) # 处理核心变量更新 if update_type == 'organized_data': # 确保所有核心变量已初始化 if not hasattr(self, 'core_vars'): self.core_vars = { 'front_area': StringVar(), 'back_area': StringVar(), 'front_hot': StringVar(), 'front_cold': StringVar(), 'back_hot': StringVar(), 'back_cold': StringVar() } # 更新界面变量 self.core_vars['front_area'].set(str(data.get('front_numbers', []))) self.core_vars['back_area'].set(str(data.get('back_numbers', []))) self.core_vars['front_hot'].set(str(data.get('front_hot', []))) self.core_vars['front_cold'].set(str(data.get('front_cold', []))) self.core_vars['back_hot'].set(str(data.get('back_hot', []))) self.core_vars['back_cold'].set(str(data.get('back_cold', [])))
最新发布
07-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值